externals
externals 表示外部扩展的意思,使用这个属性可以防止将指定的依赖打包到 bundle.js 中,而是在运行时(runtime)从用户环境(例如从 cdn 或 node_modules)获取这些扩展依赖。
使用这个的目的是减少我们的代码包体积,从而可以提高首屏渲染速度。
cnd使用介绍:
当我们使用一个cnd资源时,只需要使用 script 标签加载对应的cnd路径就可以。
这样,就会在全局下(window)挂载一个属性或方法,使用时就会从全局对象下查找。
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><!-- 使用cnd的方式加载lodash三方库 --><script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script></head><body><script>console.log(window);console.log(window._.isEmpty({}))</script></body></html>
可以看到,lodash 已经成为了 window 的一个属性,我们已经可以使用window._进行访问 lodash 的方法了。

externals的使用:
比如用户环境域名为 https://xxx.jituan.com,cdn 域名为 https://statics.jituan.com ,cdn 上放了一些公用的静态资源,如 loadsh,moment 之类的工具库。
那在项目中,可如下配置
index.html
<!-- 使用cdn资源 -->
<script src="https://statics.jituan.com/lodash/4.17.11/index.min.js,moment/2.22.2/locale/zh-cn.js,react/16.8.6/index.min.js,react-dom/16.8.6/index.min.js"></script>
const externals = {
// key: value, key 表示npm包名,value 表示挂载在全局下的属性或方法
lodash: '_',
moment: 'moment',
react: 'React',
'react-dom': 'ReactDOM',
}
module.exports = {
//...
externals,
};
我们已经知道了 cdn 的资源,可以通过window来访问。但现在都是使用模块化的开发,而不是传统开发。我们会使用 import 导入我们需要的资源
import moment from 'moment'
from 后面的值可以是路径,也可以是模块名。当它是一个模块名时,那就需要通过配置,告诉 JS 引擎模块的位置。
我们可以通过 babel-plugin-const-replace-import解决这个问题。
babelOptions
借助 babel-plugin-const-replace-import,可以在编译时,将通过 import 的方式引入的指定类库转成 const 的方式。
webpack.config.js
const externals = {
lodash: '_',
moment: 'moment',
react: 'React',
'react-dom': 'ReactDOM',
}
module.exports = {
//...
externals,
babelOptions: {
plugins: [
[
require.resolve("babel-plugin-const-replace-import"),
{ libraries: externals }
]
]
}
};
源码:
import React from 'react'
import { isEmpty } from 'lodash'
转换后:
const React = window.React
const { isEmpty } = window._
react、lodash的值将会检索一个全局的 React、_变量,import 的声明方式转换为 const 的声明方式。
转换的目的是为了防止从本地node_modules中去查找依赖。import 的声明,会从node_modules 查找依赖,配置babel-plugin-const-replace-import后,就变成了 const 的方式,也就是说不会去node_mudules 中查找。
未配置babel-plugin-const-replace-import 导致的bug
以下是我们公司的子项目配置
index.html
<script src="//statics.jituancaiyun.com/cdn/js/??react/16.6.3/index.js,prop-types/15.6.2/index.js,react-dom/16.6.3/index.js,redux/4.0.1/index.js,react-redux/5.1.1/index.js,react-router-dom/4.3.1/index.js,redux-actions/2.6.4/index.min.js,moment/2.22.2/index.min.js,moment/2.22.2/locale/zh-cn.js,antd/3.16.2/index.min.js,axios/0.18.0/index.min.js"></script>
package.json
"scripts": {
"local": "cross-env NODE_ENV=development abc dev",
"dev": "node ./script/update && cross-env RUN_ENV=net cross-env NODE_ENV=development abc dev",
"build": "node ./script/update && cross-env RUN_ENV=net cross-env NODE_ENV=production abc build",
"releaseIndex": "cross-env NODE_ENV=production abc build",
"releaseApp": "cross-env NODE_ENV=production cross-env RUN_ENV=com abc build --config=abc.library.js",
"release": "npm run releaseIndex && npm run releaseApp",
"eslint-fix": "node_modules/.bin/eslint --fix src/"
},
webpack.config.js
const isDev = process.env.NODE_ENV === 'development'
const externals = {
lodash: '_',
moment: 'moment',
react: 'React',
'react-dom': 'ReactDOM',
}
const babelOptions = {}
if (!isDev) {
babelOptions.plugins = [
[
require.resolve('babel-plugin-const-replace-import'),
{ libraries: externals },
],
]
}
module.exports = {
// ...,
externals,
babelOptions,
}
cdn 上的 react 版本为 16.6.3,本地安装的版本为16.8,有一个同学开发时使用了hooks(16.8及以上的版本才支持)进行开发,本地开发是没有问题的,线上环境却出了问题,就是因为没有配置babel-plugin-const-replace-import 导致的。如果12行去掉,在本地开发时就会及时发现这个问题。
