• home > tools > Bundler > webpack >

    webpack打包npm组件库,libraryTarget如何选择?

    Author:zhoulujun Date:

    官方文档如下:https: webpack docschina org configuration output outputlibrarytarget webpack 官方文档将 libraryTarget 主要分

    官方文档如下:https://webpack.docschina.org/configuration/output/#outputlibrarytarget

    webpack 官方文档将 libraryTarget 主要分为三类

    1. Expose a Variable: 暴露为一个变量

    2. Expose Via Object Assignment: 通过对象属性暴露

    3. 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