webpack打包npm组件库,libraryTarget如何选择?
Author:zhoulujun Date:
官方文档如下:https://webpack.docschina.org/configuration/output/#outputlibrarytarget
webpack 官方文档将 libraryTarget 主要分为三类
Expose a Variable: 暴露为一个变量
Expose Via Object Assignment: 通过对象属性暴露
Module Definition Systems: 模块定义系统
libraryTarget属性值解析
暴露为一个变量
libraryTarget: “var”
对象定义:
libraryTarget:“window”,在window对象上定一个library设置的变量。在node环境下不支持。
libraryTarget:“global”,在global对象上定义一个library设置的变量。受target属性影响,当target为默认值web时,会在window上注册,如果你想在global上注册,必须修改target为node。
libraryTarget:“this”,在当前this对象上定义一个library设置的变量,如果this是window,就在window。在node的环境中,如果没指定require赋值的变量,并不会在指向global。
模块定义
libraryTarget:“commonjs”,在export对象上定义library设置的变量。在node中支持,浏览器中不支持。
libraryTarget:“commonjs2”,直接用module.export导出export,会忽略library设置的变量。在node中支持,在浏览器中不支持。
libraryTarget:“amd”,在define方法上定义library设置的变量,不能用script直接引用,必须通过第三方模块RequireJS来时用
libraryTarget:“umd”,该方案支持commonjs、commonjs2、amd,可以在浏览器、node中通用。
但是如果你想做到这一点,必须要额外设置,umdNamedDefine: true,globalObject: ‘this’,umdNamedDefine为设置amd前置名称使用library设置的变量,globalObject为改变全局指向。这样就能保证你的库在node和浏览器中通用了。
libraryTarget属性值原理说明
当我们引入别人开发的类库时有几种方式?下面假设我们引入一个demo方法:
传统方式:script标签
<script src="demo.js">
</script><script>demo();</script>
AMD
define(['demo'], function(demo) { demo(); });
commonjs 方式
const demo = require('demo'); demo();
ES6 module
import demo from 'demo'; demo();
大家思考一下,为什么这个类库能支持不同方式的引入?如何实现的?这就是 webpack 配置output.library和output.libraryTarget提供的功能。
首先我们将 webpack 编译后的文件进行简化,去除模块系统相关的逻辑,只保留最终导出的结果和赋值语句,最后的代码如下:
//--- webpack编译后的文件:dist/index.js // 构建模块函数 function a() { ... return __webpack_require__(__webpack_require__.s = "./src/index.js"); } //导出 a() // libraryTarget不同,导出语句不同 //--- 其他模块 require('dist/index.js');
由于 libraryTarget 只是影响最后的赋值导出操作,所以我们这里假设构建后的模块固定,只通过对比最终的赋值操作,来描述 library 和 libraryTarget 的实际效果。现假设构建后的模块如下所示:
{ name: 'jack', age: 24 }
第一类:暴露为一个变量
libraryTarget: “var”
webpack 配置:
output: { ... libraryTarget: 'var', },
编译后的文件如下所示:
(() => { return { name: 'jack', age: 24, }; })();
特点:没有赋值操作,所以其他文件没法引用该对象
加了 library 之后的 webpack 配置:
output: { ... library: 'finalModule', libraryTarget: 'var', },
编译后的文件如下所示:
var finalModule = (() => { return { name: 'jack', age: 24, }; })();
特点:有赋值操作,赋值给 library 定义的变量。
libraryTarget: “assign”
该值需要绑定 library 属性
webpack 配置:
output: { ... library: 'finalModule', libraryTarget: 'assign', },
编译后的文件如下所示:
finalModule = (() => { return { name: 'jack', age: 24, }; })();
特点:赋值给全局变量, 可能会覆盖宿主环境下的同名属性值。
通过对象属性暴露
libraryTarget: “this”
如同上面var 字段一样,区别就是:
不加 library,则将所有属性 mixin 到 this 上
加 library,则将对象挂载到 this[library]字段上
libraryTarget: “window”||libraryTarget: “global”
逻辑同 this,只是将 this 替换成 global||window
由于 webpack 默认 target 为"web",所以默认即使设置 libraryTarget 为 global,最终的挂载对象仍然是 window。需要先将 target 改为"node",才能使得挂载对象为 globa
更符合模块定义系统
libraryTarget:“commonjs2”
符合 commonjs 规范
加了 library 没有效果
webpack 配置:
output: { ... libraryTarget: 'commonjs2', },
编译后的文件如下所示:
module.exports = (() => { return { name: 'jack', age: 24 }})());
libraryTarget:“amd”
符合 amd 规范
加了 library,会改为定义 library 模块
加了 library 之后的 webpack 配置:
output: { ... library: 'finalModule', libraryTarget: 'umd', },
编译后的文件如下所示:
define('finalModule', [], () => { return (() => { return { name: 'jack', age: 24, }; })();});
libraryTarget:“umd”
定义了兼容各种模块的执行函数
不加 library,则将所有属性 mixin 到导出模块上
加 library,则将对象挂载到导出模块的 library 字段上
加了 library 之后的 webpack 配置:
output: { ... library: 'finalModule', libraryTarget: 'umd', },
编译后的文件如下所示:
(function webpackUniversalModuleDefinition(root, factory) { if (typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if (typeof define === 'function' && define.amd) define([], factory); else if (typeof exports === 'object') exports['finalModule'] = factory(); else root['finalModule'] = factory(); })(window, () => { return (() => { return { name: 'jack', age: 24, }; })(); });
externals和libraryTarget的关系
libraryTarget配置如何暴露 library。如果不设置library,那这个library就不暴露。就相当于一个自执行函数
externals是决定的是以哪种模式去加载所引入的额外的包
libraryTarget决定了你的library运行在哪个环境,哪个环境也就决定了你哪种模式去加载所引入的额外的包。也就是说,externals应该和libraryTarget保持一致。library运行在浏览器中的,你设置externals的模式为commonjs,那代码肯定就运行不了了。
如果是应用程序开发,一般是运行在浏览器环境libraryTarget可以不设置,externals默认的模式是global,也就是以全局变量的模式加载所引入外部的库。
具体参看:
https://libraryTarget的几种选择我们来好好分析 https://zhuanlan.zhihu.com/p/108216236
WebpackLibrary和LibraryTarget详解 https://blog.csdn.net/wdz512/article/details/111032811
详解webpack的out.libraryTarget属性 https://www.xlaoyu.info/2018/01/05/webpack-output-librarytarget/
webpack externals 深入理解 https://segmentfault.com/a/1190000012113011
转载本站文章《webpack打包npm组件库,libraryTarget如何选择?》,
请注明出处:https://www.zhoulujun.cn/html/tools/Bundler/webpack/2022_0519_8820.html