阅读 1106

读懂webpack4,看这一篇就够了

概述,在webpack里面有四个概念: 入口、出口、插件和loader,进行模块化打包,支持CMD、ADM、commonJS、import等模块化操作。

安装说明:在webpack安装的时候,需要先安装一个全局的webpack,然后在需要的文件夹中安装局部的依赖,webpack webpack-cil脚手架,在webpack3的版本中,它们是集成在一起。本次笔记是针对于:webpack4.x以上的版本

npm install webpack -g 	//先安装全局的webpack,安装完成后,才可以实验webpack命令,直接打包
npm install webpack webpack-cli  -D  // 在需要的目录安装webpack

webpack  // 在cmd或者vscode中运行这个,就可以打包了,其它的配置,且看后面
复制代码

安装完成后,需要在项目根目录新建一个webpack-config.js的配置文件;没有新建这个文件,也可以打包,因为webpack有默认配置。

如果没有安装全局webpack的话,就无法直接使用webpack直接打包,当然,也可以单独是配置脚本,例:

scripts: {
    dev: webpack --config webpack-config.js
}
复制代码

在4.0以后的版本里面,新增了一个多入口打包命令:webpack app.js app1.ja -o bulid.js

在webpack中有生产模式(production)和开发模式(development),默认为开发设置,用,mode参数去设置模式,例:

module.exports = {
  mode: 'production' // 模式选择,生产和开发两种
};
复制代码

特别说明:需要指定mode属性,不然控制台会报出一个警告信息

入口(entry)

入口(entry point),表示 webpack 应该使用哪个模块;进入入口以后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。

入口可以是单入口和多入口(使用一个数组或者对象表示),在实际操作中,为了便于优化以及首屏加载问题,会更倾向于第三种方式,既对象的形式。

但是使用对象写法(entry代码分离),会有一些小问题;假设entry引入了2个js文件,分别是a.jsb.js,但是这个两个文件都引入了同一个依赖,打包以后,就会造成代码重复,非常不利于优化,所以需要使用SplitChunksPlugin来进行防止重复或者是动态倒入的方法。点击查看这种方式的坑点:代码分离

module.exports = {
  entry: './app.js' // 写法一
  entry: ['./app.js, main.js'] //写法二
  entry: {
    app: './app.js',
    main: './main.js'
} //写法三
};
复制代码

出口(output)

如果使用(output)属性表示,这个属性是用来告诉webpack输出它所创建的内容,默认的目录是dist文件夹,在项目的根目录中,也可以指定一个目录导出。

const path = require('path');
module.exports = {
  entry: './path/to/my/entry/file.js', // 入口
  output: { // 出口
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js' // 单文件合并的写法
    filename: '[name].js', // 多入口多文件
    chunkFilename: [name][contenthash].js, // 防止缓存,只有在更新以后才会生成随机码
    publicPath: 'CDN地址' // 设置CDN服务器地址, 理论上是可以直接打包到CDN服务器上,前提是有权限
  }
};
复制代码

入口文件有配置的,就走filename这个属性,如果是其它文件导入的,就走下面这个,既chunkFilename,在使用的时候,目的就是解决代码更新,而浏览器读取缓存获取不到更新。

output中,filename属性是用来告诉webpack打包后的文件名称;而path是告诉webpack需要创建的文件夹的位置信息,这个要调用node里面一个模块,既path模块,__dirname是node里面的一个关键字,表示路径,绝对路径

提示:在webpack里面,多个入口如果要合并文件,只能使用数组的方式; 如果需要打包多文件的时,入口需要使用对象,出口使用一个动态的写法,如上代码"多入口文件", [name].js,如果入口是一个对象,出口一定是一个动态写法,否则就会导致打包不正常。

loader

loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。

loader的执行顺序,是从下到上,从右到左的执行顺序,所以在设置loader的时候,顺序不能放错,否则,可能会导致打包异常。

个人理解: loader就相当于是一个翻译器,将我们写的代码,进行翻译并处理。使用方法,如下:babel为例

注意:在lodaer里面,配置正则(test)的时候,一定不能加引号,否则会提示lodaer错误。

babel

介绍:babe就是将ES6转成ES5的一个编译器,需要安装3个依赖;虽然Babel可以将ES6的语法转成ES5的语法,但是如果是ES6+里面的内置API就无法去转换了,因此安装一个垫片@babel/polyfill,它主要是为了兼容低版本的浏览器,

babel-loader @babel/core  // 主要是语法转换
@babel/preset-env  // 这个用来指定ES的版本
@babel/polyfill // 全局垫片,用于转换内置API支持ES5的语法
/*   局部垫片需要安装的依赖项   */
@babel/plugin-transform-runtime
@babel/runtime
复制代码

webpack配置方式

module: {
  rules: [
    {
      test: /\.m?js$/, // 匹配文件的后缀名
      exclude: /(node_modules|bower_components)/, // 需要剔除的目录
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env'], // 垫片使用
          plugins: ['@babel/plugin-proposal-object-rest-spread'] //额外的
        }
      }
    }
  ]
}
复制代码

关于属性use的操作,如果配置项目不是很多的时候,可以不用use这个属性,它们两个是等价的,直接写,例:

rules: [{
    test: /\.m?js$/, // 匹配文件的后缀名
    loader: 'babel-loader', // 加载的loader
    exclude: /(node_modules|bower_components)/, // 需要剔除的目录
    options: { 	//配置项
   		presets: ['@babel/preset-env'], // 默认写法,不使用配置的写法
        presets: [["@babel/preset-env", { // 第二种写法,配置一个babel/polyfill垫片
        useBuiltIns: "usage", // 按需加载 entry | usage
        targets: {
          chrome: "68"  // 浏览器目标,也可以跟随一个浏览器的市场份额
        }
      }]
      ]
    }
}]
复制代码

关于@babel/polyfill的使用:先安装,需要在被打包的文件中引入 @babel/polyfill这个模块,默认是会打包所有的API内置模块,因此需要一个按需加载,写法:useBuildIns: 'usage'

默认会报出一个警告,解决方案:如果配置了usage的话,就不需要在被打包的文件种引入@babel/polyfill,如果需要引入的话:则需要用entry替换(局部方式,按照文件需求)

关于局部垫片的使用说明:如果是写的一个插件,就不能使用全局垫片,使用局部垫片需要有两个依赖@babel/plugin-transform-runtime@babel/runtime

.babelrc配置方法

在项目根目录需要新建一个单文件,文件名:.babelrc,格式是一个标准的JSON,所以需要使用标准的JSON格式,下面已局部垫片配置为例:

{
	"presets":["@babel/preset-env"],
	"plugins": [["@babel/plugin-transform-runtime", { 
         "absoluteRuntime": false,
         "corejs": 2, // corjs需要改成2,不改的话会造成打包异常,官方默认是false,可选数字和布尔
         "helpers": true,
         "regenerator": true,
         "useESModules": false
      }]
  ]
}
/*  ================  正常模式下 使用文件名的方式 配置 ========================*/
{
      "presets": [["@babel/preset-env", {
          "useBuiltIns": "usage",
          "targets": {
          "chrome": "68"  // 浏览器目标,也可以跟随一个浏览器的市场份额,也可以是:last 100 versions
        }
      }]
      ]
}
复制代码

关于异常的处理:corejs官方的默认值是false,但是这样会有异常,结果就是不会转换;因此在corejs的属性后面要跟上一个数字2,但是这样,还是不行,因此需要下载一个@babel/runtime-corejs2依赖项,用来改变之前的@babel/runtime

注意:在设置presets属性的时候,如果需要配置,第二个对象是放在嵌套数组里面,如:[['xxx',{} ]],并且,不可以在在webpack里面设置了以后,再去单独的.babelrc里面去设置,根据试验证明:会报错。

插件(plugins)

想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建它。

这是官方的说法,我自己的理解就是,给webpack扩展一个功能,这个功能极其强大,可以用来处理各种各样的任务,插件的使用方法如下:

const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装的模块
const webpack = require('webpack'); // 用于访问内置插件
const path = require('path') //这是一个文件路径的内置模块,在使用resolve属性时,就必须要有这个

const config = { // 多文件配置的写法
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
        template: './src/index.html'
    })
  ]
};
module.exports = config 
复制代码

HtmlWebpackPlugin的使用

HtmlWebpackPlugin插件,该插件将为你生成一个 HTML5 文件, 其中包括使用 script 标签的 body 中的所有 webpack 包。 使用的时候,需要安装这个插件html-webpack-plugin,基本具体配置如下:

var HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入插件
plugins: [
    new HtmlWebpackPlugin({ // 可以配置更多的值,比如title
        title: 'webpack', // 生成HTML标题
        templat: './index.html' // 配置的模板文件
        hash: true, // 防止缓存,给生成的JS文件添加一个hash值,包含css
        cache: true // 仅在内容被更改时才更新文件
        inject: true, // JS注入的位置,true,默认底部 
        chunks:['xxx', 'entry入口文件名'], // 多入口文件,会根据配置生成JS文件,默认全部引用 
        minify: {
        	removeAttributeQuotes: true, // 是否移除属性的引号 默认false
    }
    })
]
复制代码

参考资料: npm原文链接webpack4 之html-webpack-plugin。更多配置查看前面的NPM原文链接

clean-webpack-plugin的使用

clean-webpack-plugin插件能对配置的输出文件进行清除,在build命令开始执行的时候,会自动的去检查,如有有就会去先清除掉,这样就永远地保证了这个目录里面的文件都是最新的。但是也可以自定义删除某个目录,文档请戳这里

const { cleanWebpackPlugin } = require('clean-webpack-plugin') // 先引入这个包,需要使用这种方式
plugins: [
	new cleanWebpackPlugin()  // 使用,默认不需要进行传参, 根据实测,在3.X的版本,可以直接引入
]
复制代码

多个loader或多插件配置

如果要使用多个lodaer,只需要将规则rules属性下面,放一个数组,里面可以有多个对象

module: {
rules: [{  // 关于多个loader配置如下,每个loader都是一个对象
		test: /\.tsx?$/,
		use: 'ts-loader',
		exclude: /node_modules/ 
	},{
      test: /\.m?js$/, // 匹配文件的后缀名
      exclude: /(node_modules|bower_components)/, // 需要剔除的目录
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env'],
          plugins: ['@babel/plugin-proposal-object-rest-spread']
        }
      }
    }]
}
复制代码

提示:其实,每一个loader或者插件都可以使用单文件的方式,这样可以让webpack文件变得不是那么的庞大,但是会造成项目根目录文件的变得很多,具体根据自己得项目来进行配置。

关于TS文件的打包

在转换TS文件的时候,需要下载typescript ts-loader的依赖,安装好以后,需要在项目根目录新建一个为tsconfig.json的JSON文件,配置如下:

{
  "compilerOptions": {
    "outDir": "./dist/",  // 输出的文件,如果webpack已经配置了,就不需要了
    "noImplicitAny": true, 
    "module": "es6",	// 已什么模块的方式引入文件,主要有ES6和commonJS
    "target": "es5",	// 需要编译成什么类型的语法,ES5
    "jsx": "react",	  // 使用jsx语法,
    "allowJs": true   // 在TS文件种是否存在ES的语法,默认true
  },
  "include": ['./src/', './'], // 引入的文件目录,需要编译的文件
  "exclude": /node_modules/  // 剔除不需要编译的目录,多个使用(xx|xx)这种方式
}
复制代码

编译typescriptwebpack的配置如下:

module: {
	rules: [{
		test: /\.tsx?$/,
		use: 'ts-loader',
		exclude: /node_modules/ 
	}]
},
复制代码

lodash是一个一致性、模块化、高性能的 JavaScript 实用工具库,主要用于代码类型约束。在TS里面使用types/lodash可以对TS进行类型约束。**提示:**lodash具有针对性,如果需要对jquery进行类型约束,则需要下载对应的lodash依赖包。

图片打包

在图片打包以前,需要下载一个loader,使用file-loader可以对图片进行打包,webpack常用配置如下:

module: {
	rules: [{
		test: /\.(png|jpg|gif)$/,
        use: [{
            loader: 'file-loader',
            options: {
            	name: '[path][name].[ext]' // 自定义名字,[name]名字[ext]扩展名,还可以加混淆比如哈希值
                outputPath: 'images/'  //将打包的文件,生成打包相对文件(dist)的images/的目录下
            }
		}]
	}]
}
复制代码

说明:图片打包,默认文件名使用哈希进行混淆,也可以自定义配置。具体查看webpack官方的url-loader说明,示例上仅仅只是显示了一部分。

如果图片需要进行BASE64的打包,需要下载url-loader的一个loader,但是需要注意的是,file-loadeurl-loader的处理是一样的,唯一不一样的就是url-loader要比file-loade的功能更加强大,使用的方法和file-loade的方法一模一样,并没有什么区别,但是url-loade拥有更多的配置项。

module: {
	rules: [{
		test: /\.(png|jpg|gif)$/,
        use: [{
            loader: 'url-loade',
            options: {
            	name: '[path][name].[ext]', // 自定义名字,[name]名字[ext]扩展名,还可以加混淆比如哈希值
                outputPath: 'images/',  //将打包的文件,生成打包相对文件(dist)的images/的目录下
                limit: '1024', // 图片打包base的大小限制,单位是字节
            }
		}]
	}]
}

复制代码

使用url-loader会默认的将图片打包成了base64的文件,但是在正常情况下,只有在图片比较小的话,才会打成base的格式,因此需要在options里面进行图片大小进行配置

CSS模块化打包

在打包css的时候,需要下载一个css-loaderstyle-loaderlodaer去做编译,如果要使用预编译,则需要安装对应的预编译loader。例如:sass需要 sass-loadernode-sass这两个loader,根据实验,只需要sass-loader就可以了。

css-loader用来打包css

style-loader用来挂在页面,将这个loader自动挂在到html页面上,一般方式在html头部

基本配置信息如下(包含了sass预处理器):

module.exports = {
    module: {
    rules: [{
        test: /\.css$/,  //  /\.sass$/
        use: [ 'style-loader', 'css-loader' , 'sass-loader']
      }]
  }
}

复制代码

给CSS3添加前缀需要使用postcss-loaderlodaer和 autoprefixer插件,继续使用上面的例子,基本配置如下:

module.exports = {
    module: {
    rules: [{
        test: /\.css$/,  //  /\.sass$/
        use: [ 'style-loader', 'css-loader' , 'sass-loader','postcss-loader'] // 写法1
        use: [ 'style-loader', 'css-loader' ,'sass-loader',{ // 写法2,可以对每个lodaer做单独配置
        	loader:'postcss-loader',
        	options: {
            	plugins: [
                	require("autoprefixer") /* 生成浏览器前缀 */
             ]}
    	}] 
      }]
  }
}

复制代码

如果要让它自动添加前缀的话,需要在项目根目录新建一个文件(也可以是其它文件夹,全局配置一般都放在根目录),postcss.config.js,在里面进行一个配置,也可以如上代码那样,写在webpack.config.js中;基本配置信息如下:

module.exports = {
	 plugins: [ //插件的方式
     	 require'autoprefixer'// 加载模块  默认写法
         require('autoprefixer')({ // webpack或者postcss.config.js配置,
    		overrideBrowserList: 'last 100 versions'
		})
     ]
}

复制代码

autoprefixer在默认的配置下,只能自动的添加webkit的前缀信息,如果需要添加更多的配置,还需要添加额外的参数。

需要在package.json里面添加一个属性,属性名是browserslist,对应的是一个数组,里面填入浏览器、或者node版本的名称,例如:"last 100 versions"

如果需要在webpack里面配置的话,就需要使用overrideBrowserList属性去替换browserslist这个属性,否则在编译的时候,webpack会有错误提示。关于browserslist的具体用法,请点击这里

CSS模块化机制,简单配置:

module.exports = {
    module: {
    rules: [{
        test: /\.css$/,  //  /\.sass$/ 		// 匹配的后缀名
        use: [ 'style-loader', {  // 配置需要的加载器,loader
            loader: 'css-loader',
            options: {
                modules: {
                 	 localIdentNane: '[name]_[hash:base64:10]' // 自定义class的名字,bese需要截取
                }
            }
        }, 'sass-loader']
      }]
  }
}

复制代码

如果开启了模块化操作,那么在模块化导入的时候,文件就只能是局部的方式导入,否则webpack在编译的时候,会出错,例子如下:

import 'xxx.css'  // 全局引入
import xxx from 'xxx.ss' // 局部引入

复制代码

打包字体:打包字体和打包文件一样,loader用的也是打包图片的那个file-loader

module: {
	rules: [{
		test: /\.(woff|woff2|svg|ttf|eot )$/,  // 字体文件后缀名
        use: [{
            loader: 'file-loader',
            options: {
            	outputPath: 'font/'  //生成的文件夹,将打包的文件,放在里面
                name: [name].[ext]   // 修改输出文件名,这样就默认原来的文件名和后缀名
            }
	}]
	}]
}

复制代码

devServer常用配置

webpack-dev-server 能够用于快速开发应用程序,所以简称devServer;devServer是用来提高开发效率的,不是用devServer来做打包,它提供了一些配置项,可以用于改变devServer的默认行为,要配置devServer,除了可以在配置文件里通过devServer传入参数,还可以通过命令行传入参数。

/* 截取的vue cli的例子 通过命令传递的参数*/
"dev": "webpack-dev-server --host 0.0.0.0  --inline --progress --config build/webpack.dev.conf.js",

复制代码

但是需要注意的是:devServer 只适用于开发模式

开启本地服务

webpack开启本地服务器安装一个内置插件webpack-dev-server来帮我们开启一个本地服务,虽然webpackDevServer插件是内置的,但是依然需要下载,只是不需要引入,需要在webpack.config.jspackage.json文件进行配置,配置内容如下:

/* webpack.config.js 配置 */
module.exports = {
	devServer: {
		contentBase: './dist' // 配置打包的路径
        open: true, // 设置是否默认打开
        prot: "8080", // 端口
        hot: true , // 开启热更新
	}
}
/* package.json 配置 */
"scripts": {
    "dev": "webpack-dev-server --open"
}

复制代码

在正确配置了webpack-dev-server以后,从dist目录中提供文件,默认地址是:localhost:8080,也可以自定义配置,配置方案如上代码。更多配置点击这里,但是这种方法,会更新所有数据,可以理解成是"页面刷新"

webpack-dev-server在编译后不会写任何输出文件。相反,它将捆绑文件保存在内存中并为它们提供服务,就好像它们是安装在服务器根路径上的真实文件一样。如果您的页面希望在不同的路径中找到捆绑包文件,则可以使用publicPathdev服务器配置中的选项更改此选项。

webpack 热更新

在使用webpack热更新的时候,需要有一个插件HotModuleReplacementPlugin,简称是HMR,这个插件是webpack集成的一个内置插件,在使用的时候,不需要下载,但是要导入webpack

但是HotModuleReplacementPlugin这个插件不能用于生产模式,热更新也是devServer里面的一个小项。 HotModuleReplacementPlugin里面的配置项,可以忽略,官方说是实验性的,不推荐配置。

const webpack = require('webpack') 
plugins:[
 	new webpack.HotModuleReplacementPlugin({
		multiStep: true, // 将分两步构建 - 首先编译热更新块,然后编译剩余的普通资产
		fullBuildTimeout: Number // 时间,表示启用 multiStep 这个之间的延迟
        requestTimeout: Number // 时间(毫秒) 用于下载清单的超时时间
	});   
 ]

复制代码

请注意,webpack.HotModuleReplacementPlugin完全启用HMR是必需的。如果webpack或者webpack-dev-server使用该--hot选项启动,则会自动添加此插件,因此您可能不需要将此添加到您的webpack.config.js

"dev": "webpack-dev-server --host 0.0.0.0 "  // 上述说明的实例,不需要webpack.config.js配置

复制代码

webpack 预加载

webpack 反向代理

dev-server 使用了非常强大的 http-proxy-middleware 包。更多高级用法,请查阅其文档;在我们做开发的时候,会因为跨域问题导致无法进行数据请求,因此需要借助wenpack提供的反向代理,既porxy,使用方法如下:

/* 省略其它代码 */
devServer: {
    porxy: {
    	"/api": "http://localhost:3000"// 基础用法,将/api绑定到目标服务器。
        "/api": { // 采用配置的用法,用法二
         	target: "http://localhost:3000", // 目标服务器
            pathRewrite: {"^/api" : ""},  //路径重写,这样在前端访问的时候,就不需要传/api
            secure: false, // 配置是否需要使用https的方式,默认是不允许
        }
    }, // 多个API请求同一服务器的写法
     porxy:[{
     	context: ["/auth", "/api"], // 多个请求
  		target: "http://localhost:3000", // 目标服务器
     }]
}
/***  前端请求,已axions为例  ***/
   axios.get('/api/xxx').then(red => { xxx })  
 axios.get('// xxx').then(red => { xxx })  // 配置请求的方式访问 忽略掉/api

复制代码

假设有多个API请求都在同一个服务器,可以使用context上下文的方式跟上一个数组,代码如上例子,关于webpack proxy的更多配置,请戳这里访问官网更多说明。

webpack 预加载

SourceMap的设置

其实配置devtool就是在配置SourceMap,意为:源文件映射,能够快速找到代码出错的位置,但是这种方式,需要打包完了以后才能看到是否出错;但是也有折中的解决方案,需要在package.json里面设置一个脚本,可以自动的为我们打包,但它是一个文件协议,正常来说,页面需要手动刷新,所有还是使用devServer的方式会比较好点儿。

module.export = {
    /* cheap-module-eval-source-map 开发环境使用,速度快,包含三方模块 仅提示行错误*/
    /* cheap-module-source-map  生产环境使用, 其实也可以不使用*/ 
    /* source-map 源文件映射  inline-source-map(精准告诉你哪行那列出错)*/
    devtool: 'cheap-module-eval-source-map'
}
/*================================= package.json ===============================*/
"scripts": {
    "dev": "webpack --watch" 
  }

复制代码

devtool您可以使用SourceMapDevToolPlugin, 而不是使用该选项,EvalSourceMapDevToolPlugin因为它有更多选项。切勿同时使用devtool选项和插件,该devtool选项在内部添加插件,因此您最终会将插件应用两次。

devtool您可以使用SourceMapDevToolPlugin, 而不是使用该选项,EvalSourceMapDevToolPlugin因为它有更多选项其实这句话,我也不太理解是什么意思? 如果需要更加细粒度的控制,就使用SourceMapDevToolPlugin这个插件,但是不能同时使用,原因上面也说了。 其实就我个人觉得,自带的就够了。

Tree Shaking的配置

Tree Shaking翻译过来就是摇晃树枝 ,所以简称摇树;举个简单的例子:秋天的马路,马路两边种了很多行道树,有的叶子枯黄、有的叶子任然是绿色,因为枯黄的叶子随风飘落,为了更好的解决落叶的问题,就找了一个大型的器械,抓着树干使劲摇晃,枯黄的叶子也就掉落了下来,剩下的就是不易掉落的绿色叶子。

但是反过来可以这样理解,就是说,对于webpack来说,入口就相当于是一棵树干,入口上面有很多模块,这些模块就是树枝,但是这些依赖模块并没有全部使用,或者只使用了模块中的某一个功能,这时就需要借助Tree Shaking它将不需要的模块给摇掉,也就是说:在打包后文件里面,代码都是精简且需要的。原理请戳这里:Tree-Shaking性能优化实践 - 原理篇

tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块系统中的静态结构特性,例如 importexport。这个术语和概念实际上是兴起于 ES2015 模块打包工具 rollup

新的 webpack 4 正式版本,扩展了这个检测能力,通过 package.json"sideEffects" 属性作为标记,向 compiler 提供提示,表明项目中的哪些文件是 "pure(纯的 ES2015 模块)",由此可以安全地删除文件中未使用的部分。

说了这么多? 那在webpack里面应该怎样使用呢?需要在package.json中配置

{
  "sideEffects": false, // 简单版本,
  "sideEffects": [ // 使用数组的方式,个人理解:就是将不需要摇的东西给剔除出来,。
      "@babel/polyfill",
      "./src/some-side-effectful-file.js",
      "*.css"
  ]
}

复制代码

这样只是标记“未使用代码(dead code)”,但是需要是在编译后(bulid)删除,因此:我们需要切换到生产模式(production)来表示,命令模式(--optimize-minimize);使用配置的方式,代码如下:

mode: 'production',  // 必须是生产模式
optimization: {	 // 设置删除为标记的 未使用代码,也就是死代码
	usedExports: true
}

复制代码

总结:Tree Shaking只在生产模式有效,开发模式无效,并且只能使用ESmodul的方式,详细说明请戳这里

单独的webpack配置

在vue cli 2.x的版本里面有很多webpack配置,一开始我也不知道为什么需要这么配置,通过后来的了解才知道,在webpack里面有很多模式,比如:生产模式、开发模式,介于它们之间的还有各种配置文件、公共模块等,以下是vue cli的webpack配置目录。

.
|—— vue cli
|—— config
|	|——	dev.env.js
|	|——	index.js
|	|——	prod.env.js
|	|__	test.env.js
|——	build
|	|——	build.js
|	|——	check-versions.js
|	|——	utils.js
|	|——	vue-loader.conf.js
|	|——	webpack.base.conf.js
|	|——	webpack.dev.conf.js
|	|__	webpack.prod.conf.js
|
.

复制代码

可是有这么多的单独的文件,最终需要怎么才能合并在一起呢? 是直接引入还是需要使用插件呢?这里需要使用一个插件,这个插件的名字是:webpack-merge,在使用它的时候,需要先去安装这个插件。

如果将上面的目录进行一个拆分,假设就只有webpack.prod.conf.jswebpack.dev.conf.jswebpack.base.conf.js这么三个文件,其中webpack.base.conf.js是一个公共文件,另外两个表示生产模式和开发模式。使用webpack-merge的方法如下:

/* ======= 以生产模式为例 webpack.prod.conf.js======= */
const merge = require('webpack-merge');
const common = require('./webpack.prod.conf.js');

module.exports = merge(common, { 
	mode: 'production',
	devtool: 'source-map'
});

复制代码

还需要在npm里面进行配置,仍然已vue cli目录为例,在package.json中加入以下内容:

scripts": {
	"dev": "webpack-dev-server --open --progress --config build/webpack.dev.conf.js",
	"build": "webpack --config build/webpack.prod.conf.js"
}
/* --progress 显示进度条的意思 */

复制代码

环境变量的使用

就只想使用一个文件入口,在里面配置一个环境变量,然后就能按照自己的需要,产生不同的编译结果,实施方法可以按照如下操作。

那里面的commonConfig文件和prodConfig还有这个devConfig它们也是一个单独的文件,为了避免一个文件很大,所以在使用的过程中,还是单独分开写的比较好,只不过在具体配置的时候,使用一个环境变量来判断。

module.export = (env) => {
    if(env && env.production){ // 条件判断
        return merge(commonConfig, prodConfig); // 生产模式(线上模式)
    } else {
        return merge(commonConfig, devConfig); // 开发模式
    }
}

复制代码

看了上面的代码,会想到一个问题,条件判断的变量env && env.production是从哪里来的,其实这个变量是从package.json``里面配置得来的,也就是说:如果需要使用这种方式的话,就需要去更改启动脚本,代码如下:

scripts": {
	"dev": "webpack-dev-server --open --progress --config build/webpack.base.conf.js",
	"build": "webpack --env.production. --config build/webpack.base.conf.js"
}
/* --progress 显示进度条的意思 */

复制代码

可以很清除的看到devbuild这两个方法启动的目录文件都是同一个,但是在build里面多了一个env.production这个属性,其实,这个变量就是上面的条件判断语句所需要的参数。

环境变量还可以这样使用,就nodeJS提供的process.env.NODE_ENV,具体的时候方法,参考简书NodeJs/Vue项目中对process.env的使用

在Webpack中配置Esint

在很多时候,我们都需要使用eslint来帮助我们进行代码检查和规范,这样往往在团队中,能做到风格统一,非常有利于团队协作、后期维护等,但配置eslint是非常繁琐的,下面一起来看看,我们如何在webpack中如何配置eslint吧?在慕课手记中也有关于自定义的详细说明,更多查看请戳这里

eslint在使用前,需要先安装eslint这个包,更多安装方法戳这里查看官方教程,安装方法如下:

npm install eslint --save-dev // 下载eslint这个安装包
npx eslint --init // 运行这个命令进行eslint的详细配置

复制代码

关于eslint配置说明,当运行npx eslint --init的时候,会弹出如下信息:

? How would you like to use ESLint? (Use arrow keys)  //如何配置eslint这个文件,你想如何使用
  To check syntax only  // 仅检查语法
> To check syntax and find problems // 检查语法并找出问题
  To check syntax, find problems, and enforce code style //检查语法、发现问题和强制代码样式
/* 我选择的是检查语法并找出问题,然后就有了下面的配置信息 */

? What type of modules does your project use? // 你想使用什么类型的模块呢?
  JavaScript modules (import/export) // 基于ES6
> CommonJS (require/exports)	// 这个主要是node中使用
  None of these // 这些都不是                                                                   

  /* 因为在我的实验中,用的是vue,所需需要使用esmodule这种语法,然后弹出的信息如下 */
  ? Which framework does your project use? (Use arrow keys) // 你想使用那个框架
   React
 > Vue.js  // 因为我是vue,所有选择vue
  None of these // 其它框架
  
  /* 当选择vue后,配置如如下,问你是在那个地方运行,可以多选,使用空格点亮,然后回车 */
  ? Where does your code run? (Press <space> to select, <a> to toggle all, <i> to invert selection)
>(*) Browser // 浏览器
 ( ) Node    // node                                                                                  
/* 当我选择以后,会出现如下配置  当然是使用JS文件的方式,个人爱好*/
? What format do you want your config file to be in? (Use arrow keys) // 使用什么文件作为配置文件
> JavaScript
  YAML
  JSON

  /* 问你是否想安装它们,这里当然选择是了 */
  ? Would you like to install them now with npm? (Y/n) // y 基础配置过程就完毕了

复制代码

当一些列配置完毕后,会发现在项目的根目录中有一个.eslintrc.js的配置文件,这个文件就是eslint的配置文件啦,更多配置请戳这里,也可以选择第三方公司的编码配置,安装方法参考eslint配置(使用Airbnb编码规则),比较变态的就是airbnb规则了,非常的严格。

虽然这样在vscode(前提是webpack中有插件)中可以使用了,但是对于其它的代码编辑器,却不会怎么报错? 所以还需要下载一个eslint-loader才可以,具体操作方法如下:

 npm install eslint-loader --save-dev

复制代码

下载好了以后,基本配置如下,但是一定要将eslint-loader放在最后面,因为loader的执行顺序是从后往前:

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: ["babel-loader", "eslint-loader"] 
      }
    ]
  }
};

复制代码

仅仅是这样(如果不做如下配置,只能在cmd控制台显示),还是不行滴,还需要一点儿额外的其它配置,配置如下:

module.exports = {
  devServer: {
    overlay: true  //  在浏览器上弹出一个层,也就是在浏览器中显示全屏覆盖
  }
};

复制代码

但都知道在vue cli2 的版本中,当检测到警告信息,会输出在浏览器的控制台显示,但是这个问题,我还木有解决,有知道的小伙伴,可以告诉我以下。

overlay:{
    warnings: false, 
    errors: true
}

复制代码

我也按照vue cli源代码进行了一个配置,发现并没有按照我的意愿来。

打包第三方库(shimming)

在刚学Vue.js的时候,那时候运用的不是很熟,有时候需要操作DOM,但是Vue.js操作DOM非常的不方便;有时候需要获取到 一个列表里面所有的标签,那时候就想到了使用Jquery来帮我操作DOM,当时就以为像其它JS文件一样全局导入就可以了,可在实际运用的时候却傻眼了,就发现,能够在控制台输出Jquery对象,但是却无法操作DOM,这让我很抓狂。

经过学习,知道webpakc本身就已经提供了一套关于shimming解决方案:使用webpack内置的ProvidePlugin插件和expose-loader,在网上找了很多文档,在使用webpack内置的ProvidePlugin插件的时候,虽然勉强解决这个问题,但是还是有一些小问题,就eslint报错的问题。后来发现使用expose-loader的方式也可以解决这个问题。

webpack.ProvidePlugin

ProvidePluginwebpack内置插件,因此在使用前需要导入webpack,它主要的功能是:自动加载模块,而不必到处 importrequire , 这也是webpack推荐的使用方法。

但是:对于 ES2015 模块的 default export,你必须指定模块的 default 属性,因为它的名字;以jquery为例: 它实际上就是 import $ from 'jquery',具体例子请看如下代码。更多例子请戳这里,查看官方示例说明。

const webpack = require('webpack')
/* 省略其它代码  */
plugins: [
    new webpack.ProvidePlugin({
        $: 'jquery',  // 后面的jquery其实就是 import $ from 'jquery'
        JQuery: 'jquery'
    })
]

复制代码

expose-loader

可以通过expose-loaderwebpack注入一个全局的公共模块,假设用了这个方法导入了jquery的话,是可以全局使用,可以理解成绑定到了window上,因此,在使用它的时候,需要下载这个expose-loader,以jquery为例,使用方法如下:

module: {
  rules: [{
    test: require.resolve('jquery'), // 导入jquery模块,但这个方法是node.js提供,和webpack无关
    use: [{
      loader: 'expose-loader', 
      options: 'jQuery' // 全局使用的名称, window.jQuery
    },{
      loader: 'expose-loader',
      options: '$' // 全局使用名称, window.$  
    }]
  }]
}

复制代码

但是这种方法,需要在局部导入(也就是说,需要每个页面都需要导入),然后才能进行全局暴露,但是如果需要全局注入的话,显然这种方式不太妥当。

防止重复(SplitChunksPlugin)

webpack提供了一个Code Splitting拆分代码,点击这里,可以查看代码分离的方式以及介绍。

使用CommonsChunkPlugin用于避免它们的重复依赖,但是无法进一步优化;在webpack4的版本以后,删除了optimization.splitChunks这个属性;SplitChunksPlugin方法使用如下:

module.exports = {
    splitChunks: {
        chunks: "all",  // 同步和异步都做分割
        cacheGroups: { // 缓冲组
            vendors:false,
            default: false
        }
    }
}

复制代码

SplitChunksPlugin的更多配置如下,具体参考官方说明,戳这里点击官方文档

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async', // async 默认推荐使用,异步加载做分割, initial 同步  all 同步和异步都做分割
      minSize: 30000, // 字节 引入的包或者模块大于这个这个值,才会做代码分割,只针对同步加载
      maxSize: 0, // 超过 minSize 以后,会进行二次打包,默认不配置,不能强拆
      minChunks: 1, // 引入次数,引入次数为多个,可以打包
      maxAsyncRequests: 5, // 请求超过5个的部分不拆分,一般默认值
      maxInitialRequests: 3, // 页面初始化同时发送的请求数量最大不能3个,超过以后就不会被拆分,默认值就会
      automaticNameDelimiter: '~', // 用以代码分离打包的文件名默认连接符,cacheGroups里面的vendors
      automaticNameMaxLength: 30, // 最长字节数,不能超过这个值
      name: true, // 拆分的名字, true表示根据模块名和cacheGroups组的key来自动生成文件名
      cacheGroups: { // 缓存配置,一个文件import多个库,如果需要多个库打包成一个文件,则使用这个缓存组
        vendors: {
          test: /[\\/]node_modules[\\/]/, // 检测引人的库是否是node_modules下面的
          priority: -10, // 权重,决定哪个组的优先配置
          name: "vendors" // 自定义名字,不需要后缀名
        },
        default: { // 前面没有匹配上,则使用这个组,通常是业务代码
          minChunks: 2, // 引入超过2次才从新打包成一个新文件
          priority: -20, // 权重
          reuseExistingChunk: true // 当为true后就不会重复打包,会进行自动复用。
        }
      }
    }
  }
};

复制代码

chunks的时候,还需要走cacheGroups缓冲组,分离出来的文件名是:cacheGroups里面的vendors,中间的连接符是automaticNameDelimiter,例vendors-main.js

cacheGroups可以自定义多个组,如果多个组都满足要求,则以权重大的组为准。

webpack为了更好的提升性能,最好的还是使用异步的方式,这也是官方推荐。

Lazy Loding(动态导入懒加载)

当涉及到动态代码拆分时,webpack 提供了两个类似的技术。对于动态导入,第一种,也是优先选择的方式是:ES7提出的一个草案,既import()语法。第二种,则是使用 webpack 特定的 require.ensure。先来说说第一种:

动态导入最大的好处就是实现了懒加载,用到那个模块就才会调用那个模块,非常适合ASP导入,可以大大的减少等待时间,但使用这个需要用到babel去转,也就是需要下载@babel/plugin-syntax-dynamic-import这个插件,使用方法如下:

{
  "plugins": ["@babel/plugin-syntax-dynamic-import"] // 使用插件,在webpack plugins里面添加
}

复制代码

在页面添加动态import导入懒加载的方式,这种方式会返回一个promise对象,代码如下:

/* 声明这个方法 需要一个函数 */ 
function getComponent() {
	import(/* webpackChunkName: "lodash" */ 'lodash').then( _ => {
            var element = document.createElement('div');
            element.innerHTML = _.join(['Hello', 'webpack'], ' ');
            return element;
		 }).catch( error => 'An error occurred while loading the component');
 }
/* 调用这个方法 如果不调用则不执行*/
getComponent().then(component => {
	document.body.appendChild(component);
})

复制代码

这种方法,则会对代码进行分离,将动态打入的第三方模块,会单独的打一个文件,而自己所写的业务代码,则会被打包到新的一个文件里面。

/* webpackChunkName: "lodash" */ 'lodash'魔法字符串,懒加载,配合SplitChunksPlugin使用,可以进行一个单独配置,其实这个配置和SplitChunksPlugin是共用。

module.exports = {
    splitChunks: {
        cacheGroups: { // 缓冲组
            vendors: {
              test: /[\\/]node_modules[\\/]/, // 检测引人的库是否是node_modules下面的
              priority: -10, // 权重,决定哪个组的优先配置
              name: "vendors" // 自定义名字,不需要后缀名
        	},
            default: false
        }
    }
}

复制代码

但是使用懒加载的时候,会有一个弊端,就是当用户需要一个模块的时候,只有触发以后,才能去加载对应的模块,这就会造成一个网络的延时,有一个等待期,不利于用户体验,所以就需要另外一个技术,也就是预加载,当页面自动加载出来以后,利用闲置的带宽,去加载其它可能需要的模块。

预加载(Prefetching)

这个是webpack 4.6.0新增的一个功能,用通俗的话来说,就是在网络空闲的时候,才去自动下载其它的非必要模块,这种方式极大的提升了用户体验,也解决了等待延时问题,可是要怎么使用呢?

/* 使用方法和上面的 懒加载是一样的,只是这个魔法字符串里面的参数不一样 */
import(/* webpackPrefetch: true */ 'LoginModal');

复制代码

在声明导入时使用这些内联指令允许webpack输出“Resource Hint”,使用方法如上,它告诉浏览器:

  • prefetch:未来可能需要资源
  • preload:当前可能需要资源

区别:preload是和核心代码一起加载,prefetch预加载

缓存带来的代码提升是有限的,所以使用懒加载代码优化是现在性能优化的主要使用方式。

webpack打包分析

关于打包分析,官网为我们提供了一个内置插件Bundle Analysis,在guider下面的Code Splitting里面的Bundle Analysis,他有几种类型,webpack-chart:(交互式饼图)等类型,具体请戳这里。使用方法也很简单,只需要在package.json中配置一段脚本即可,例子如下:

scripts": {
	"build": "webpack  --profile --json > stats.json --env.production. --config build/webpack.base.conf.js"
}

复制代码

其实也可以直接使用webpack --profile --json > stats.json, 打包完成后,会生成一个stats.json文件,这个文件就是结果分析,然后需要将这个文件上传到这个网站,进行数据分析,但是这个表,感觉非常不人性化,个人觉得很难看,所以需要使用webpack-bundle-analyzer插件。

Webpack Bundle Analyzer也是一个打包分析,它和webpack-chart不同的是,Webpack Bundle Analyzer 是一个很直观的平面图,这是一个插件,所以在使用的时候,需要进行下载安装,npm install --save-dev webpack-bundle-analyzer

如果使用了这个以后,在打包的时候,控制台不会输出打包信息,比如文件、 打包时间、chunk等信息,这也算是一个弊端。

在使用它的时候,也需要有如上的package.json中配置(实测,可不需要使用),在webpack里面的基本配置如下,更多配置请戳这里

// 导入插件
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
// 使用它
module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}

复制代码

CSS的代码分离

它默认是打包在js文件里面,这样就会导致文件变得很大,所以将CSS代码分离出来是很有必要的,使用CSS代码分析则需要使用一个插件来帮我们完成,这个一般只做线上版本,在开发版本则不需要,这个插件的名字是MiniCssExtractPlugin,点击这里查看更多官方配置。

npm install --save-dev mini-css-extract-plugin  // 需要先安装

复制代码

简单的配置说明,在使用的时候需要将style-loader替换为MiniCssExtractPlugin.loader,如果需要在开发环境中使用热更新,需要重新进行一个设置。

const MiniCssExtractPlugin = require('mini-css-extract-plugin'); // 导入插件
module.exports = {
  plugins: [
    new MiniCssExtractPlugin({ // 方法同output里面的方法,最好就是加一个hash值,防止缓存
      filename: '[name].css',  // 生成的文件名
      chunkFilename: '[id].css', // 构建时生成,主要是防止文件更新浏览器不更新,缓存导致的
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader, // 替换style-loader
            options: { // 其它的配置项
              publicPath: '../', // 指定的地址,同等与output里面的publicPath
              hmr: process.env.NODE_ENV === 'development', // 环境变量
            },
          },
          'css-loader',
        ],
      },
    ],
  },
};

复制代码

CSS代码分离,默认是不会被压缩,如果想要代码压缩,则需要使用optimize-css-assets-webpack-plugin插件,但需要注意的是:设置optimization.minimizer会覆盖webpack提供的默认值,因此需要指定,JS minimalizer, 使用方法如下:

const TerserJSPlugin = require('terser-webpack-plugin'); // 不添加则JS不会被压缩
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); // 导入CSS分离插件
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); // CSS代码压缩
module.exports = {
  optimization: { // 添加一个属性,
    minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css',
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },
};

复制代码

设置optimization.minimizer会导致重写,所以需要下载terser-webpack-plugin这个插件,去压缩JS,如果没有这个插件就会导致,CSS代码被压缩了,但是JS代码则不会被压缩。

配置模块的解析方式(resolve)

Webpack 在启动后会从配置的入口模块出发找出所有依赖的模块,Resolve 配置 Webpack 如何寻找模块所对应的文件。 Webpack 内置 JavaScript 模块化语法解析功能,默认会采用模块化标准里约定好的规则去寻找,但你也可以根据自己的需要修改默认的规则。

关于resolve的基本配置如下,更多配置戳这里查看官方教程。

const path = require('path') // node提供的文件路径模块
module.exports = {
  resolve: {
    extensions: ['.wasm', '.mjs', '.js', '.json'],  // 自动解析确定的扩展,可以省略的扩展名
    alias: { //创建 import 或 require 的别名,来确保模块引入变得更简单。
        "@": path.resolve(__dirname, '../src'),
        xyz$: path.resolve(__dirname, 'XXX.js') // 精确匹配 xyz	
    }
  }
};

复制代码

vue loader的使用

在搭建vue脚手架之前,需要下载两个loader,分别是:vue-loadervue-template-compiler,具体的安装方法查看Vue loader官方说明。

npm install -D vue-loader vue-template-compiler // 安装

复制代码

需要注意的问题:每个 vue 包的新版本发布时,一个相应版本的 vue-template-compiler 也会随之发布。编译器的版本必须和基本的 vue 包保持同步,这样 vue-loader 就会生成兼容运行时的代码。这意味着你每次升级项目中的 vue 包时,也应该匹配升级 vue-template-compiler。

main.js中加入最基本的代码,这也是最基本的入口配置信息

import Vue from 'vue'

window.vm = new Vue({
  el: '#app',
  render: h => h(App),
  components: { App },
  template: '<App/>'
})


复制代码

正常来说,webpack在编译的时候,是不认识.vue的后缀名,因此需要我们手动的在webpack里面新增一个loader,用于识别.vue文件,基本配置如下:

// webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')

module.exports = {
  mode: 'development',
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      // 它会应用到普通的 `.js` 文件
      // 以及 `.vue` 文件中的 `<script>` 块
      {
        test: /\.js$/,
        loader: 'babel-loader'
      },
      // 它会应用到普通的 `.css` 文件
      // 以及 `.vue` 文件中的 `<style>` 块
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    // 请确保引入这个插件来施展魔法
    new VueLoaderPlugin()
  ]
}
复制代码

vue loader15+的版本都需要去使用一个vue-loader/lib/plugin插件,这个插件不用单独下载,在使用vue-loader的时候,这个插件就已经有了。

webpack打包优化(DllPlugin)

为了更快的使用打包优化,需要及时更新nodeJS以及webpack的版本,尽可能的减少一些loader的转换,并且需要剔除一些不必要的目录,比如:node-modules的文件夹,插件尽可能得时候使用官方推荐。 还可以使用webpack内置的DllPlugin插件去帮我们进行打包速度优化。

DllPlugin插件专门用于单独的webpack配置中,以创建仅限dll的捆绑包。它创建了一个manifest.json文件,用于DllReferencePlugin映射依赖项(先创建一个,然后进行复制,这样就提升了打包效率)。

更多的方法戳这里查看官方教程,通常是创建一个webpack.dll.js文件,用来专门配置dllplugin,目的是为了将一些公共的库,打包成一个文件,后续打包不会重新打包,只会去引用打包好的包。

const path = require('path') // 导入node 模块
module.exports = {
    mode: 'production', // 生产模式
    entry: {
        vendors: ['vue','jquery'] // 入口文件,自定义属性
    },
    output:{
        filename: '[name].dll.js', // 输出,多个文件打包成一个文件
        path: path.resolve(__dirname, '../dll'), // 打包的文件夹目录
        library: '[name]' // 导出全局变量,用于其它文件
    }
}
复制代码

但是这种方法,不会自动添加到html页面中,因此需要一个插件add-asset-html-webpack-plugin,来帮我我们将webpack.dll.js打包后文件自动添加到html中,关于更多说明戳这里查看NPM教程

 cnpm i add-asset-html-webpack-plugin -D  // 安装
复制代码

使用它的时候,此时就不能写在webpack.dll.js文件中,应该写在公共的文件当中,比如webpack.base.conf.js文件里面,基本配置方法如下:

const HtmlWebpackPlugin = require('html-webpack-plugin'); // 导入自动生成html插件
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin'); //  自动添加插件
const webpackConfig = {
  entry: 'index.js',
  output: {
    path: 'dist',
    filename: 'index_bundle.js',
  },
  plugins: [
    new HtmlWebpackPlugin(),// 更多说明请看前面的 HtmlWebpackPlugin 配置
     // require.resolve和 path.resolve, 在这里的用法是一样的,
    new AddAssetHtmlPlugin({ filepath: require.resolve('../dll/vendors.dll.js') }),
  ],
};
复制代码
文章分类
前端
文章标签