首先贴上自己关键插件版本
"webpack": "^5.52.0",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.1.1"
本文引用依赖较多,请注意配置是否已失效。
webpack核心概念
本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。
在开始前你需要先理解一些核心概念:
入口起点(entry point) 指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
输出(output)
output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js
,其他生成文件默认放置在 ./dist
文件夹中。
loader
webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中
在更高层面,在 webpack 的配置中,loader 有两个属性:
test
属性,识别出哪些文件会被转换。use
属性,定义出在进行转换时,应该使用哪个 loader。
插件(plugin)
loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
Webpack 支持所有符合 ES5 标准 的浏览器(不支持 IE8 及以下版本)。webpack 的 import()
和 require.ensure()
需要 Promise
。如果你想要支持旧版本浏览器,在使用这些表达式之前,还需要 提前加载 polyfill。
环境(environment)
Webpack 5 运行于 Node.js v10.13.0+ 的版本。
process.env
process.env就是Nodejs提供的一个API,它返回一个包含用户环境信息的对象。如果我们给Nodejs 设置一个环境变量,并把它挂载在 process.env
返回的对象上,便可以在代码中进行相应的环境判断。
设置process.env
一般通过设置package.json来实现,但是Windows 系统和Mac系统有区别。
Windows 系统
// package.json
{
...
"scripts": {
"dev": "set NODE_ENV=development webpack-dev-server",
"build": "set NODE_ENV=production xxx"
}
}
Mac 系统
// package.json
{
...
"scripts": {
"dev": "export NODE_ENV=development webpack-dev-server ",
"build": "export NODE_ENV=production xxx"
}
}
它们的语法都相同,在相反环境会带来问题,所以有了cross-env。 cross-env是一个跨平台设置环境变量的第三方包,它可以让你只配置一行命令,就能轻松地在多个平台设置环境变量。首先先安装
npm install --save-dev cross-env
然后
// package.json
{
...
"scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server ",
"build": "cross-env NODE_ENV=production xxx"
}
}
使用
全局下可以使用
//webpack.config.js
const isProd = process.env.NODE_ENV === 'production';
一、初识webpack
1、配置文件名称
webpack默认配置文件:webpack.config.js
可以通过webpack --config指定配置文件
module.exports = {
entry: './src/index.js', // 4.0会默认制定入口位置为‘src/index.js’
output: './dist/main.js', // 4.0会默认制定入口位置为‘dist/main.js’
mode: 'production', // 环境
module: {
rules: [ // loader配置
{
test:/\.txt$/, use: 'raw-loader'
}
]
},
plugins:[
new HtmlwebpackPlugin({ // 插件配置
template: './src/index.html'
})
]
}
2、安装nvm
安装 nvm(node.js version management,顾名思义是一个nodejs的版本管理工具。通过它可以安装和切换不同版本的nodejs。下面列出下载、安装及使用方法。)
安装命令:
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
or Wget:
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
安装完之后添加到环境变量。
source ~/.bash_profile
// 推出并重启终端,查看是否安装成功:
nvm --version
// 安装node.js:
nvm i v10.15.3
// 创建项目文件夹,并初始化
mkdir 01project
cd 01project
// 所有询问都是yes
npm init -y
//安装webpack
npm i webpack webpack-cli --save-dev
//查看项目是否安装成功
./node_modules/.bin/webpack -v
webpack 5.52.0
webpack-cli 4.8.0
3、一个简单例子
新建webpack.config.js文件
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
},
mode: 'production'
}
新建src/index.js、src/helloworld.js文件
// index.js文件
import { helloworld } from "./helloworld";
document.write(helloworld())
// helloworld.js文件
export function helloworld(){
return 'hello webpack'
}
运行./node_modules/.bin/webpack命令,打包文件
新建dist/index.html文件,并引入打包文件
<!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>
</head>
<body>
<script src="./bundle.js"></script>
</body>
</html>
4、通过npm script运行webpack
为什么package.json可以直接运行node_module/.bin的命令
原理:模块局部安装会在node_module/.bin目录创建软链接
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build":"webpack"
},
新建src/serach.js文件
document.write('search info')
修改webpack.config.js文件 //通过占位符确保文件名称唯一
const path = require('path')
module.exports = {
entry: {
'index': './src/index.js',
'search': './src/search.js'
},
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js'
},
mode: 'production'
}
修改dist/index.html文件
<!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>
</head>
<body>
<script src="./index.js"></script>
<script src="./search.js"></script>
</body>
</html>
5、核心概念之Loaders
webpack开箱即用只支持JS和JSON两种文件类型,通过Loaders去支持其他文件类型并且把她们转化成有效的模块,并且可以添加到依赖图中。
本身是一个函数,接受源文件作为参数,返回转换的结果。
常用的loaders有哪些
6、核心概念之Plugins
插件用于bundle文件的优化,资源管理和环境变量注入,作用域整个构建过程。
二、常用Loaders
2.1、解析es6、React JSX、Vue
2.1.1 解析es6 babel-loader(配置文件.babelrc)
安装相关依赖
npm install -D babel-loader @babel/core @babel/preset-env webpack
webpack配置
module:{
rules: [
{
test: /\.js$/,
use: 'babel-loader'
}
]
}
为了支持es6需要增加配置
module:{
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
option: {
// babel-loader两个重要概念
// presets是一系列babel-plugins的集合,
// 一个plugins对应一个功能
presets: ["@babel/preset-env"],
// plugins: ['@babel/plugin-proposal-object-rest-spread']
}
}
}
]
}
又或者给babel一个配置文件.babelrc,新建.babelrc文件
{
"presets": ["@babel/preset-env"]
}
2.1.2 解析React JSX @babel/preset-react
安装相关依赖
npm install -D react-dom @babel/preset-react
修改.babelrc文件
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
修改src/search.js文件,编写react组件
import React from 'react'
import ReactDOM from 'react-dom'
class Search extends React.Component{
render() {
return <div>search components</div>
}
}
ReactDOM.render(
<Search/>,
document.getElementById('root')
)
2.1.3 解析Vue vue-loader
和 vue-template-compiler
Vue Loader 的配置和其它的 loader 不太一样。除了通过一条规则将 vue-loader
应用到所有扩展名为 .vue
的文件上之外,请确保在你的 webpack 配置中添加 Vue Loader 的插件:
这个插件是必须的! 它的职责是将你定义过的其它规则复制并应用到 .vue
文件里相应语言的块。例如,如果你有一条匹配 /\.js$/
的规则,那么它会应用到 .vue
文件里的 <script>
块。
npm install -S vue
npm install -D vue-loader vue-template-compiler
// webpack.config.js
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
module: {
rules: [
// ... 其它规则
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
// 请确保引入这个插件!
new VueLoaderPlugin()
]
}
import search from './search.vue' // 正常的vue写法
//默认 webpack 无法打包 .vue文件 要安装相关的loader cnpm i vue-loader vue-template-compiler -D
import Vue from 'vue'
var vm = new Vue({
el: '#app',
data: {
msg: '123'
},
render: (createElements, context) => createElements(search)
})
2.2 css解析相关 css-loader、style-loader
2.2.1css-loader、style-loader(css-loader解析css、style-loader将样式通过<style>标签插入到head中)
安装依赖
npm i -D css-loader style-loader
修改webpack.config.js文件
module:{
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
}
},
{
test: /\.css$/,
use: [ //webapck use执行顺序从右到左、链式调用,所以这里先执行css-loader
'style-loader',
'css-loader',
]
}
]
}
新建src/css/index.css文件
.search-txt {
font-size: 20px;
color: blue;
}
在src/search.js文件中引用css文件
import React from 'react'
import ReactDOM from 'react-dom'
import './css/index.css'
class Search extends React.Component{
render() {
return <div className="search-txt ">search components</div>
}
}
ReactDOM.render(
<Search/>,
document.getElementById('root')
)
2.2.2 解析less和sass (nde-sass sass-loader 因为sass-loader 依赖node-sass)
安装依赖
npm i -D less less-loader
修改src/css/index.css文件为src/css/index.less
修改src/css/search.js内关于index.css的引用名
修改webpack文件
module:{
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
}
},
{
test: /\.css$/,
use: [ //webapck use执行顺序从右到左、链式调用,所以这里先执行css-loader
'style-loader',
'css-loader',
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader',
]
}
]
}
npm install --save-dev sass-loader node-sass
2.3 资源解析
2.3.1 file-loader用于处理图片
安装依赖npm i -D file-loader
在src/img文件夹下放置一张图片
在src/search.js文件中引入图片
import React from 'react'
import ReactDOM from 'react-dom'
import './css/index.less'
import fileImg from './img/fileImg.png'
class Search extends React.Component{
render() {
return <div className="search-txt ">search components
<img src={fileImg}></img>
</div>
}
}
ReactDOM.render(
<Search/>,
document.getElementById('root')
)
添加相关配置
module:{
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
}
},
{
test: /\.css$/,
use: [ //webapck use执行顺序从右到左、链式调用,所以这里先执行css-loader
'style-loader',
'css-loader',
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader',
]
},
use: {
loader: 'file-loader',
options: {
esModule: false
}
}
]
}
这是因为file-loader默认采用ES模块语法,即import '../image.png'
;然而Vue生成的是CommonJS模块语法,即require('../image.png')
;二者不一致。要么让file-loader或url-loader采用CommonJS语法,要么让Vue采用ES语法。刚好file-loader或url-loader有一个esModule
选项能调整,将其设置为false即可:
2.3.1 file-loader用于处理字体
新建src/font文件夹,放入字体文件
css引入,修改src/css/index.css文件
@font-face{
font-family: 'liuKai';
src: url('../font/liuKai.ttf')
}
.search-txt {
font-size: 20px;
color: blue;
font-family: 'liuKai';
}
修改webpack配置
module:{
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
}
},
{
test: /\.css$/,
use: [ //webapck use执行顺序从右到左、链式调用,所以这里先执行css-loader
'style-loader',
'css-loader',
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader',
]
},
{
test: /\.(png|jpeg|gif|jpg)$/,
use: [
'file-loader']
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader']
}
]
}
2.3.2 url-loader也可以处理图片和字体,可以设置较小资源自动base64
安装依赖
npm i -D url-loader
webpack.config.js中修改关于图片处理的部分
{
test: /\.(png|jpeg|gif|jpg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10240 // 限制大小为10k
}
}]
},
url-loader和file同时使用需要注意,最好不要同级使用
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use:
{
loader: 'url-loader',
options: {
limit: 100024,
name: 'img/[name].[hash:7].[ext]',
esModule: false,
fallback: {
loader: 'file-loader',
options: {
name: '[name].[hash:8].[ext]'
}
}
}
}
}
2.4 文件监听(watch)
文件监听是在发现源码发生变化时,自动重新构建出新的输出文件
webpack开启监听模式,有两种方式
- 启动webpack命令时,带上--watch参数
- 在配置webpack.config.js中设置watch:true
缺陷:每次需要手动刷新浏览器
"dev": "cross-env NODE_ENV=development webpack --watch"
或者
watch: true, //默认为false 不开启
watchOptions: { // 只有watch开启时,watchOptions才会有意义
ignored: /node_modules/, // 不坚挺的文件或文件夹,支持正则匹配
aggregateTimeout: 300, //监听到变化后300ms再去执行,默认300ms
poll: 1000 //判断文件是否变化的轮询间隔时间(有变化先混存,到期再更新)
}
2.5 热更新:webpack-dev-server(配合HotModuleReplacementPlugin)
wds不刷新浏览器
wds不输出文件,而是放在内存中
安装依赖
npm install webpack-dev-server -D
"scripts": {
"dev": "webpack-dev-server --open"//有更新自动打开浏览器
},
由于热更新主要是用在开发环境中,修改mode: development
HotModuleReplacementPlugin是内部插件
const webpack = require('webpack')
plugins: [
new webpack.HotModuleReplacementPlugin()
],
devServer: { // 专门为webpack-dev-server指定相关配置选项
hot: true,
static: {
directory: path.resolve(__dirname, "dist"),
}
},
热更新的原理
- webpack compile:将JS变异成bundle
- HRM Server:将热更新的文件输出给HMR Runtime
- Bundle Server:提供文件在浏览器的访问
- HMR Runtime:会注入到浏览器,更新文件的变化
- bundle.js: 构建输出的文件
2.5 文件指纹(ContentHash,ChunkHash,Hash)
文件指纹如何生成
在webpack
中有三种hash
可以配置
hash 所有文件哈希值相同,只要改变内容跟之前的不一致,所有哈希值都改变,没有做到缓存意义
chunkhash 当有多个chunk
,形成多个bundle
时,如果只有一个chunk
和一个bundle
内容变了,其他的bundle
的hash
都会发生变化,因为大家都是公用的一个hash
,这个时候chunkhash
的作用就出来了。它根据不同的入口文件(Entry)
进行依赖文件解析、构建对应的 chunk
,生成对应的哈希值。
contenthash 在打包的时候我们会在js
中导入css
文件,因为他们是同一个入口文件,如果我只改了js
得代码,但是他的css
抽取生成css
文件时候hash
也会跟着变换。这个时候contenthash
的作用就出来了。
2.5.1 js文件的指纹设置(chunkhash在生产环境使用,无法和热更新HotModuleReplacementPlugin一起使用)
设置output的filename,使用[chunkhash]// filename: '[name].[chunkhash].js'
新建一份生产环境专用的webpack配置文件webpack.prod.js,
设置mode: 'production',去掉热更新相关模块。
设置文件指纹长度filename: '[name]_[chunkhash:8].js'
设置图片、字体的文件指纹
{
test: /\.(png|jpeg|gif|jpg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name]_[hash:8][ext]'
}
}]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name]_[hash:8][ext]'
}
}]
}
const path = require('path')
module.exports = {
entry: {
'index': './src/index.js',
'search': './src/search.js'
},
output: {
path: path.join(__dirname, 'dist'),
filename: '[name]_[chunkhash:8].js'
},
mode: 'production',
module:{
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
}
},
{
test: /\.css$/,
use: [ //webapck use执行顺序从右到左、链式调用,所以这里先执行css-loader
'style-loader',
'css-loader',
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader',
]
},
{
test: /\.(png|jpeg|gif|jpg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name]_[hash:8].[ext]'
}
}]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name]_[hash:8].[ext]'
}
}]
}
]
}
}
添加prod执行命令
"prod":"webpack --config webpack.prod.js"
2.5.2 css文件指纹设置(plugin:MiniCssExtractPlugin将css样式抽离到css文件)
设置MiniCssExtractPlugin的filename,使用[contenthash]
// filename: '[name].[contenthash].js'
安装依赖
npm i -D mini-css-extract-plugin
引入插件,加入plugin数组中
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
plugins: [
new MiniCssExtractPlugin({
filename: '[name]_[contenthash:8].css'
});
]
}
同时还需要添加到依赖中。MiniCssExtractPlugin的功能是将css样式抽离到css文件,而style-loader是将样式以<style>标签的形式引入,二者有所冲突,可以去除style-loader。
{
test: /\.css$/,
use: [ //webapck use执行顺序从右到左、链式调用,所以这里先执行css-loader
MiniCssExtractPlugin.loader,
'css-loader',
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
]
},
2.5.2 图片、字体文件指纹设置
设置file-loader的filename,使用[hash]
// filename: '[name].[hash].js'
2.6 文件压缩 (prod环境)
2.6.1 js 文件的压缩(内置的terser-webpack-plugin, 生产模式生效)
parallel: true, //使用多进程并发运行 强烈建议打开,可以填写数字
const TerserPlugin = require('terser-webpack-plugin')
optimization: { // 优化
minimize: true,
minimizer: [
new TerserPlugin({
test: /\.js(\?.*)?$/i, //匹配参与压缩的文件
parallel: true, //使用多进程并发运行
terserOptions: {
compress: {
drop_console: true,//移除所有console相关代码;
drop_debugger: true,//移除自动断点功能;
pure_funcs: ["console.log", "console.error"],//配置移除指定的指令,如console.log,alert等
},
format: {
comments: false, // 删除注释 也可写入正则配合extractComments剥离有效注释
},
},
extractComments: false // 是否将注释剥离到单独的文件中
})
]
},
2.6.2 css 文件的压缩 css-minimizer-webpack-plugin
npm install css-minimizer-webpack-plugin --save-dev
引入依赖
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /.s?css$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
},
],
},
optimization: {
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
// `...`,
new CssMinimizerPlugin(),
],
},
plugins: [new MiniCssExtractPlugin()],
};
如果还想在开发环境下启用 CSS 优化,请将 optimization.minimize
设置为 true
:
webpack.config.js
// [...]
module.exports = {
optimization: {
// [...]
minimize: true,
},
};
2.6.3 html 文件的压缩(html-webpack-plugin prod环境)
修改html-webpack-plugin,设置压缩参数(模板文件不能有注释)
安装依赖
npm i -D html-webpack-plugin
引入html
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname,'src/search.html'),// 需要打包的文件路径
filename: 'search.html',
chunks: ['search'], //
inject: true, // 设置为true,js css会自动注入html
minify: {
}
}),
new HtmlWebpackPlugin({
template: path.join(__dirname,'src/index.html'),// 需要打包的文件路径
filename: 'index.html',
chunks: ['index'], //
inject: true, // 设置为true,js css会自动注入html
minify: {//压缩选项
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCss: true,
minifyJs: true,
removeComments: false
}
})
]
}
2.7 自动清理构建目录产物(clean-webpack-plugin)
会自动清除output目录
安装依赖, 引入插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
plugins: [
new CleanWebpackPlugin()
]
}
2.8 自动补齐css前缀(postcss-loader、autoprefixer prod环境)
安装依赖
npm i -D postcss-loader postcss autoprefixer
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
{
loader: 'postcss-loader',
}
]
},
新建postcss.config.js文件进行配置
module.exports = {
plugins: [
require('autoprefixer')({
overrideBrowserslist: [
'last 2 version',
'>1%',
'ios 7']
})
]
}
2.8 移动端CSS px自动转换成rem
px2rem-loader设置尺寸稿。
lib-flexible保证
安装依赖
npm i -D px2rem-loader
//动态计算根元素单位
npm i lib-flexible -S
lib-flexible会自动在html的head中添加一个meta name="viewport"
的标签,同时会自动设置html的font-size为屏幕宽度除以10,也就是1rem等于html根节点的font-size。
配合2.91的raw-loader使用
引入使用
module.exports = {
module: {
rules: [
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
{
loader: 'postcss-loader',
},
{
loader: 'px2rem-loader',
options: {
remUnit: 75, // 1rem = 75px
remPrecision: 8 // 转换时的位数
}
}
]
},
]
}
}
在html内引入lib-flexible代码,后期使用2.8.1介绍raw-loader引入
<!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>
</head>
<body>
<div id="root"></div>
<script>
$( require('raw-loader!babel-loader./meta.html') )
; (function (win, lib) {
var doc = win.document;
var docEl = doc.documentElement;
var metaEl = doc.querySelector('meta[name="viewport"]');
var flexibleEl = doc.querySelector('meta[name="flexible"]');
var dpr = 0;
var scale = 0;
var tid;
var flexible = lib.flexible || (lib.flexible = {});
if (metaEl) {
console.warn('将根据已有的meta标签来设置缩放比例');
var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
if (match) {
scale = parseFloat(match[1]);
dpr = parseInt(1 / scale);
}
} else if (flexibleEl) {
var content = flexibleEl.getAttribute('content');
if (content) {
var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
if (initialDpr) {
dpr = parseFloat(initialDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
if (maximumDpr) {
dpr = parseFloat(maximumDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
}
}
if (!dpr && !scale) {
var isAndroid = win.navigator.appVersion.match(/android/gi);
var isIPhone = win.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio = win.devicePixelRatio;
if (isIPhone) {
// iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)) {
dpr = 2;
} else {
dpr = 1;
}
} else {
// 其他设备下,仍旧使用1倍的方案
dpr = 1;
}
scale = 1 / dpr;
}
docEl.setAttribute('data-dpr', dpr);
if (!metaEl) {
metaEl = doc.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
if (docEl.firstElementChild) {
docEl.firstElementChild.appendChild(metaEl);
} else {
var wrap = doc.createElement('div');
wrap.appendChild(metaEl);
doc.write(wrap.innerHTML);
}
}
function refreshRem() {
var width = docEl.getBoundingClientRect().width;
if (width / dpr > 540) {
width = 540 * dpr;
}
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
win.addEventListener('resize', function () {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}, false);
win.addEventListener('pageshow', function (e) {
if (e.persisted) {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}
}, false);
if (doc.readyState === 'complete') {
doc.body.style.fontSize = 12 * dpr + 'px';
} else {
doc.addEventListener('DOMContentLoaded', function (e) {
doc.body.style.fontSize = 12 * dpr + 'px';
}, false);
}
refreshRem();
flexible.dpr = win.dpr = dpr;
flexible.refreshRem = refreshRem;
flexible.rem2px = function (d) {
var val = parseFloat(d) * this.rem;
if (typeof d === 'string' && d.match(/rem$/)) {
val += 'px';
}
return val;
}
flexible.px2rem = function (d) {
var val = parseFloat(d) / this.rem;
if (typeof d === 'string' && d.match(/px$/)) {
val += 'rem';
}
return val;
}
})(window, window['lib'] || (window['lib'] = {}));
</script>
</body>
</html>
2.8.1 静态资源内联 raw-loader
资源内联的意义
代码层面:
- 页面框架的初始化脚本
- 上报相关打点
- css内联避免页面闪动
请求层面:减少HTTP网络请求数
- 小图片或者字体内联(url-loader)
HTML和JS内联 raw-loader(读取文件返回一个string,插入合适的位置)
css内联
- 借助style-loader
- html-inline-css-webpack-plugin
安装依赖
npm i raw-loader@0.5.1 -D
新建src/meta.html文件,存储相关配置,作为静态资源等待引入
<meta name="keywords" content="CSDN博客,CSDN学院,CSDN论坛,CSDN直播">
<meta name="description"
content="CSDN是全球知名中文IT技术交流平台,创建于1999年,包含原创博客、精品问答、职业培训、技术论坛、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区.">
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<meta name="referrer" content="always">
修改src/index.html文件(模板文件不可有注释,下列代码注释仅作解释说明,不可正式使用)
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 引入元标签 -->
<%=require('raw-loader!./meta.html')%>
<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>
</head>
<body>
<div id="root"></div>
<script>
// 引入js静态资源
<%=require("raw-loader!babel-loader!../node_modules/lib-flexible/flexible.js")%>
</script>
</body>
</html>
2.9 多页面应用
每一次页面跳转的时候,后台服务器都会返回一个新的html,这种类型的网站也就是多页网站,也叫做多页应用。
每个页面对应一个entry、一个html-webpack-plugin
缺点:每次新增或删除页面需要改wenpack配置
利用glob.sync
- entry: glob.sync(path: path.join(__dirname, './src/*/index.js'))
安装依赖
npm i -D glob
文件夹变动,并改动相关引用文件路径。
- src/index.html=>src/index/index.html
- src/index.js=>src/index/index.js
- src/helloworld.js=>src/index/helloworld.js
- src/search.html=>src/search/index.html
- src/search.js=>src/search/index.js
修改webpack入口文件、htmlwebpackplugins配置
const glob = require('glob')
const setMPA = ()=>{
const entry = {}
const htmlWebpackPlugins = []
const entryFiles = glob.sync(path.join(__dirname, './src/*/index.js'))
Object.keys(entryFiles).map((index)=>{
const entryFile = entryFiles[index]
const match = entryFile.match(/src\/(.*)\/index\.js/)
const pageName = match && match[1]
entry[pageName] = entryFile
htmlWebpackPlugins.push(
new HtmlWebpackPlugin({
template: path.join(__dirname, `src/${pageName}/index.html`), // 需要打包的文件路径
filename: `${pageName}.html`,
chunks: [pageName], // 关联entry文件入口名
inject: true, // 设置为true,js css会自动注入html
minify: {
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCss: true,
minifyJs: true,
removeComments: false
}
})
)
})
return {
entry,
htmlWebpackPlugins
}
}
const { entry, htmlWebpackPlugins} = setMPA()
module.exports = {
entry: entry,
output: {
path: path.join(__dirname, 'dist'),
filename: '[name]_[chunkhash:8].js'
},
mode: 'production',
plugins: [
].concat(htmlWebpackPlugins)
}
2.10 使用source map
作用:运行代码与源代码之间完全不同,不方便调试应用、定位错误信息,source map就是用来映射转换之后的代码与源代码之间的关系。
开发环境开启,线上环境关闭(线上排查问题可以将sourcemap上传到错误监控系统)
webpack支持多source-map实现方式,效率和效果也不相同。
- eval:仅能定位文件 不生成source-map
- eval-source-map:可以定位到文件、行列信息 生成source-map
- cheap-eval-source-map:只能定位到行 生成source-map
- cheap-module-eval-source-map:定位代码与源代码一模一样
- 特征:eval是否使用eval执行代码
- cheap-source-map:是否包含行信息
- module是否能够得到loader处理之前的源代码
- inline-source-map以data-url形式嵌入代码,会增大代码体积。
- hidden-source-map开发第三方包使用,并没有通过注释的方式引入,所以浏览器看不到效果。
- nosources-source-map没有源代码,但同样提供了行列信息
webpack选择合适的source-map选择
- 开发模式:eval-cheap-module-source-map
- 发布打包:不使用sourcemap,避免暴露源代码或者使用nosources-source-map
devtool: 'source-map'
2.11 提取公共页面资源
思路:比如将react、react-dom基础包通关cdn引入,不参与打包
方法:
- 使用html-webpack-externals-plugin
- 利用splitchunksplugin进行公共脚本分离
2.11.1 通过splitchunksplugin提取公共页面资源
chunks参数说明
- async同步引入的库进行分离(默认)
- initial同步引入的库进行分离
- all 所有引入的库进行分离(推荐)
test:匹配出需要分离的包
- minChunks:设置最小引用次数
- minuSize:分离包的体积大小
optimization: { // 优化
splitChunks: {
chunks: 'async',
minSize: 20000, // 分离包的体积大小
minChunks: 1, // 设置最小引用次数
minRemainingSize: 0,
// minRemainingSize 通过确保拆分后剩余的最小 chunk 体积超过限制来避免大小为零的模块。
// 'development' 模式 中默认为 0。对于其他情况,splitChunks.minRemainingSize 默认为 splitChunks.minSize 的值,
// 因此除需要深度控制的极少数情况外,不需要手动指定它。
// maxAsyncRequests: 30, // 按需加载时的最大并行请求数。
// maxInitialRequests: 30, // 入口点的最大并行请求数。
enforceSizeThreshold: 50000, // 强制执行拆分的体积阈值和其他限制(minRemainingSize,maxAsyncRequests,maxInitialRequests)将被忽略。
cacheGroups: {
// 缓存组可以继承和/或覆盖来自 splitChunks.* 的任何选项。
// 但是 test、priority 和 reuseExistingChunk 只能在缓存组级别上进行配置。将它们设置为 false以禁用任何默认缓存组。
// 缓存组可以继承和/或覆盖来自
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10, // 一个模块可以属于多个缓存组。优化将优先考虑具有更高 priority(优先级)的缓存组。默认组的优先级为负,以允许自定义组获得更高的优先级(自定义组的默认值为 0
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用,而不是生成新的模块。这可能会影响 chunk 的结果文件名。
},
},
},
},
打包公共资源
新建common/index.js文件
2.11.2 通过html-webpack-externals-plugin提取公共页面资源
安装依赖
npm i -D html-webpack-externals-plugin
const htmlWebpackExternalsPlugin = require('html-webpack-externals-plugin')
module.exports = {
new htmlWebpackExternalsPlugin({
externals: [
{
module: 'react',
entry: 'https://11.url.cn/now/lib/16.2.0/react.min.js',
global: 'React'
},
{
module: 'react-dom',
entry: 'https://11.url.cn/now/lib/16.2.0/react-dom.min.js',
global: 'ReactDOM'
}
]
})
]
}
2.12 tree shaking 摇树优化(webpack内置)
使用 webpack默认支持,在.babelre里设置modules: false即可
production mode的情况下默认开启,测试时设置为none
要求:必须是es6的语法,CJS不支持
DCE( dead code elimination)
代码不会被执行,不可到达
代码执行的结果不会被用到
代码只会影响死变量(只写不读)
Tree-shaking原理
利用es6模块的特点:
- 只能作为模块顶层语句出现
- import的模块名只能死字符串常量
- import binding是immutable的
代码擦除:uglify阶段删除无用代码
2.13 scope hositing(webpack内置)
原理:将所有模块的代码按照引用顺序放在一个函数作用域里,然后适当的重命名一些变量以防止变量名冲突
对比:通过scope hoisting可以减少函数声明代码和内存开销
production mode的情况下默认开启
在development模式下,需要手动打开该模块
new webpack.optimize.ModuleConcatenationPlugin()
要求:必须是es6的语法,CJS不支持
2.14 代码分割和动态加载
webpack可以将代码库分割成chunks(愉快),当代码运行到需要它们的时候再进行加载。
适用的场景:
- 抽离相同代码到一个共享块
- 脚本懒加载,使得初始下载的代码更小
懒加载JS脚本的方式
- CommonJS: require.ensure
- ES6: 动态import (需要babel转换)
安装依赖
npm i -D babel-plugin-syntax-dynamic-import
修改.babelra配置,使其支持动态import
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-syntax-dynamic-import"
]
}
修改src/search/search.js文件
import React from 'react'
import ReactDOM from 'react-dom'
import '../css/index.less'
import fileImg from '../img/fileImg.png'
import '../../common/index'
class Search extends React.Component{
constructor() {
super(...arguments)
this.state = {
Text: null
}
}
loadComponent() {
import('./text.js').then((Text)=>{
console.log('Text', Text)
this.setState({
Text: Text.default
})
})
}
render() {
const { Text } = this.state
return <div className="search-txt ">search component
{Text? <Text></Text>: null}
测试字体
<img src={fileImg} onClick={()=>this.loadComponent()}></img>
</div>
}
}
ReactDOM.render(
<Search/>,
document.getElementById('root')
)
2.15 webpack和ESLint结合
方案一:webpack与CI/CD集成
方案二:webpack与ESLint集成
2.15.1 webpack与CI/CD集成
本地开发阶段增加precommit钩子
2.15.2 webpack与ESLint集成 (打包)
安装依赖
npm i -D eslint-config-airbnb eslint@^7.2.0 eslint-plugin-import@^2.22.1 eslint-plugin-jsx-a11y@^6.4.1 eslint-plugin-react@^7.21.5
npm i -D eslint-loader
npm i -D babel-eslint
修改webpack配置文件
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: [
'babel-loader',
'eslint-loader'
]
},
{
]
},
}
新建.eslintrc.js文件,设置eslint规则
module.exports = {
"parser": "babel-eslint", //制定解析器
"extends": "airbnb"
"env": { //JavaScript 可在文件中使用注释来指定环境
"browser": true,
"node": true
}
"rules": {
"semi": "error" //结尾应当有分号
}
}
打包,然后demo收获一堆报错……
2.16 webpack打包库和组件 terser-webpack-plugin
webpack除了可以用来打包应用,也可以用来打包js库
实现一个大整数加法库的打包,要求:
- 需要打包压缩版和非压缩版本
- 支持AMD/CJS/ESM模块引入
建立一个新的项目文件夹large-number
初始化项目,并安装依赖
npm init -y
npm i webpack webpack-cli -D
新建src/index.js文件,存放大整数加法
export default function add(a, b){
let i = a.length - 1
let j = b.length - 1
let carry = 0 // 进位
let res = ''
while ( i >= 0 || j>=0 ){
let x = 0; // a某一位的值
let y = 0; // b某一位的值
let sum
if( i >= 0 ){
x = a[i] - '0'
i --
}
if( j >= 0 ){
y = b[j] - '0'
j --
}
sum = x + y + carry
if( sum >= 10 ){
carry = 1
sum -= 10
}else {
carry = 0
}
res = sum + res
}
if( carry ){
res = carry + res
}
return res
}
修改打包文件
module.exports = {
entry: {
'large-number': './src/index.js',
'large-number.min': './src/index.js'
},
output: {
filename: '[name].js',
library: 'largeNUmber', // 打包后库的的名字
libraryTarget: 'umd', // 打包类库的发布格式,这里使用UMD
libraryExport: 'default' // 对外暴露default属性,就可以直接调用default里的属性
}
}
添加打包命令
"build": "webpack"
如果就此打包的话,'large-number'、'large-number.min'均会被压缩,需要进一步改动
npm i -D terser-webpack-plugin
修改webpack配置,设置使用环境、使用 terser-webpack-plugin插件
const terserWebpackPlugin = require('terser-webpack-plugin')
module.exports = {
mode: 'none', // 避免全部自动压缩,打包出未压缩文件
entry: {
'large-number': './src/index.js',
'large-number.min': './src/index.js'
},
output: {
filename: '[name].js',
library: 'largeNUmber', // 打包后库的的名字
libraryTarget: 'umd', // 打包类库的发布格式,这里使用UMD
libraryExport: 'default' // 对外暴露default属性,就可以直接调用default里的属性
},
optimization: {
minimize: true,
minimizer: [new terserWebpackPlugin({
include: /\.min\.js$/
})],
},
}
新建index.js文件
if(process.env.NODE_ENV === 'production'){
module.exports = require('./dist/large-number.min.js')
}else {
module.exports = require('./dist/large-number.js')
}
发布npm包
npm login
npm publish
在01project项目中使用打包
npm i -D gu-large-number
直接使用
import '../../common/index'
import largeNumber from 'gu-large-number'
export function helloworld(){
return 'hello awebpack'+largeNumber(10,20)
}
2.17 功能模块设计和目录结构:
2.17.1 基础配置
- 资源解析
- 样式增强
- 目录清理
- 多页面打包
- 命令行打包和优化
- css提取成一个单独的文件
2.17.2 开发阶段配置
- 代码热更新
- sourcemap
通过webpack-merge
安装依赖
npm i -D webpack-merge
const merge = require('webpack-merge')
const baseConfig = require('./webpack.base.js')
const webpack = require('webpack')
const devConfig = {
mode: 'development',
plugins:[
new webpack.HotModuleReplacementPlugin(),
],
devServer: { // 专门为webpack-dev-server指定相关配置选项
contentBase: './dist',
hot: true,
stats: 'error-only'
},
devtool: 'cheap-module-eval-source-map'
}
module.exports = merge(baseConfig, devCOnfig)
2.17.2 生产阶段配置
- 代码压缩
- 文件修改
- tree-shaking
- scope hositing
- 速度优化
- 提及优化
安装依赖
npm i -D eslint babel-eslint eslint-config-airbnb-base
新建.eslintrc.js配置文件,配置eslint规则
module.exports = {
"parser": "babel-eslint", //制定解析器
"extends": "airbnb-base"
"env": { //JavaScript 可在文件中使用注释来指定环境
"browser": true,
"node": true
}
"rules": {
// "semi": "error" //结尾应当有分号
}
}
增加运行命令
"eslint": "eslint --fix"
三、分析webpack
3.1、使用webpack内置的stats
在package.json中设置新命令
"build:stats": "webpack --config webpack.prod.js --json > stats.json",
启动,得到一个stats.json文件,着实不直观……
3.2、速度分析:使用speed-measure-webpack-plugin
可以看到每个loader和插件执行耗时
安装依赖
npm i -D speed-measure-webpack-plugin
webpack引入
const speedMeasureWebpackPlugin = require('speed-measure-webpack-plugin')
const smp = new speedMeasureWebpackPlugin()
module.exports = smp.wrap({
})
报错:
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
{
loader: 'postcss-loader',
},
{
loader: 'px2rem-loader',
options: {
remUnit: 75, // 1rem = 75px
remPrecision: 8 // 转换时的位数
}
}
]
},
ERROR in ./src/css/index.less
Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js):
Error: You forgot to add 'mini-css-extract-plugin' plugin (i.e. `{ plugins: [new MiniCssExtractPlugin()] }`), please read https://github.com/webpack-contrib/mini-css-extract-plugin#getting-started
at Object.pitch (/Users/gujianxiang/Demo/study/webpack/01project/node_modules/mini-css-extract-plugin/dist/loader.js:43:14)
@ ./src/search/index.js 25:0-27
暂未解决……
3.3 体积分析:使用webpack-bundle-analyzer
可以分析哪些问题?
- 依赖的第三方模块文件大小
- 业务组件大小
安装依赖
npm i -D webpack-bundle-analyzer
修改配置文件
const webpackBundleAnalyzer = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = {
plugins: [
new webpackBundleAnalyzer(),
],
}
script命令,重点在于--progress
"analyzer": "cross-env ENV_TYPE=staging ANALYZER=true webpack --progress"
3.3 Babel-polyfill 的作用
Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。举个栗子,ES6在Array对象上新增了Array.from方法。Babel就不会转码这个方法。如果想让这个方法运行,必须使用babel-polyfill,为当前环境提供一个垫片。
npm i -D @babel/runtime
npm install -D @babel/cli @babel/core
npm i -D @babel/plugin-transform-runtime
3.4 多进程多实例 thread-loader terser-webpack-plugin多进程并发运行 parallel: true
使用时,需将此 loader 放置在其他 loader 之前。放置在此 loader 之后的 loader 会在一个独立的 worker 池中运行。
use: [
{
loader: "thread-loader",
// 有同样配置的 loader 会共享一个 worker 池
options: {
// 产生的 worker 的数量,默认是 (cpu 核心数 - 1),或者,
// 在 require('os').cpus() 是 undefined 时回退至 1
workers: 2,
// 一个 worker 进程中并行执行工作的数量
// 默认为 20
workerParallelJobs: 50,
// 额外的 node.js 参数
workerNodeArgs: ['--max-old-space-size=1024'],
// 允许重新生成一个僵死的 work 池
// 这个过程会降低整体编译速度
// 并且开发环境应该设置为 false
poolRespawn: false,
// 闲置时定时删除 worker 进程
// 默认为 500(ms)
// 可以设置为无穷大,这样在监视模式(--watch)下可以保持 worker 持续存在
poolTimeout: 2000,
// 池分配给 worker 的工作数量
// 默认为 200
// 降低这个数值会降低总体的效率,但是会提升工作分布更均一
poolParallelJobs: 50,
// 池的名称
// 可以修改名称来创建其余选项都一样的池
name: "my-pool"
},
},
// 耗时的 loader(例如 babel-loader)
];
使用terser-webpack-plugin多进程并发运行 parallel: true, //使用多进程并发运行 强烈建议打开,可以填写数字
3.5 缩小构建目标
3.5.1、比如babel-loader不解析node_modules
exclude: 'node_modules'
include: ''
3.6 图片压缩 image-webpack-loader
module.exports = {
...
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[hash:7].[ext]'
},
},
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 50,
},
optipng: {
enabled: true,
},
pngquant: {
quality: [0.5, 0.65],
speed: 4,
},
gifsicle: {
interlaced: false,
},
webp: { // 不支持WEBP就不要写这一项
quality: 75
},
},
},
],
},
],
},
}
3.7 擦除无效的css purgecss-webpack-plugin
npm i -D purgecss-webpack-plugin
通过 mini-css-extract-plugin 插件使用
const path = require('path')
const glob = require('glob')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const PurgecssPlugin = require('purgecss-webpack-plugin')
const PATHS = {
src: path.join(__dirname, 'src')
}
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
},
optimization: {
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true
}
}
}
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader"
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
}),
new PurgecssPlugin({
paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }),
}),
]
}
3.7 预编译资源模块
认识DLL库
-
什么是DLL
- DLL全称是动态链接库(Dynamic Link Library),是为软件在Windows中实现共享函数库的一种实现方式;
- 那么webpack中也有内置DLL的功能,它指的是可以将可以共享,并且不经常改变的代码,抽取成一个共享的库;
- 这个库在之后编译的过程中,会被引入到其他项目的代码中,减少的打包的时间;
-
DDL库的使用分为两步:
- 第一步:打包一个DLL库;
- 第二步:项目中引入DLL库
3.7.1 预编译资源模块
新建一个处理dll的webpack处理文件,新建一个指向该文件的script命令
const path = require("path");
const { DllPlugin } = require("webpack");
// const { merge } = require("webpack-merge")
// const commonConfig = (isProduction) => {
module.exports = {
// return {
context: path.join(__dirname, "./"),
entry: {
library: ["react", "react-dom"]
},
mode: process.env.NODE_ENV,
output: {
path: path.join(__dirname, "dll"),
filename: "dll_[name].js",
library: "dll_[name]"
},
plugins: [
new DllPlugin({
name: '[name].manifest.json',
path: path.resolve(__dirname, "./dll/[name].manifest.json")
})
],
// }
}
// const devConfig = require('./webpack.dev')
// const prodConfig = require('./webpack.prod')
// module.exports = function (env) {
// const isProduction = env.production;
// process.env.NODE_ENV = isProduction ? "production" : "development"
// const config = isProduction ? prodConfig : devConfig
// return merge(commonConfig(isProduction), config)
// }
打包完成后,在根目录下会有一个dll文件夹,内有Dll文件和相应manifest.json文件。
DLL使用
- 如果我们的代码中使用了react、react-dom,有配置splitChunks的情况下,他们会进行分包,打包到 一个独立的chunk中。
- 但是现在有了dll_react,不再需要单独去打包它们,可以直接去引用dll_react即可:
- 第一步:通过DllReferencePlugin插件告知要使用的DLL库;
- 第二步:通过AddAssetHtmlPlugin插件,将打包的DLL库引入到Html模块中;
new webpack.DllReferencePlugin({
// context:path.join(__dirname, ""),
manifest:path.join(__dirname,"dll/library.manifest.json")
}),