使用场景

使用vue-cli创建项目,使用webpack打包。其中,有一个webpack优化webpack.optimize.CommonsChunkPlugin,它会将node_modules中的必需模块提取到vendor文件中,项目开发中,增加第三方模块,比如element-uivue-echarts等,vendor的包都会增大。这个时候,就需要考虑减轻vendor包的大小,增加构建速度。我们可以使用webpack的外部扩展(externals)功能。

externals配置选项,可以防止将import的包打包到bundle中,并在运行时再去从外部获取这些扩展依赖。

大白话:请不要将这个模块注入编译后的JS文件里,对于我源代码里出现的任何import/require这个模块的语句,请将它保留

使用方式

jquery为例,我们将从CDN上引入它,而不是将它下载到本地:

  1. <body>
  2. <div id="app"></div>
  3. <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
  4. <!-- built files will be auto injected -->
  5. </body>

vue.config.js文件里面加上:

  1. module.exports = {
  2. ···
  3. configureWebpack: config => {
  4. config.externals = {
  5. // key 值 jquery 是要引入的别名
  6. // value 值 jQuery 是 jquery 库对外暴露的全局变量
  7. 'jquery': 'jQuery',
  8. }
  9. },
  10. ...

可以在main.js中使用:

import $ from 'jquery'

原理分析

我们新建一个index.js文件:

import $ from 'jquery'

$("#content").html("<h1>hello world</h1>");

新建webpack配置文件webpack.config.js

const path = require('path');

module.exports = {
  mode: 'development',
  devtool: 'source-map',
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

jquery是通过npm i安装的,使用webpack打包后,查看webpackBootstrap参数部分的代码:

({
  "./node_modules/jquery/dist/jquery.js": (function(module, exports, __webpack_require__) {
    var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*!
    * jQuery JavaScript Library v3.4.1
    * https://jquery.com/
    *
    * Includes Sizzle.js
    * https://sizzlejs.com/
    *
    * Copyright JS Foundation and other contributors
    * Released under the MIT license
    * https://jquery.org/license
    *
    * Date: 2019-05-01T21:04Z
    */
    ...
  }),

  "./src/index.js": (function(module, __webpack_exports__, __webpack_require__) {

    "use strict";
    __webpack_require__.r(__webpack_exports__);
    /* harmony import */ 
    var jquery__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! jquery */ "./node_modules/jquery/dist/jquery.js");
    /* harmony import */
    var jquery__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(jquery__WEBPACK_IMPORTED_MODULE_0__);

    jquery__WEBPACK_IMPORTED_MODULE_0___default()("#content").html("<h1>hello world</h1>");

  }),

})

可以看到jquery源码都打包进来了,我们再配置externals

externals: {
  jquery: "jQuery",
}

再查看webpack打包后的webpackBootstrap参数部分的代码:

({

  "./src/index.js": (function(module, __webpack_exports__, __webpack_require__) {

    "use strict";
    __webpack_require__.r(__webpack_exports__);
    /* harmony import */ 
    var jquery__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! jquery */ "jquery");
    /* harmony import */
    var jquery__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(jquery__WEBPACK_IMPORTED_MODULE_0__);

    jquery__WEBPACK_IMPORTED_MODULE_0___default()("#content").html("<h1>hello world</h1>");

  }),

  "jquery": (function(module, exports) {

    module.exports = jQuery;

  })

})

可以看到jquery的源码替换成module.exports = jQuery了,这也是externals的作用。**jQuery****jquery**库暴露在全局的变量,**module.exports****webpack**自身维护的供外部使用的输出变量(不是**node**自带的),也就是说当**import xxx from jquery**的时候,实际上是从被暴露的**jQuery**全局变量引入,这就是核心

设置 externals 形式

  • global - 外部library能够作为全局变量使用。用户可以通过在script标签中引入来实现。这是externals的默认设置。
  • commonjs2 - 保留require()语句,适用于node环境。
  • AMD - 在define中定义依赖模块。
  • umd - 通用方式,包括commonjs2AMD和全局变量方式,

externals中配置:

externals: {
  jquery: 'commonjs2 jQuery'
}

// 打包后的形式
module.exports = require('jQuery')
externals: {
  jquery: 'jQuery'
}

// 打包后的形式
module.exports = jQuery

使用默认设置时,如果想让你的代码运行在浏览器中,你所引用的包,必须暴露出一个全局变量。如果没有,这种方式不适合在浏览器下使用。

libraryTarget / library / externals

  • libraryTarget:在output中配置,指定你的模块输出类型。
  • library:在output中配置,可以指定你的库的名称。
  • libraryTarget配置如何暴露library。如果不设置library,那这个library就不暴露,就相当于一个自执行函数。
  • externals决定的是以哪种模式去加载所引入的额外的包。
  • **libraryTarget**决定了你的**library**运行在哪个环境,哪个环境也就决定了你哪种模式去加载所引入的额外的包。也就是说,**externals**应该和**libraryTarget**保持一致。**library**运行在浏览器中的,你设置**externals**的模式为**commonjs**,那代码肯定就运行不了了

注意点

如果通过这种方式引入第三方包出现问题,可以通过以下几个方向进行查找:

  1. script的先后顺序;
  2. cdn的地址路径是否正确;
  3. 引入的变量名是否和externals属性中的value相对应,可以在console控制台输出看看。
  4. 确定externalslibraryTarget所支持的环境是否一致。

关于webpack externals更多详解可以参考这篇文章