本文承接上一篇 深入浅出webpack -- loader和plugin原理及区别
主要从下面几个点入手优化:
项目本身:
- 减少依赖嵌套深度
- 使用尽可能少的处理
webpack层面:
- Dll处理
- 通过include减少loader范围
- HappyPack
- Uglify优化
- 减少resolve,sourcemap,cache-loader用新版本的node和webpack
代码分割
一般vue打包后的文件中会有三个常见的js文件,app.js业务代码 vendor.js第三方代码,manifest.js,webpack的代码,拆分的规范一般是:
- 多页面: 主业务代码 + 公共依赖 + 第三方包 + webpack运行代码
- 单页面:主业务代码 + 异步模块 + 第三方包 + webpack运行代码
1 单页面
对于单页面,主要是拆分,减少体积,把需要异步加载的改成异步加载,主要就是做一个异步的拆分。
在webpack3中 需要用下面的插件来进行代码分割
new webpack.optimize.CommonsChunksPlugin({
name:'vendor',
minChunks:'infinity'
}),
new webpack.optimize.CommonsChunksPlugin({
name:'manifest',
minChunks:'infinity'
}),
new webpack.optimize.CommonsChunksPlugin({
name:'app.js',
minChunks:2
}),
webpack4 配置一个属性
optimization:{
minimize:true, // 压缩代码,减少体积
splitChunks:{
name:true,
chunks:"all", // 'initial' 只对入口文件进行公共模块分析, 'all' 对所有文件进行模块分析 'async'
minSize: 30000, // 默认30000, 大于30kb的文件进行提取
cacheGroups:{
mode1:{ // 自定义要提取的模块
test:/mode1/,
},
vendor: { // 提取node_modules中文件 都喜欢把第三方依赖都打包在一起
test: /([\\/]node_modules[\\/])/,
name: "vendor"
}
}
},
runtimeChunk:true // 把webpack运行代码的提取出来
},
运行webpack
打包后的文件也都被引入到了index.html中。
2 splitChunks基本配置
(1)chunks
:分割代码的模式
- async 异步代码分割模式:只分割异步引入的代码
异步代码指的是异步引入的代码模块单独打包成一个或多个文件,下面是异步引入的例子:
//异步加载模块
function getComponent () {
return import(/* webpackChunkName:"lodash" */ 'lodash').then(({ default: _ }) => {
var element = document.createElement('div')
element.innerHTML = _.join(['Dell', ' ', 'Lee', '-'])
return element
})
}
getComponent().then(el => {
document.body.appendChild(el)
})
- initial 同步代码分割模式:只分割同步引入的模块
同步代码引入方式:
//同步加载模块
import _ from 'lodash' //第三方库
import test from './test.js' //业务代码
import jquery from 'jquery' //第三方库
console.log(test.name)
var element = document.createElement('div')
element.innerHTML = _.join(['Dell', ' ', 'Lee', '-'])
document.body.appendChild(element)
console.log(jquery('div'))
- all 同步异步都分割模式:在所有配置条件都满足的情况下,无论如何引入模块都会进行分割
(2)minSize和maxSize(单位:字节)
minSize
指的是引入的模块的最小值 maxSize
指的是引入的模块的最大值,当引入的模块大小大于最大值时,weback会尝试将这个模块以最大值为准分割成多个模块,前提是这个模块可以分割,比如lodash的提交大于50KB,那么设置maxSize:5000时,依然打包出一个文件来,故此属性一般不用
(3)minChunks:模块至少使用次数
当值为2时,代表只引用了一次的模块不做分割打包处理
(4)maxAsyncRequests:同时加载的模块数量最大值
当需要分割的模块同步引入个数超出限时时,webpack只会分割限制值内的模块,其它的将不做处理
(5)maxInitialRequests:
首次加载引入模块可分割的最大值
(6)automaticNameDelimiter:
缓存组名称和生成文件名称之间的连接字符串
(7)name
设置为true时,缓存组里面的filename生效,覆盖默认命名方式
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
new CleanWebpackPlugin(),
清除之前的dist
webpack3的压缩
new webpack.optimize.UglifyJsPlugin(),
2 多页面应用
对于多页面,我们要做的就是提取公共依赖,把几个页面中都用到的依赖打包到一个文件中。
有时候我们并不希望业务代码中混入了第三方代码,或者webpack 的代码。就需要把这些不希望出现在业务中的代码拆分成单独的文件。
多个html文件,多个入口
var extractTextCss=require('extract-text-webpack-plugin');
var htmlWebpackPlugin=require('html-webpack-plugin');
module.exports= {
mode:'production',
entry:{
app:"./src/app.js",
app2: "./src/app2.js"
},
output:{
path:__dirname+"/dist",
filename:"./[name].bundle.js",
},
module:{
rules: [
{
test:/\.css$/,
use:extractTextCss.extract({
fallback:{
loader:'style-loader',
options:{
//insertInto:"#mydiv",
//transform:"./transform.js"
}
},
use:[
{
loader:'css-loader',
options:{
/*modules:{
localIdentName:'[path][name]_[local]_[hash:4]'
} */
}
},
]
})
},
{
test:/\.(png|jpg|jgeg|gif)$/,
use:[
{
loader:'url-loader',
options:{
//默认是[hash].[ext]
name:'[name].[hash:4].[ext]',
outputPath:"assets/img",
publicPath:"assets/img",
limit:5000
}
},
{
loader:'img-loader',
options:{
plugins:[
require('imagemin-pngquant')({
speed:2 //1-11
}),
require('imagemin-mozjpeg')({
quality:80 //1-100
}),
require('imagemin-gifsicle')({
optimizationLevel:1 //1,2,3
})
]
}
},
]
},
{
test:/\.html$/,
use:{
loader:'html-loader',
options:{
attrs:["img:data-src"]
}
}
}
]
},
plugins:[
new extractTextCss({
filename:'[name].min.css'
}),
new htmlWebpackPlugin({
filename:"index.html",
template:"./src/index.html",
chunks:['app']
}),
new htmlWebpackPlugin({
filename:"index1.html",
template:"./src/index1.html",
chunks:['app2']
}),
]
}
index.html 和 index1.html内容一样
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div id="mydiv" class='div1'></div>
<div class='div1 img1'></div>
<div class='div1 img2'></div>
<div class='div1 img3'></div>
<div class='div1 img4'></div>
</body>
项目目录结构
打包后
配置代码拆分:(下面代码中的Setting可以理解为一个存储当前常量,环境信息的对象)
// 代码拆分
optimization: {
minimize: Setting.NODE_ENV === 'production', // This is true by default in production mode.
runtimeChunk: {
name: 'manifest',
}, // 把webpack运行代码的提取出来
splitChunks: {
chunks: 'initial',
minChunks: 1,
minSize: 30000,
maxAsyncRequests: 5,
maxInitialRequests: 3,
name: false,
cacheGroups: {
commons: {
test: /common/, // 把公共代码提取出来
name: Setting.common,
},
vendor: { // 提取node_modules中文件 都喜欢把第三方依赖都打包在一起
test: /([\\/]node_modules[\\/])/,
name: 'vendor',
},
},
},
},
打包后:
看index.html知道 只引入了app2.js文件。
还需要进行下面的配置,才可以把所有文件都引入:
config.plugins.push(new Html({
inject: true,
title,
env: Setting.NODE_ENV,
template: 'public/index.html',
filename: `${k}.html`,
chunks: [k, Setting.common, 'vendor', 'manifest'],
minify: Setting.NODE_ENV === 'production' || Setting.NODE_ENV === 'analyze' ? {
removeComments: true,
} : false,
}));
两个html文件中都引入了webapck代码,和第三方依赖
分析代码
下面两种方式可以获得可视化的打包结果分析
1 官方版本
(1)Mac webpack --profile --json > stats.json
(2)Window: webpack --profile --json | Out-file 'stats.json' -Encoding OEM
然后将输出的json文件上传到如下网站进行分析
http://webpack.github.io/analyse/
选择stats.json文件可以看到详细的信息,生成了可视化的页面,然后打开 Modules页面,可以分析每一个文件的打包时间和大小
2 社区版本
npm i webpack-bundle-analyzer --save
webpack引入插件
const wba = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
new wba()
可以用这样的方式分析每个模块的大小
介绍一种提取css文件的方式, 比较支持webpack4
npm install mini-css-extract-plugin
const miniCssExtractPlugin = require('mini-css-extract-plugin');
new extractTextCss({ // 不支持hash命名
filename:'[name].min.css'
}),
new miniCssExtractPlugin({ // 支持hash
filename: '[name].[hash].css'
}),
style-loader换成 miniCssExtractPlugin.loader
可以把css文件也加上hash。
dll处理
- 将网页依赖的基础模块抽离出来,打包到一个个单独的动态链接库中。在一个动态链接库中可以包含多个模块。
- 当需要导入的模块存在于某个动态链接库中时,这个模块不能被 再次打包,而是去动态链接库中获取。
- 页面依赖的所有动态链接库都需要被加载。
为什么为We b项目构建接入动态链接库的思想后,会大大提升构建 速度呢?原因在于,包含大量复用模块的动态链接库只需被编译一次, 在之后的构建过程中被动态链接库包含的模块将不会重新编译,而是直 接使用动态链接库中的代码。由于动态链接库中大多数包含的是常用的 第三方模块,例如 react、react-dom,所以只要不升级这些模块的版 本,动态链接库就不用重新编译。
Webpack已经内置了对动态链接库的支持,需要通过以下两个内置的插件接入。
- DllPlugin插件:用于打包出一个个单独的动态链接库文件。
- DllReferencePlugin插件:用于在主要的配置文件中引入DllPlugin 插件打包好的动态链接库文件。
webpack.dll.js
const webpack=require('webpack');
module.exports={
entry:{
jquery:["jquery"],
loadsh:["loadsh"]
},
output:{
path:__dirname+"/src/dll",
filename:"./[name].js",
//引用名
library:'[name]'
},
plugins:[
new webpack.DllPlugin({
path:__dirname+"/src/dll/[name].json",
name:"[name]"
})
]
}
注意这里的output.library属性要与DllPlugin中的name属性保持一致。dll打包后的文件需要单独引入html,因为不会再被打包进主文件内,也相当于做了代码拆分。
webpack --config webpack.dll.js现把第三方依赖打包好,src下面多了一个dll文件夹,这就是构建出的动态链接库文件。
如果需要压缩 可以增加配置,例子:
const webpack = require('webpack');
const TerserPlugin = require('terser-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: "development",
entry:{
vue: ["vue"],
},
output: {
path: __dirname + "/src/dll/",
filename: '[name].dll.js',
library: 'dll_[name]'
},
optimization: {
minimize: true,
// 压缩js
minimizer: [
new TerserPlugin({
parallel: true, // 启动多进程压缩 官方建议
})
]
},
plugins:[
new CleanWebpackPlugin(), // 清空得是output中得path
new webpack.DllPlugin({
path: __dirname+'/src/dll/[name].dll.json',
name: 'dll_[name]',
})
]
}
entry: ['vue-router', 'vue/dist/vue.esm.js'] vue项目中处理
json是给到webpack的
webpack.config.js 增加两个插件,这样每次打包的时候jquery和loadsh就不用再打包了,直接用打包好的。大大提升了打包速度。
new webpack.DllReferencePlugin({
manifest:require('./src/dll/jquery.json')
}),
new webpack.DllReferencePlugin({
manifest:require('./src/dll/loadsh.json')
})
并且用下面的插件,把dll/*.js文件插入html。
// 将某个文件打包输出去,并在html中自动引入该资源
new AddAssetHtmlPlugin([{
outputPath: "js/",
filepath: path.resolve(__dirname,'src/dll/vue.dll.js') // 文件路径
}])
happypack
由于有大量文件需要解析和处理,所以构建是文件读写和计算密集 型的操作,特别是当文件数量变多后,Webpack构建慢的问题会显得更 为严重。运行在Node.js之上的Webpack是单线程模型的,也就是说 Webpack需要一个一个地处理任务,不能同时处理多个任务。
文件读写和计算操作是无法避免的,那能不能让 Webpack 在同一 时刻处理多个任务,发挥多核CPU电脑的功能,以提升构建速度呢?
HappyPack就能让Webpack 做到这一点,它将任务分解给多个子进程去并发执行,子进程处理完后 再将结果发送给主进程。
由于 JavaScript 是单线程模型,所以要想发挥多核 CPU 的功能,就 只能通过多进程实现,而无法通过多线程实现。
//建议当文件较多的时候再使用这个,如果只有一两个反而会拖慢。
const HappyPack=require('happypack');
const os=require('os'); // 拿到操作系统
const happyThreadPool=HappyPack.ThreadPool({size:os.cpus().length}) // 新建进程池
{
test: /\.js$/,
loader: 'happypack/loader?id=happyBabel', // 对js的处理使用happypack
include: [resolve('src')]
},
new HappyPack({
id:'happyBabel',
loaders:[
{
loader:'babel-loader?cacheDirectory=true' // 代替babel-loader
}
],
threadPool:happyThreadPool,
verbose:true
}),
长缓存
主要原理是充分利用浏览器的缓存机制,提高首页渲染速度。
- 把hash改为chunkhash
- NamedChunksPlugin和NameModulesPlugin
- mini-css-extract-plugin
使用hash
output:{
path:__dirname+"/dist",
filename:"./[name].[hash].js",
},
每一个文件的hash都是一样的。
改变一下app.js文件,把ma和mb文件换一个循序
import lod from "loadsh";
//import './css/test1.css';
import mb from "./moduleb.js";
import ma from "./modulea.js";
a(988989893232);
/*require.ensure(["./moduleb"],function(){
var ma=require('./modulea.js');
})*/
console.log(22);
webpack 打包
只改变了一个文件,导致多个文件的hash都发生了改变。
把hash换成chunkhash
output:{
path:__dirname+"/dist",
filename:"./[name].[chunkhash].js",
},
webapck 打包发现每一个文件的hash 都不一样。
import lod from "loadsh";
//import './css/test1.css';
import ma from "./modulea.js";
import mb from "./moduleb.js";
a(988989893232);
/*require.ensure(["./moduleb"],function(){
var ma=require('./modulea.js');
})*/
console.log(22);
再把循序换一下,webpack打包,发现还是所有文件的hash都改变了
增加两个插件
new webpack.NamedChunksPlugin(),
new webpack.NamedModulesPlugin(),
打包发现 chunks不一样了
再改变一下app.js模块引入循序,打包发现只有改变的文件的hash改变了,其它的还保持原来的hash,这样上线发布以后,那些没有改变的文件就可以继续使用浏览器的缓存。
缩小文件搜索范围
1 resolve.modules配置优化
resolve.modules的默认值是['node_modules'],含义是先去当前目录 的./node_modules 目录下去找我们想找的模块,如果没找到,就去上一 级目录../node_modules中找,再没有就去../../node_modules中找,以此类 推,这和Node.js的模块寻找机制很相似。
当安装的第三方模块都放在项目根目录的./node_modules 目录下 时,就没有必要按照默认的方式去一层层地寻找,可以指明存放第三方 模块的绝对路径,以减少寻找,配置如下:
resolve: {
...
modules: [path.resolve(__dirname, 'node_modules')],
},
2 resolve.alias的配置优化
在实战项目中经常会依赖一些庞大的第三方模块,以 React库为 例,安装到 node_modules目录下的React库的目录结构如下:
可以看到在发布出去的React库中包含两套代码。
- · 一套是采用 CommonJS 规范的模块化代码,这些文件都放在 lib目录下,以package.json中指定的入口文件react.js为模块的入口。
- · 一套是将React的所有相关代码打包好的完整代码放到一个单独的 文件中,这些代码没有采用模块化,可以直接执行。其中dist/react.js 用 于开发环境,里面包含检查和警告的代码。dist/react.min.js用于线上环 境,被最小化了。
在默认情况下,Webpack会从入口文件./node_modules/react/react.js 开始递归解析和处理依赖的几十个文件,这会是一个很耗时的操作。通 过配置resolve.alias,可以让Webpack在处理React库时,直接使用单独、 完整的react.min.js文件,从而跳过耗时的递归解析操作。
resolve: {
extensions: ['.js', '.jsx'],
alias: {
'@': path.resolve(__dirname, 'src'),
react: path.resolve(__dirname, './node_modules/react/dist/react.min.js'),
},
modules: [path.resolve(__dirname, 'node_modules')],
},
此处具体项目还需要看具体路径,引入的需要是react生产环境的压缩代码。
除了 React库,大多数库被发布到 Npm仓库中时都会包含打包好的 完整文件,对于这些库,也可以对它们配置alias。
这里注意,要这样引入:
react: isDev ? path.resolve(__dirname, './node_modules/react/cjs/react.development.js') : path.resolve(__dirname, './node_modules/react/cjs/react.production.min.js'),
开发环境引入开发环境的react,不然页面会不显示。
alias也可以解决重复打包的问题,比如vue.esm.js被打包了两次:
一次:node_modules/vue/dist/vue.esm.js
还有一次:node_modules/am4-pub-js-common-component/vue/dist/vue.esm.js
配置resolve.alias:
alias: {
'vue': path.resolve(process.cwd(), 'node_modules', 'vue'),
},
之后再打包,可以看到只剩下一个vue了:
但是,对某些库使用本优化方法后,会影响到后面要讲的使用 Tree-Sharking 去除无效代码的优化,因为打包好的完整文件中有部分代 码在我们的项目中可能永远用不上。一般对整体性比较强的库采用本方 法优化,因为完整文件中的代码是一个整体,每一行都是不可或缺的。 但是对于一些工具类的库如 lodash,我们的项目中可能只用到 了其中几个工具函数,就不能使用本方法去优化了,因为这会导致在我们的输出代码中包含很多永远不会被执行的代码。
Tree-Sharking是一种按需进入的技术,只会引入我们用到的函数。
3 resolve.extensions的配置优化
resolve: {
extensions: ['.js', '.json'],
}
当遇到 require('./data')这样的导入语句时,Webpack 会先去寻找./data.js文件,如果该文件不存在,就去寻找./data.json文 件,如果还是找不到就报错。
如果这个列表越长,或者正确的后缀越往后,就会造成尝试的次数 越多,所以resolve.extensions 的配置也会影响到构建的性能。在配置 resolve.extensions时需要遵守以下几点,以做到尽可能地优化构建性 能。
- · 后缀尝试列表要尽可能小,不要将项目中不可能存在的情况写到 后缀尝试列表中。
- · 频率出现最高的文件后缀要优先放在最前面,以做到尽快退出寻 找过程。
- · 在源码中写导入语句时,要尽可能带上后缀,从而可以避免寻找 过程。例如在确定的情况下将require('./data')写成 require('./data.json')。
4 module.noParse的配置优化
module.noParse配置项可以让Webpack忽略对部 分没采用模块化的文件的递归解析处理,这样做的好处是能提高构建性 能。原因是一些库如 jQuery、ChartJS庞大又没有采用模块化标准,让 Webpack解析这些文件既耗时又没有意义。
在前面讲解优化 resolve.alias 配置时讲到,单独、完整的 react.min.js 文件没有采用模块化,让我们通过配置module.noParse忽略 对react.min.js文件的递归解析处理,相关的Webpack配置如下:
注意,被忽略掉的文件里不应该包含import、require、define等模块 化语句,不然会导致在构建出的代码中包含无法在浏览器环境下执行的 模块化语句。
以上就是所有和缩小文件搜索范围相关的构建性能优化方面的内容 了,在根据自己项目的需要按照以上方法改造后,构建速度一定会有所 提升。
module: {
noParse: [/react\.min\.js$/],
...
}
开发实践:
moment.js
一般情况下:
只有main和jsnext:main,而./moment.js是编译好的,./dist/moment.js是module模式,webpack不识别jsnext:main配置,不会去加载模块化的moment.js,所以没有必要再对moemnt.js进行解析,
- main : 定义了 npm 包的入口文件,browser 环境和 node 环境均可使用, 其中 .js 文件是使用 commonJS 规范的语法(require('xxx')),
- jsnext:main: 是用 ESM 规范的语法(import 'xxx')
- module: .mjs 是用 ESM 规范的语法(import 'xxx')
"main": "lib/index.js", // main
"module": "lib/index.mjs", // module
module: {
noParse: function(content) {
return /moment/i.test(content)
}
...
}
多进程压缩代码
当Webpack有多个JavaScript文件需要输出和 压缩时,原本会使用UglifyJS去一个一个压缩再输出,但是 ParallelUglifyPlugin会开启多个子进程,将对多个文件的压缩工作分配 给多个子进程去完成,每个子进程其实还是通过 UglifyJS 去压缩代码, 但是变成了并行执行。所以ParallelUglifyPlugin能更快地完成对多个文 件的压缩工作。
cnpm i webpack-parallel-uglify-plugin --D
plugins: [
...
new ParallelUglifyJsPlugin({
cacheDir: '.cache/', // 开启缓存
uglifyJS: {
output: {
// 最紧凑的输出
beautify: false,
// 删除注释
comments: false,
},
warnings: false,
compress: {
// 在uglifyJS时删除么有用到的代码时不输出警告
// 删除console
drop_console: true,
// 内嵌已经定义但是只用到一次的变量
collapse_vars: true,
// 提取出出现多次但是没有定义成变量去引用的静态值
reduce_vars: true,
},
},
}),
]
cnpm run build 会发现打包速度快了很多
优化前
优化后
在通过new ParallelUglifyPlugin()实例化时,支持以下参数。
- · test:使用正则去匹配哪些文件需要被 ParallelUglifyPlugin 压缩, 默认为/.js$/,也就是默认压缩所有的.js文件。
- · include:使用正则去命中需要被ParallelUglifyPlugin压缩的文件, 默认为[]。
- · exclude:使用正则去命中不需要被ParallelUglifyPlugin压缩的文 件,默认为 []。
- · cacheDir:缓存压缩后的结果,下次遇到一样的输入时直接从缓存 中获取压缩后的结果并返回。cacheDir用于配置缓存存放的目录路径。 默认不会缓存,若想开启缓存,则请设置一个目录路径。
- · workerCount:开启几个子进程去并发执行压缩。默认为当前运行 的计算机的CPU核数减1。
- · sourceMap:是否输出Source Map,这会导致压缩过程变慢。
- · uglifyJS:用于压缩 ES5 代码时的配置,为 Object 类型,被原封不动地传递给UglifyJS作为参数。
- · uglifyES:用于压缩 ES6 代码时的配置,为 Object 类型,被原封不动地传递给UglifyES作为参数。
其中的test、include、exclude与配置Loader时的思想和用法一样。
UglifyES(https://github.com/mishoo/UglifyJS2/tree/harmony)是 UglifyJS的变种,专门用于压缩ES6代码,它们都出自同一个项目,并 且不能同时使用。
UglifyES一般用于为比较新的 JavaScript运行环境压缩代码,例如用 于 ReactNative 的代码运行在兼容性较好的 JavaScriptCore 引擎中,为了 得到更好的性能和尺寸,可采用UglifyES压缩。
ParallelUglifyPlugin同时内置了UglifyJS和UglifyES,也就是说 ParallelUglifyPlugin支持并行压缩ES6代码。
安装成功后重新执行构建,会发现速度变快了许多。如果设置cacheDir 开启缓存,则在之后的构建中速度会更快。
使用自动刷新
文件监听是在发现源码文件发生变化时,自动重新构建出新的输出 文件。
Webpack官方提供了两大模块,一个是核心的 webpack(https://www.npmjs.com/package/webpack),webpack-dev-server。而文件监听功能是Webpack提供的。
External
Externals用来告诉在Webpack要构建的代码中使用了哪些不用被打 包的模块,也就是说这些模板是外部环境提供的,Webpack在打包时可 以忽略它们。
假设:我们开发了一个自己的库,里面引用了lodash这个包,经过webpack打包的时候,发现如果把这个lodash包打入进去,打包文件就会非常大。那么我们就可以externals的方式引入。也就是说,自己的库本身不打包这个lodash,需要用户环境提供。
import _ from 'lodash';
配置externals
externals: {
"lodash": {
commonjs: "lodash",//如果我们的库运行在Node.js环境中,import _ from 'lodash'等价于const _ = require('lodash')
commonjs2: "lodash",//同上
amd: "lodash",//如果我们的库使用require.js等加载,等价于 define(["lodash"], factory);
root: "_"//如果我们的库在浏览器中使用,需要提供一个全局的变量‘_’,等价于 var _ = (window._) or (_);
}
}
有些JavaScript运行环境可能内置了一些全局变量或者模块,例如在 我们的HTML HEAD标签里通过以下代码引入jQuery:
<script src="path/jQuery.js"></script>
这时,全局变量jQuery就会被注入网页的JavaScript运行环境里。
如果想在使用模块化的源代码里导入和使用jQuery,则可能需要这 样:
import $ from 'jQuery'
构建后我们会发现输出的Chunk里包含的jQuery库的内容,这导致 jQuery库出现了两次,浪费加载流量,最好是Chunk里不会包含jQuery 库的内容。
Externals配置项就是用于解决这个问题的。
通过externals可以告诉Webpack在JavaScript运行环境中已经内置了 哪些全局变量,不用将这些全局变量打包到代码中而是直接使用它们。 要解决以上问题,可以这样配置externals:
module.export = {
externals: {
// 把导入语句里的 jquery 替换成运行环境里的全局变量 jQuery
jquery: 'jQuery',
react: 'React'
}
}
不同环境设置externals方式
- 如果你的代码想运行在Node环境中,那么你需要在external中添加前缀commonjs2或者commonjs
externals:{
react:'commonjs2 react',
jquery:'commonjs2 jquery'
}
1.如果需要requirejs等符合AMD规范的环境中加载,那就要添加amd
externals:{
react:'amd React',
jquery:'amd jQuery'
}
2.如果要在浏览器中运行,那么不用添加什么前缀,默认设置就是global。
externals:{
react:'React',
jquery:'jQuery'
}
也可以这样
externals:["React","jQuery"]
这种方式配置下,就是配置你所引用你的库暴露出的全局变量。上面两种模式下或者说,如果你想运行代码在浏览器中,你所引用的包,必须暴露出一个全局变量。如果没有,这种方式不适合在浏览器下使用 。
externals
和libraryTarget
的关系
- libraryTarget配置如何暴露 library。如果不设置library,那这个library就不暴露。就相当于一个自执行函数
- externals是决定的是以哪种模式去加载所引入的额外的包
- libraryTarget决定了你的library运行在哪个环境,哪个环境也就决定了你哪种模式去加载所引入的额外的包。也就是说,externals应该和libraryTarget保持一致。library运行在浏览器中的,你设置externals的模式为commonjs,那代码肯定就运行不了了。
- 如果是应用程序开发,一般是运行在浏览器环境libraryTarget可以不设置,externals默认的模式是global,也就是以全局变量的模式加载所引入外部的库。
把模块中异步加载的组件都打包到一起
了解更多,移步webpack专题分类专栏:https://blog.csdn.net/qq_41831345/category_9640180.html