webpack
2020-06-05 14:16:48 0 举报
AI智能生成
前端面试
作者其他创作
大纲/内容
3.webpack核心概念
Loader
loader是什么?
它是一个打包方案,webpack只知道如何打包js文件,不知道如何打包非.js文件,loader可以告诉webpack如何打包这些文件
常用loader
vue-loader
打包.vue文件
file-loader
打包图片、.txt等
打包图片时,单独生成一个图片文件
url-loader
与file-loader的区别
file-loader 单独生产一个图片文件
url-loader 把图片转换成一个base64的字符串,然后直接把这个base64的字符串放到bundle.js里,而不是单独生成一个图片文件
url-loader 把图片转换成一个base64的字符串,然后直接把这个base64的字符串放到bundle.js里,而不是单独生成一个图片文件
利弊
好处是,可以少发一次http请求,适合图片比较小的情况
坏处是,如果图片过大,bundle.js会变得很大,请求时间过长,页面很久才能展示出来
改进
{
loader: 'file-loader',
options: {
limit:2048
}
}
loader: 'file-loader',
options: {
limit:2048
}
}
图片大于2048字节(2KB)时使用file-loader
图片小于2048字节时,使用url-loader,把图片变成base64放到bundle.js里
图片小于2048字节时,使用url-loader,把图片变成base64放到bundle.js里
使用loader打包静态资源
图片
{
loader: 'file-loader',
options: {
name: '[path][name]_[hash].[ext]'
}
}
loader: 'file-loader',
options: {
name: '[path][name]_[hash].[ext]'
}
}
[ext] 资源扩展名
[name] 资源的基本名称
[path] 资源相对于 context的路径
[hash] 内容的哈希值
字体
iconfont
阿里矢量图标库
下载图标,把iconfont.css里的样式粘贴到本地样式文件里,修改字体src
file-loader
它告诉webpack去打包后缀为.eot .ttf .svg的字体
样式
module: {
rules: [
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader','sass-loader','postcss-loader' ]
}
]
}
rules: [
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader','sass-loader','postcss-loader' ]
}
]
}
style-css
在得到css生成的内容以后,把这段内容挂在到页面的<head>里
css-loader
分析几个css文件之间的关系,把几个css文件合并成一段css
css-loader扩展项
{
loader: 'css-loader',
options: {
importLoaders: 2
modules:true
}
},
loader: 'css-loader',
options: {
importLoaders: 2
modules:true
}
},
importLoaders: 2
用于配置「css-loader 作用于 @import 的资源之前」有多少个 loader
对scss文件里@import的scss文件如何进行打包
对scss文件里@import的scss文件如何进行打包
modules:true
对css进行模块化打包,可以防止样式冲突
sass-loader
把.scss文件翻译成css
postcss-loader
自动添加厂商前缀
postcss.config.js
autoprefixer插件
plugins
使打包更加便捷
plugin可以在webpack运行到某一个时刻时帮你做一些事情,比如html-webpack-plugin会在打包结束这一个时刻自动生成html文件
常用插件
html-webpack-plugin
plugins: [
new HtmlWebpackPlugin(), // Generates default index.html
]
new HtmlWebpackPlugin(), // Generates default index.html
]
在打包结束后,自动生成一个html文件,并把打包生成的js自动引入到这个html文件中
plugins: [
new HtmlWebpackPlugin({ // Also generate a test.html
filename: 'test.html',
template: 'src/assets/test.html'
})
]
new HtmlWebpackPlugin({ // Also generate a test.html
filename: 'test.html',
template: 'src/assets/test.html'
})
]
以template指定的文件为模板生成html文件
clean-webpack-plugin
plugins: [
new CleanWebpackPlugin(['dist']),
]
new CleanWebpackPlugin(['dist']),
]
在打包之前,先删除dist目录
hot-module-replacement-plugin
热更新
Entry与Output
多个入口起点
{
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
}
}
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
}
}
filename是输出的js的文件名
[name]里的name就是entry里的key值
打包多个入口文件
静态资源放在CDN时
output: {
publicPath: 'http://cdn/...'
}
publicPath: 'http://cdn/...'
}
打包后的html引入的js文件中会加上 publicPath前缀
SouceMap
它是一个映射关系,它会建立打包后的文件和源文件之间的映射关系,告诉你源文件的什么位置出错
打包会变慢,因为它要构建映射关系
devtool配置选项
devtool: "source-map"
打包后生成 .js.map文件
该文件对源代码和打包后的代码进行映射
该文件对源代码和打包后的代码进行映射
devtool: "inline-source-map"
打包后不会生成.js.map
映射关系以base64的形式被引入打包后的js文件中的souceMappingURL中
映射关系以base64的形式被引入打包后的js文件中的souceMappingURL中
devtool: "cheap-inline-source-map"
只提示哪一行出错不提示列,只针对业务代码不针对loader生成sourcemap,可以提升打包性能
devtool: "eval"
最快,但是业务复杂时提示不全面
devtool: "cheap-module-source-map"
module 针对loader生成sourcemap
production环境选用这个较好
devtool: "cheap-module-eval-source-map"
development环境选这个较好,性能快,信息全
WebpackDevServer
webpack --watch
可以监听变动并自动打包
它不能起服务器,每次打包后需要手动刷新浏览器
webpack-dev-server
功能
监听变动并自动打包
自动打开、刷新浏览器
模拟服务器特性
开启一个web服务器,localhost:8080,启动完服务器打包代码到dist目录下
以http的方式打开浏览器,可以发ajax请求
而以file
而以file
基础配置
devServer: {
contentBase: path.join(__dirname, 'public'),
open: true,
proxy: ,
port: ,
}
contentBase: path.join(__dirname, 'public'),
open: true,
proxy: ,
port: ,
}
contentBase:WebpackDevServer 服务器要启动在哪一个文件夹下
open: true, 启动时自动打开浏览器
proxy 接口代理
port 端口号
Hot Module Replacement(HMR)
devServer: {
hot: true,
hotonly:true
}
并引入hot-module-replacement-plugin插件
hot: true,
hotonly:true
}
并引入hot-module-replacement-plugin插件
module.hot.accept('file',()=>{ })
当file文件的内容改变时,执行后边的回调函数进行热更新
样式文件 vue react不需要手写这段HMR代码,是因为css-loader vue-loader内置了这个功能
Bable
把ES6代码转换成浏览器可以识别的ES5代码
安装
npm install babel-loader @babel/core --save-dev
npm install @babel/preset-env --save-dev
npm install @babel/polyfill --save
npm install @babel/preset-env --save-dev
npm install @babel/polyfill --save
@babel/core
是babel的核心库,
它能让babel去识别js代码里的内容,然后把js代码转成AST抽象语法树,然后再把抽象语法树编译转成新的语法
它能让babel去识别js代码里的内容,然后把js代码转成AST抽象语法树,然后再把抽象语法树编译转成新的语法
babel-loader
负责打通跟webpack的关联,并没有转换
@babel/preset-env
负责转换新的JavaScript句法(syntax)而不转换新的API
@babel/polyfill
弥补ES5缺失的变量或者函数,设置useBuiltIns为'usage'可以只打包已经用到的API语法,按需引入减少文件体积
配置
打包业务代码
presets:[["@babel/preset-env",{
targets:{
chrome:'67'
},
useBuiltIns:"usage"
}]]
targets:{
chrome:'67'
},
useBuiltIns:"usage"
}]]
打包业务代码,只需要设置@babel/preset-env
在chrome大于67以上的版本进行语法转换,如果浏览器本身支持ES6语法,则不进行转换
@babel/pollyfill会污染全局环境
打包内库代码
plugins:[["@babel/plugin-transform-runtime",{
"corejs":2,
"helpers":true,
"regenerator":true,
"useESModules":false
}]]
"corejs":2,
"helpers":true,
"regenerator":true,
"useESModules":false
}]]
生成一些第三方模块或者UI组件时,打包这些库时不希望污染全局变量,
@babel/plugin-transform-runtime会以闭包的方式注入对应的内容,当可以防止污染全局变量
@babel/plugin-transform-runtime会以闭包的方式注入对应的内容,当可以防止污染全局变量
"corejs":2,
"corejs":要设置为2,当页面不存在map promise方法时,才会把代码打包进来
需要额外安装@babel/runtime-corejs2
babel配置也可以单配在.babelrc文件里,将.babelrc放到项目的根目录下
哪些文件转换?
哪些不转换?
哪些不转换?
include: [
path.resolve(__dirname, "app/src"),
path.resolve(__dirname, "app/test")
],
exclude: /node_modules/
path.resolve(__dirname, "app/src"),
path.resolve(__dirname, "app/test")
],
exclude: /node_modules/
include: 表示目录中的哪些 .js 文件需要进行转换
exclude: babel-loader做语法解析时忽略node_modules这些第三方模块
exclude: babel-loader做语法解析时忽略node_modules这些第三方模块
4.webpack进阶
Tree Shaking
当引入一个模块时,不引入该模块所有的代码,只引入需要的代码,剔除掉无用代码
只支持ES Module,因为它的底层是静态引入
不支持CommonJS,因为它的底层是动态引入
不支持CommonJS,因为它的底层是动态引入
配置
webpack.config.js
生产环境
mode: 'production',
自动进行treeShaking
不需要写
optimization: {
usedExports: true,
},
不需要写
optimization: {
usedExports: true,
},
开发环境
mode: 'development',
optimization: {
usedExports: true,
},
optimization: {
usedExports: true,
},
usedExports: true
表示,哪些模块用export导出了就打包,如果模块没有export打包时就忽略掉
表示,哪些模块用export导出了就打包,如果模块没有export打包时就忽略掉
在development环境下打包时,即使用了treeShaking,也不会把代码直接从打包后的js文件里剔除掉,但是会提示exports used
因为开发环境需要做一些调试,如果直接剔除可能会导致soucemap对应的行数出错
因为开发环境需要做一些调试,如果直接剔除可能会导致soucemap对应的行数出错
package.json
"sideEffects": []
把不需要进行treeShaking的文件写到[],比如css文件['*.css']
"sideEffects":false
如果对所有文件都进行treeShaking,设为"sideEffects":false
webpack和code splitting
为什么要分割?
比如引入lodash,仅使用其中一两个方法,仍会把所有的lodash打包进来,造成打包文件过大,加载时间长
分割方法
手动代码分割
新建一个lodash.js
import _ from 'lodash'
window._ = _ ;
import _ from 'lodash'
window._ = _ ;
entry:{
lodash:'./src/lodash.js'
}
lodash:'./src/lodash.js'
}
使用webpack配置进行代码分割
两种情形下的分割配置
同步引入模块代码
webpack.common.js
optimization:{
splitChunks:{
chunks: 'all'
}
}
optimization:{
splitChunks:{
chunks: 'all'
}
}
异步引入模块代码
import('loadash').then。。。
import('loadash').then。。。
无需配置optimization
动态引入语法需安装 npm install babel-plugin-dynamic-import-webpack --save-dev
.bebelrc 添加配置:"plugins": ["dynamic-import-webpack"]
动态引入语法需安装 npm install babel-plugin-dynamic-import-webpack --save-dev
.bebelrc 添加配置:"plugins": ["dynamic-import-webpack"]
代码分割与webpack无关
代码分割的三种方法
- Entry Points:入口文件设置的时候可以配置
- CommonsChunkPlugin
- Dynamic Imports:动态导入
splitChunksPlugin参数详解
splitChunks: {
chunks: "async",
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
chunks: "async",
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
Lazy Loading
其实懒加载就是通过import来异步的加载一个模块。什么时候执行import语法时,它对应的模块才会被载入。
好处是借助Import这种语法,我们可以让页面加载的更快。
好处是借助Import这种语法,我们可以让页面加载的更快。
minChunks: 2
假设我们打包以后会有很多个文件,如果有两个以上文件里面需要引用lodash 那我们就需要对lodash进行代码分割。
假设我们打包以后会有很多个文件,如果有两个以上文件里面需要引用lodash 那我们就需要对lodash进行代码分割。
打包分析 Preloding Prefeching
打包分析
生成打包过程的描述文件:
webpack --profile --json > stats.json
webpack --profile --json > stats.json
打包分析工具
bundle 分析
bundle 分析
https://github.com/webpack/analyse/
webpack-chart
webpack-bundle-analyzer
预加载
使用场景
异步加载交互代码时:例如当点击的时候再加载异步代码,虽然提高了页面初始化速度,但是对用用户点击的体验不好,速度太慢;
为了解决懒加载带来的问题:使用prefetch preload
使用预加载,比如打开一个网站,首页加载完,在首页加载完成后利用空闲时间,预先加载登陆模态框,等用户登陆时模态框可以很快的加载出来
为了解决懒加载带来的问题:使用prefetch preload
使用预加载,比如打开一个网站,首页加载完,在首页加载完成后利用空闲时间,预先加载登陆模态框,等用户登陆时模态框可以很快的加载出来
使用方法
使用魔术注释
import(/* webpackPrefetch: true */ 'LoginModal');
import(/* webpackPrefetch: true */ 'LoginModal');
区别
prefetch 会等主流程都加载完成,等待空闲再加载;(最优)
preload 和主线程一起加载
preload 和主线程一起加载
优化
代码使用率
打开控制台 :ctrl+shift+p 输入coverage 查看js代码使用率
缓存能带来的性能提升是非常有限的,应该重点考虑提升代码的使用率,
有些交互之后才能用到的代码,可以放到单独的异步模块里 提高加载速度及页面利用率
有些交互之后才能用到的代码,可以放到单独的异步模块里 提高加载速度及页面利用率
CSS文件的代码分割
css代码分割插件
mini-css-extract-plugin
mini-css-extract-plugin
解决什么问题
css in js
默认会把css打包进js中
默认会把css打包进js中
作用
css代码分割,可以将css单独打包
缺点
暂时不支持HMR,因此只能在线上使用
安装使用
1、安装插件
npm install --save-dev mini-css-extract-plugin
2、配置webpack.prod.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
plugins: [new MiniCssExtractPlugin()],
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
};
module.exports = {
plugins: [new MiniCssExtractPlugin()],
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
};
引入插件, require...
plugins中使用插件
new MiniCssExtractPlugin({...})
更改loader,开发环境和线上环境分开配置loader,配置loader为MiniCssExtractPlugin.loader
3、经过1和2,还不能把css拆分出来,原因在于
对css文件进行了tree shaking
对css文件进行了tree shaking
webpack.prod.js
optimization:{
usedExports:true
}
usedExports:true
}
package.json
sideEffects:[
'*.css'
]
'*.css'
]
css文件不进行tree shaking
4、css压缩
对抽离出来的css文件进行代码的合并压缩
optimize-css-webpack-plugin
扩展用法
Extracting CSS based on entry
根据入口分割css
将不同入口文件中加载的所有模块的css样式打包到一个文件里
比如有两个入口文件index.js index1.js,将index.js index1.js中加载的所有模块的样式分别进行打包
比如有两个入口文件index.js index1.js,将index.js index1.js中加载的所有模块的样式分别进行打包
配置
配置 cacheGroups
optimization:{
splitChunks:{
cacheGroups:{ 。。。 }
}
}
optimization:{
splitChunks:{
cacheGroups:{ 。。。 }
}
}
Extracting all CSS in a single file
当有多个入口文件时,将所有入口文件里引入的css文件全部打包到一个css文件里
配置
optimization: {
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true,
},
},
},
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true,
},
},
},
将所有 .css文件打包到一个名为styles的文件里
webpack与浏览器缓(caching)
缓存
浏览器缓存
存在什么问题
存在什么问题
代码打包,将dist目录下的文件上传到服务器
用户第一次打开页面,浏览器加载打包后的文件,
用户再次刷新,则浏览器从缓存中拿文件
修改源代码,重新打包,将dist目录下的文件上传到服务器
用户再次刷新,浏览器请求文件时发现本地有缓存(因为文件的名字没有改变),则从缓存中拿文件,就不会加载新上传的文件
用户第一次打开页面,浏览器加载打包后的文件,
用户再次刷新,则浏览器从缓存中拿文件
修改源代码,重新打包,将dist目录下的文件上传到服务器
用户再次刷新,浏览器请求文件时发现本地有缓存(因为文件的名字没有改变),则从缓存中拿文件,就不会加载新上传的文件
怎么解决
在每次修改源文件后,打包时使用contenthash改变文件的名字,可以解决上述问题
contenthash
作用
contenthash,是根据content产生的一个hash字符串,content不变hash字符串就不会变
如果源代码没有改变,那么打包生成文件的contenthash永远都不会变
如果源代码没有改变,那么打包生成文件的contenthash永远都不会变
配置
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].js'
}
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].js'
}
修改源文件,重新打包,将dist目录下的文件(main.js、vendor.js、index.html)放到服务器上去
当用户再次访问线上的页面时,流程如下:
打开index.html,浏览器加载index.html中<script>引入的js文件,
加载vendor.js时,发现vendors.***.js里的contenthash值没变,则使用本地缓存
加载main.js时,main.******.js里的contenthash值变了,则去服务器上加载最新的main.js文件
当用户再次访问线上的页面时,流程如下:
打开index.html,浏览器加载index.html中<script>引入的js文件,
加载vendor.js时,发现vendors.***.js里的contenthash值没变,则使用本地缓存
加载main.js时,main.******.js里的contenthash值变了,则去服务器上加载最新的main.js文件
shimming的作用
作用
解决webpack打包时存在的兼容性问题,修改webpack的默认行为,实现webpack自身无法实现的一些效果
使用场景
eg: 自动引入第三方库
如果使用了一些版本比较老的第三方模块,它里边用到了jquery或者lodash等第三方库,而这些模块并没有使用ES6 moudle的 import引入这些第三方库,
这时候打包会出错,使用providePlugin,可以在模块中自动引入依赖的第三方库
如果使用了一些版本比较老的第三方模块,它里边用到了jquery或者lodash等第三方库,而这些模块并没有使用ES6 moudle的 import引入这些第三方库,
这时候打包会出错,使用providePlugin,可以在模块中自动引入依赖的第三方库
用途
自动引入第三方库
new webpack.providePlugin({
$: 'jquery'
})
$: 'jquery'
})
当发现一个模块中使用了$,就会在模块里自动引入jquery这个模块,然后把$作为模块的名字
即,在底层完成了 import $ from 'jquery'
即,在底层完成了 import $ from 'jquery'
改变this的指向
安装 imports-loader
修改配置 loader: 'imports-loader?this=>window'
修改配置 loader: 'imports-loader?this=>window'
this默认指向它所在的模块
改变this的指向,让它指向window
改变this的指向,让它指向window
5.webpack实战配置案例
Library打包(库打包)
引子
当自己实现一个库时,打包后的文件给别人使用,用户可能会使用各种引入方式,比如 ES6 module 、CommonJS 、AMD、 <script>。。。
你要如何配置才能支持用户的各种引入方式?
你要如何配置才能支持用户的各种引入方式?
配置
ouput:{
library:'root',
libraryTarget:'umd'
},
library:'root',
libraryTarget:'umd'
},
<script>标签引入
library:'root',
生成一个全局变量root,用户可以在<srcipt src='root.js'></script>中引入
模块化引入
libraryTarget:' umd'
表示用户可以用任意的模块引入方式引入这个这个模块
libraryTarget:' this'
变量root会注入到this对象中
libraryTarget:' window'
变量root会注入到window对象中
防止重复引入
externals:['lodash']
externals的值为数组或对象
表示打包过程中要忽略打包的库,防止使用时用户重复引入库
发布npm包
npm adduser 用户 密码 邮箱
npm publish 把项目发布到npm的仓库里
npm publish 把项目发布到npm的仓库里
TypeScript的打包配置
typescript
它是javascript的一个超集
规范了js语法,提升代码的可维护性
安装配置
安装
npm install ts-loader typescript -D
创建tsconfig.js文件,并配置
配置webpack.config.js
多页面打包配置
多页面应用的本质就是创建多个HtmlWebpackPlugin
增加入口
创建htmlWebpackPlugin
chunks的作用
让index.html中只引入index.js,list.html中只引入list.js
如果不配置chunks,.html中会引入所有的.js文件
如果不配置chunks,.html中会引入所有的.js文件
6.webpack底层原理及脚手架分析
如何编写一个loader
loader本质
它是一个函数
写一个loader
并使用
并使用
使用自己写的loader
配置webpack
写一个loader
编写一个loader
不能写成箭头函数,要写成声明式函数,
因为这个函数里需要用this,webpack在调loader时,会把this做一下变更,变更之后才能使用this里的一些方法
因为这个函数里需要用this,webpack在调loader时,会把this做一下变更,变更之后才能使用this里的一些方法
source是引入文件的内容,即源代码,loader可以针对该源代码进行一些加工,返回你想要的内容
常见使用场景
捕获业务代码异常
可以对业务代码进行异常捕获,当检测到function时,把它放入try{function(){}}catch(e){}中
实现整个网站的国际化
伪代码
在业务代码中,使用{{title}}占位符,loader会根据不同的语言环境,把网站打包成不同的语言版本
如何编写一个plugin
插件本质
它是一个类
编写&使用插件
定义一个插件
一个插件的基本格式
使用插件
在webpack.config.js中配置plugins
以new 一个类实例的形式来使用
插件的参数
传递参数
webpack配置文件
用name给插件传递参数
接受参数
在插件中,options接受参数
webpack性能优化
提高打包速度
1. 跟上技术的迭代(Node,Npm,Yarn)
新版本的webpack node等性能更好,打包更快
2. 在尽可能少的模块上应用Loader
合理的使用include和exclude,减少loader的作用范围,减少一些无用的分析可以加快打包速度
3. Plugin尽可能精简并确保可靠
尽量少的使用插件,并确保每一个插件的可靠性和性能(尽量使用官方插件,性能有保证)
在开发环境下,不要使用代码压缩插件,节约代码压缩这部分的打包时间
在开发环境下,不要使用代码压缩插件,节约代码压缩这部分的打包时间
4.resolve参数合理配置
extensions: ['.js','.jsx']
这个作用是引入文件的时候省略后缀名
要合理配置,如果配置过多,需要查找很多次有没有这些后缀的文件,会有性能损耗
因此,建议只把.js .jsx .vue 配置到extensions里,资源类的引入时直接带着后缀引入
因此,建议只把.js .jsx .vue 配置到extensions里,资源类的引入时直接带着后缀引入
alias:{
child: path.resolve(__dirname,'../src/child')
}
child: path.resolve(__dirname,'../src/child')
}
指定一个路径的别名,当引入child时,其实是引入的'../src/child'
5.使用DllPlugin提高打包速度
问题
引入很多第三方库,每次打包时,都要从node-modules中取出这些库,然后把这些库打包到源代码中
每次打包时重复打包这些第三方库,导致打包时间很长
每次打包时重复打包这些第三方库,导致打包时间很长
解决思路
而这些第三方库几乎不怎么变化,第一次打包时把这些第三方库打包成一个文件存起来,
之后再打包时,直接用第一次打包存起来的代码
之后再打包时,直接用第一次打包存起来的代码
目标:
1.第三方模块打包一次
2.引入第三方模块的时候,使用.dll文件引入
6.控制包文件大小
没用的包去除掉,或者不要引入
把大文件拆分成小文件
7.thread-loader , parallel-webpack , happypack 多进程打包
webpack默认是通过nodejs来运行的,所以它是单进程的打包
可以借助node的多进程提升打包速度
可以借助node的多进程提升打包速度
thread-loader , happypack
多进程打包
parallel-webpack
多页应用打包时,通过parallel-webpack对多个页面一起进行打包
8.合理使用sourceMap
打包时生成sourcemap,内容越详细,打包越慢,根据情况合理使用
9.结合stats分析打包结果
10.开发环境内存编译
在开发环境使用devServer,它在做打包时不会生成dist目录,它会把编译生成的文件放到内存里,内存的读取比硬盘快
11.开发环境无用插件剔除
比如在开发环境,不需要进行代码压缩,所以不要使用压缩代码的插件
这些文章太旧了,
很多插件已经弃用了
白整理了啊 ~ ~ ~
很多插件已经弃用了
白整理了啊 ~ ~ ~
面试题1
几个常见的loader
- babel-loader:把 ES6 转换成 ES5
- file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
- url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
- style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
- css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
- sass-loader: Loads a SASS/SCSS file and compiles it to CSS.
- source-map-loader:加载额外的 Source Map 文件,以方便断点调试
- ts-loader
webpack v5中的
几个常见的plugin
- html-webpack-plugin: 为html文件中引入的外部资源,可以生成创建html入口文件
- DllPlugin
这个插件是在一个额外的独立的 webpack 设置中创建一个只有 dll 的 bundle(dll-only-bundle)。
这个插件会生成一个名为 manifest.json 的文件,这个文件是用来让 DLLReferencePlugin 映射到相关的依赖上去的。
这个插件会生成一个名为 manifest.json 的文件,这个文件是用来让 DLLReferencePlugin 映射到相关的依赖上去的。
- SplitChunksPlugin
Since version 4 the CommonsChunkPlugin was removed in favor of
optimization.splitChunks and optimization.runtimeChunk options.
optimization.splitChunks and optimization.runtimeChunk options.
- HotModuleReplacementPlugin
永远不要在生产环境(production)下启用 HMR
- DefinePlugin
DefinePlugin 允许创建一个在编译时可以配置的全局常量。
这可能会对开发模式和发布模式的构建允许不同的行为非常有用
这可能会对开发模式和发布模式的构建允许不同的行为非常有用
- uglifyjs-webpack-plugin:通过UglifyES压缩ES6代码
clean-webpack-plugin:删除打包文件
webpack有哪些优点
1、专注于处理模块化的项目,能做到开箱即用,一步到位
2、可通过plugin扩展,完整好用又不失灵活
3、使用场景不局限于web开发
4、社区庞大活跃,经常引入紧跟时代发展的新特性,能为大多数场景找到已有的开源扩展
5、良好的开发体验
2、可通过plugin扩展,完整好用又不失灵活
3、使用场景不局限于web开发
4、社区庞大活跃,经常引入紧跟时代发展的新特性,能为大多数场景找到已有的开源扩展
5、良好的开发体验
分别介绍bundle,chunk,module是什么
bundle:是由webpack打包出来的文件,
chunk:代码块,一个chunk由多个模块组合而成,用于代码的合并和分割。
module:是开发中的单个模块,在webpack的世界,一切皆模块,一个模块对应一个文件,webpack会从配置的entry中递归开始找出所有依赖的模块。
chunk:代码块,一个chunk由多个模块组合而成,用于代码的合并和分割。
module:是开发中的单个模块,在webpack的世界,一切皆模块,一个模块对应一个文件,webpack会从配置的entry中递归开始找出所有依赖的模块。
面试题2
5.Loader和Plugin的不同?
不同的作用
Loader直译为"加载器"。Webpack将一切文件视为模块,但是webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到loader。 所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。
- Plugin直译为"插件"。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
不同的用法
Loader在module.rules中配置,也就是说他作为模块的解析规则而存在。 类型为数组,每一项都是一个Object,里面描述了对于什么类型的文件(test),使用什么加载(loader)和使用的参数(options)
Plugin在plugins中单独配置。 类型为数组,每一项是一个plugin的实例,参数都通过构造函数传入。
Plugin在plugins中单独配置。 类型为数组,每一项是一个plugin的实例,参数都通过构造函数传入。
6.webpack的构建流程是什么?
从读取配置到输出文件这个过程尽量说全
从读取配置到输出文件这个过程尽量说全
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
1、初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
2、开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
3、确定入口:根据配置中的 entry 找出所有的入口文件;
4、编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
5、完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
6、输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
7、输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
1、初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
2、开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
3、确定入口:根据配置中的 entry 找出所有的入口文件;
4、编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
5、完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
6、输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
7、输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
7.是否写过Loader和Plugin?
描述一下编写loader或plugin的思路?
描述一下编写loader或plugin的思路?
Loader像一个"翻译官"把读到的源文件内容转义成新的文件内容,并且每个Loader通过链式操作,将源文件一步步翻译成想要的样子。
编写Loader时要遵循单一原则,每个Loader只做一种"转义"工作。 每个Loader的拿到的是源文件内容(source),可以通过返回值的方式将处理后的内容输出,也可以调用this.callback()方法,将内容返回给webpack。 还可以通过 this.async()生成一个callback函数,再用这个callback将处理后的内容输出出去。 此外webpack还为开发者准备了开发loader的工具函数集——loader-utils。
相对于Loader而言,Plugin的编写就灵活了许多。 webpack在运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
编写Loader时要遵循单一原则,每个Loader只做一种"转义"工作。 每个Loader的拿到的是源文件内容(source),可以通过返回值的方式将处理后的内容输出,也可以调用this.callback()方法,将内容返回给webpack。 还可以通过 this.async()生成一个callback函数,再用这个callback将处理后的内容输出出去。 此外webpack还为开发者准备了开发loader的工具函数集——loader-utils。
相对于Loader而言,Plugin的编写就灵活了许多。 webpack在运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
9.如何利用webpack来优化前端性能?(提高性能和体验)
用webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。
1、压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩JS文件, 利用cssnano(css-loader?minimize)来压缩css
2、利用CDN加速。在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于output参数和各loader的publicPath参数来修改资源路径
3、删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数--optimize-minimize来实现
4、提取公共代码。
1、压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩JS文件, 利用cssnano(css-loader?minimize)来压缩css
2、利用CDN加速。在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于output参数和各loader的publicPath参数来修改资源路径
3、删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数--optimize-minimize来实现
4、提取公共代码。
10.如何提高webpack的构建速度? 1、多入口情况下,使用CommonsChunkPlugin来提取公共代码(CommonsChunkPlugin被弃用了)
2、通过externals配置来提取常用库
3、利用DllPlugin和DllReferencePlugin预编译资源模块 通过DllPlugin来对那些我们引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。
4、使用Happypack 实现多线程加速编译(效果不好)
5、使用webpack-uglify-paralle(官网查不到)l来提升uglifyPlugin的压缩速度。 原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度
6、使用Tree-shaking和Scope Hoisting来剔除多余代码
2、通过externals配置来提取常用库
3、利用DllPlugin和DllReferencePlugin预编译资源模块 通过DllPlugin来对那些我们引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。
5、使用
6、使用Tree-shaking和Scope Hoisting来剔除多余代码
11.怎么配置单页应用?怎么配置多页应用?
单页应用可以理解为webpack的标准模式,直接在entry中指定单页应用的入口即可,这里不再赘述
多页应用的话,可以使用webpack的 AutoWebPlugin(查不到)来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范。 多页应用中要注意的是:
1、每个页面都有公共的代码,可以将这些代码抽离出来,避免重复的加载。比如,每个页面都引用了同一套css样式表
2、随着业务的不断扩展,页面可能会不断的追加,所以一定要让入口的配置足够灵活,避免每次添加新页面还需要修改构建配置
多页应用的话,可以使用webpack的
1、每个页面都有公共的代码,可以将这些代码抽离出来,避免重复的加载。比如,每个页面都引用了同一套css样式表
2、随着业务的不断扩展,页面可能会不断的追加,所以一定要让入口的配置足够灵活,避免每次添加新页面还需要修改构建配置
面试题3
一、优化构建速度 使用HappyPack开启多进程Loader转换(happypack 效果不好)
缩小文件的搜索范围
使用DllPlugin减少基础模块编译次数
使用ParallelUglifyPlugin(查不到)开启多进程压缩JS文件
二、优化开发体验
使用自动刷新
Webpack监听文件
1. 启动时 webpack --watch
2. 在配置文件中设置watch:true
DevServer刷新浏览器
开启模块热替换HMR
开启方式
1、webpack-dev-server --hot
2、使用HotModuleReplacementPlugin,比较麻烦
2、使用HotModuleReplacementPlugin,比较麻烦
开启后如果修改子模块就可以实现局部刷新
三、优化输出质量-压缩文件体积
区分环境--减小生产环境代码体积
DefinePlugin
压缩代码-JS、ES、CSS
压缩JS:Webpack内置UglifyJS插件、ParallelUglifyPlugin(官网都查不到)
压缩ES6
uglify-webpack-plugin(v5中的)提供了压缩ES6代码的功能
另外要防止babel-loader转换ES6代码,要在.babelrc中去掉babel-preset-env,
因为正是babel-preset-env负责把ES6转换为ES5
因为正是babel-preset-env负责把ES6转换为ES5
压缩CSS: css-loader?minimize(查不到minimize) PurifyCSSPlugin(查不到)
css-loader内置了cssnano,只需要使用 css-loader?minimize 就可以开启cssnano压缩
cssnano基于PostCSS,不仅是删掉空格,还能理解代码含义
需要配合 extract-text-webpack-plugin (v5 中的)使用,它主要的作用是可以去除没有用到的CSS代码,类似JS的Tree Shaking
使用Tree Shaking剔除JS死代码
它正常工作的前提是代码必须采用ES6的模块化语法,
因为ES6模块化语法是静态的(在导入、导出语句中的路径必须是静态字符串,且不能放入其他代码块中)
因为ES6模块化语法是静态的(在导入、导出语句中的路径必须是静态字符串,且不能放入其他代码块中)
启用Tree Shaking
1、修改.babelrc以保留ES6模块化语句
2、启动webpack时带上 --display-used-exports可以在shell打印出关于代码剔除的提示
3、使用UglifyJSPlugin(v3 中的),或者启动时使用--optimize-minimize
4、在使用第三方库时,需要配置 resolve.mainFields: ['jsnext:main', 'main'] 以指明解析第三方库代码时,采用ES6模块化的代码入口
3、使用
4、在使用第三方库时,需要配置 resolve.mainFields: ['jsnext:main', 'main'] 以指明解析第三方库代码时,采用ES6模块化的代码入口
四、优化输出质量--加速网络请求
使用CDN加速静态资源加载
1、HTML文件:放在自己的服务器上且关闭缓存,不接入CDN
2、静态的JS、CSS、图片等资源:开启CDN和缓存,同时文件名带上由内容计算出的Hash值,
这样只要内容变化hash就会变化,文件名就会变化,就会被重新下载而不论缓存时间多长
2、静态的JS、CSS、图片等资源:开启CDN和缓存,同时文件名带上由内容计算出的Hash值,
这样只要内容变化hash就会变化,文件名就会变化,就会被重新下载而不论缓存时间多长
多页面应用提取页面间公共代码,以利用缓存
原理
大型网站通常由多个页面组成,每个页面都是一个独立的单页应用,多个页面间肯定会依赖同样的样式文件、技术栈等。
如果不把这些公共文件提取出来,那么每个单页打包出来的chunk中都会包含公共代码,相当于要传输n份重复代码。
如果把公共文件提取出一个文件,那么当用户访问了一个网页,加载了这个公共文件,再访问其他依赖公共文件的网页时,就直接使用文件在浏览器的缓存,这样公共文件就只用被传输一次。
如果不把这些公共文件提取出来,那么每个单页打包出来的chunk中都会包含公共代码,相当于要传输n份重复代码。
如果把公共文件提取出一个文件,那么当用户访问了一个网页,加载了这个公共文件,再访问其他依赖公共文件的网页时,就直接使用文件在浏览器的缓存,这样公共文件就只用被传输一次。
相比于webpack3,4.0版本用optimization.splitChunks配置替换了3.0版本的CommonsChunkPlugin插件
分割代码以按需加载
原理
单页应用的一个问题在于使用一个页面承载复杂的功能,要加载的文件体积很大,不进行优化的话会导致首屏加载时间过长,影响用户体验。做按需加载可以解决这个问题
具体方法如下
1、将网站功能按照相关程度划分成几类
2、每一类合并成一个Chunk,按需加载对应的Chunk
3、例如,只把首屏相关的功能放入执行入口所在的Chunk,这样首次加载少量的代码,其他代码要用到的时候再去加载。最好提前预估用户接下来的操作,提前加载对应代码,让用户感知不到网络加载
2、每一类合并成一个Chunk,按需加载对应的Chunk
3、例如,只把首屏相关的功能放入执行入口所在的Chunk,这样首次加载少量的代码,其他代码要用到的时候再去加载。最好提前预估用户接下来的操作,提前加载对应代码,让用户感知不到网络加载
做法
一个最简单的例子:网页首次只加载main.js,网页展示一个按钮,点击按钮时加载分割出去的show.js,加载成功后执行show.js里的函数
import(/* webpackChunkName:show */ './show').then() 是实现按需加载的关键,
Webpack内置对import( *)语句的支持,Webpack会以./show.js为入口重新生成一个Chunk。
代码在浏览器上运行时只有点击了按钮才会开始加载show.js,且import语句会返回一个Promise,加载成功后可以在then方法中获取加载的内容。
这要求浏览器支持Promise API,对于不支持的浏览器,需要注入Promise polyfill。
/* webpackChunkName:show */ 是定义动态生成的Chunk的名称,默认名称是[id].js,定义名称方便调试代码。
Webpack内置对import( *)语句的支持,Webpack会以./show.js为入口重新生成一个Chunk。
代码在浏览器上运行时只有点击了按钮才会开始加载show.js,且import语句会返回一个Promise,加载成功后可以在then方法中获取加载的内容。
这要求浏览器支持Promise API,对于不支持的浏览器,需要注入Promise polyfill。
/* webpackChunkName:show */ 是定义动态生成的Chunk的名称,默认名称是[id].js,定义名称方便调试代码。
五、优化输出质量--提升代码运行时的效率 使用Prepack提前求值 prepack-webpack-plugin
现在Prepack还不够成熟,用于线上环境还为时过早
使用Scope Hoisting
作用域提升
需要分析模块间的依赖关系,所以源码必须是采用了ES6模块化的
六、使用输出分析工具
启动Webpack时带上这两个参数可以生成一个json文件,输出分析工具大多依赖该文件进行分析
1、官方工具Webpack Analyse
2、webpack-bundle-analyzer
可视化分析工具,比Webapck Analyse更直观
七、其他Tips
1、配置babel-loader时,use: [‘babel-loader?cacheDirectory’] cacheDirectory用于缓存babel的编译结果,加快重新编译的速度。另外注意排除node_modules文件夹,因为文件都使用了ES5的语法,没必要再使用Babel转换。
2、配置externals,排除因为已使用<script>标签引入而不用打包的代码,noParse是排除没使用模块化语句的代码。
3、配置performance参数可以输出文件的性能检查配置。
4、配置profile:true,是否捕捉Webpack构建的性能信息,用于分析是什么原因导致构建性能不佳。
5、配置cache:true,是否启用缓存来提升构建速度。
6、可以使用url-loader把小图片转换成base64嵌入到JS或CSS中,减少加载次数。
7、通过imagemin-webpack-plugin压缩图片(查不到imagemin-webpack-plugin)
8、开发环境下将devtool设置为cheap-module-eval-source-map,因为生成这种source map的速度最快,能加速构建。
在生产环境下将devtool设置为hidden-source-map
2、配置externals,排除因为已使用<script>标签引入而不用打包的代码,noParse是排除没使用模块化语句的代码。
3、配置performance参数可以输出文件的性能检查配置。
4、配置profile:true,是否捕捉Webpack构建的性能信息,用于分析是什么原因导致构建性能不佳。
5、配置cache:true,是否启用缓存来提升构建速度。
6、可以使用url-loader把小图片转换成base64嵌入到JS或CSS中,减少加载次数。
7、
8、开发环境下将devtool设置为cheap-module-eval-source-map,因为生成这种source map的速度最快,能加速构建。
在生产环境下将devtool设置为hidden-source-map
博客总结
优质资料
带你深度解锁Webpack系列(基础篇)
带你深度解锁Webpack系列(进阶篇)
带你深度解锁Webpack系列(优化篇)
一步步从零开始用 webpack 搭建一个大型项目
几个常见的loader
- babel-loader:把 ES6 转换成 ES5
图片/字体文件处理
- file-loader
把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
- url-loader
和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
将资源转换为 base64 可以减少网络请求次数,
但是 base64 数据较大,如果太多的资源是 base64,会导致加载变慢,
因此设置 limit 值时,需要二者兼顾
但是 base64 数据较大,如果太多的资源是 base64,会导致加载变慢,
因此设置 limit 值时,需要二者兼顾
- source-map-loader
加载额外的 Source Map 文件,以方便断点调试
处理样式文件
- style-loader
Adds CSS to the DOM by injecting a <style> tag
- css-loader
The css-loader interprets @import and url() like import/require() and will resolve them
- postcss-loader
处理兼容性问题,自动生成浏览器前缀
- sass-loader
Loads a SASS/SCSS file and compiles it to CSS.
- less-loader
Compiles Less to CSS.
几个常见的plugin
- html-webpack-plugin
按照模板生成一个html文件
- DllPlugin
这个插件是在一个额外的独立的 webpack 设置中创建一个只有 dll 的 bundle(dll-only-bundle)。
这个插件会生成一个名为 manifest.json 的文件,这个文件是用来让 DLLReferencePlugin 映射到相关的依赖上去的。
这个插件会生成一个名为 manifest.json 的文件,这个文件是用来让 DLLReferencePlugin 映射到相关的依赖上去的。
- DLLReferencePlugin
- SplitChunksPlugin
Since version 4 the CommonsChunkPlugin was removed in favor of
optimization.splitChunks and optimization.runtimeChunk options.
optimization.splitChunks and optimization.runtimeChunk options.
- HotModuleReplacementPlugin
永远不要在生产环境(production)下启用 HMR
- DefinePlugin
DefinePlugin 允许创建一个在编译时可以配置的全局常量。
这可能会对开发模式和发布模式的构建允许不同的行为非常有用
这可能会对开发模式和发布模式的构建允许不同的行为非常有用
- clean-webpack-plugin
删除打包文件,每次打包前清空dist目录
处理CSS
- mini-css-extract-plugin
抽离CSS,即将CSS文件单独打包,
这可能是因为打包成一个JS文件太大,影响加载速度,也有可能是为了缓存(例如,只有JS部分有改动)
这可能是因为打包成一个JS文件太大,影响加载速度,也有可能是为了缓存(例如,只有JS部分有改动)
- optimize-css-assets-webpack-plugin
将抽离出来的css文件进行压缩
一些弃用的或者项目中无须使用的 uglifyjs-webpack-plugin
通过UglifyES压缩ES6代码
进阶
按需加载
import()
很多时候我们不需要一次性加载所有的JS文件,而应该在不同阶段去加载所需要的代码。
webpack内置了强大的分割代码的功能可以实现按需加载。
webpack内置了强大的分割代码的功能可以实现按需加载。
import() 语法,需要 @babel/plugin-syntax-dynamic-import 的插件支持,
但是因为当前 @babel/preset-env 预设中已经包含了 @babel/plugin-syntax-dynamic-import,因此我们不需要再单独安装和配置。
但是因为当前 @babel/preset-env 预设中已经包含了 @babel/plugin-syntax-dynamic-import,因此我们不需要再单独安装和配置。
构建结果
webpack 遇到 import(****) 这样的语法的时候,会这样处理:
- 以**** 为入口新生成一个 Chunk
- 当代码执行到 import 所在的语句时,才会加载该 Chunk 所对应的文件(如这里的1.bundle.8bf4dc.js)
热更新
- 首先配置 devServer 的 hot 为 true
2. 并且在 plugins 中增加
new webpack.HotModuleReplacementPlugin()
new webpack.HotModuleReplacementPlugin()
3. 在入口文件中新增
此时,再修改代码,不会造成整个页面的刷新
多页应用打包
配置
目录
配置chunks 参数,指定html引入的js文件
查看 index.html 和 login.html 会发现,都同时引入了 index.f7d21a.js 和 login.f7d21a.js,通常这不是我们想要的,
我们希望,index.html 中只引入 index.f7d21a.js,login.html 只引入 login.f7d21a.js
HtmlWebpackPlugin 提供了一个 chunks 的参数,可以接受一个数组,配置此参数仅会将数组中指定的js引入到html文件中,
此外,如果你需要引入多个JS文件,仅有少数不想引入,还可以指定 excludeChunks 参数,它接受一个数组
我们希望,index.html 中只引入 index.f7d21a.js,login.html 只引入 login.f7d21a.js
HtmlWebpackPlugin 提供了一个 chunks 的参数,可以接受一个数组,配置此参数仅会将数组中指定的js引入到html文件中,
此外,如果你需要引入多个JS文件,仅有少数不想引入,还可以指定 excludeChunks 参数,它接受一个数组
resolve 配置
resolve 配置 webpack 如何寻找模块所对应的文件。
webpack 内置 JavaScript 模块化语法解析功能,默认会采用模块化标准里约定好的规则去寻找,
但你可以根据自己的需要修改默认的规则。
webpack 内置 JavaScript 模块化语法解析功能,默认会采用模块化标准里约定好的规则去寻找,
但你可以根据自己的需要修改默认的规则。
配置项
modules
resolve.modules 配置 webpack 去哪些目录下寻找第三方模块,默认情况下,只会去 node_modules 下寻找,
如果你我们项目中某个文件夹下的模块经常被导入,不希望写很长的路径,那么就可以通过配置 resolve.modules 来简化
如果你我们项目中某个文件夹下的模块经常被导入,不希望写很长的路径,那么就可以通过配置 resolve.modules 来简化
这样配置之后,我们 import Dialog from 'dialog',会去寻找 ./src/components/dialog,不再需要使用相对路径导入。
如果在 ./src/components 下找不到的话,就会到 node_modules 下寻找
如果在 ./src/components 下找不到的话,就会到 node_modules 下寻找
alias
resolve.alias 配置项通过别名把原导入路径映射成一个新的导入路径
extensions
用途
适配多端的项目
缺省文件后缀
在导入语句没带文件后缀时,会自动带上extensions 中配置的后缀后,去尝试访问文件是否存在,
因此要将高频的后缀放在前面,并且数组不要太长,减少尝试次数。
如果没有配置 extensions,默认只会找对对应的js文件
因此要将高频的后缀放在前面,并且数组不要太长,减少尝试次数。
如果没有配置 extensions,默认只会找对对应的js文件
enforceExtension
如果配置了 resolve.enforceExtension 为 true,那么导入语句不能缺省文件后缀。
mainFields
有一些第三方模块会提供多份代码,
引入模块时,去找哪个文件以及查找的顺序
引入模块时,去找哪个文件以及查找的顺序
区分不同的环境
- webpack.base.js 定义公共的配置
- webpack.dev.js:定义开发环境的配置
- webpack.prod.js:定义生产环境的配置
webpack-merge 专为 webpack 设计,提供了一个 merge 函数,用于连接数组,合并对象
定义环境变量
使用 webpack 内置插件 DefinePlugin 来定义环境变量
配置
使用
利用webpack解决跨域问题
假设前端在3000端口,服务端在4000端口,我们通过 webpack 配置的方式去实现跨域
配置代理
优化
量化
speed-measure-webpack-plugin
speed-measure-webpack-plugin 插件可以测量各个插件和loader所花费的时间
1. exclude/include
确保转译尽可能少的文件。
顾名思义,exclude 指定要排除的文件,include 指定要包含的文件
顾名思义,exclude 指定要排除的文件,include 指定要包含的文件
2. cache-loader
在一些性能开销较大的 loader 之前添加 cache-loader,将结果缓存中磁盘中
只打算给 babel-loader 配置 cache 的话,
也可以不使用 cache-loader,
给 babel-loader 增加选项 cacheDirectory
也可以不使用 cache-loader,
给 babel-loader 增加选项 cacheDirectory
cacheDirectory:默认值为 false。
当有设置时,指定的目录将用来缓存 loader 的执行结果。
之后的 Webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 重新编译过程。
设置空值或者 true 的话,使用默认缓存目录:node_modules/.cache/babel-loader
当有设置时,指定的目录将用来缓存 loader 的执行结果。
之后的 Webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 重新编译过程。
设置空值或者 true 的话,使用默认缓存目录:node_modules/.cache/babel-loader
3. happypack
把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程
当你的项目不是很复杂时,不需要配置 happypack,
因为进程的分配和管理也需要时间,并不能有效提升构建速度,甚至会变慢
因为进程的分配和管理也需要时间,并不能有效提升构建速度,甚至会变慢
4.thread-loader
除了使用 Happypack 外,我们也可以使用 thread-loader ,
把 thread-loader 放置在其它 loader 之前
把 thread-loader 放置在其它 loader 之前
thread-loader 和 Happypack 我对比了一下,构建时间基本没什么差别。
不过 thread-loader 配置起来为简单
不过 thread-loader 配置起来为简单
5.开启 JS 多进程压缩
虽然很多 webpack 优化的文章上会提及多进程压缩的优化,
不管是 webpack-parallel-uglify-plugin 或者是 uglifyjs-webpack-plugin 配置 parallel。
不过这里我要说一句,没必要单独安装这些插件,它们并不会让你的 Webpack 构建速度提升
不管是 webpack-parallel-uglify-plugin 或者是 uglifyjs-webpack-plugin 配置 parallel。
不过这里我要说一句,没必要单独安装这些插件,它们并不会让你的 Webpack 构建速度提升
当前 Webpack 默认使用的是 TerserWebpackPlugin,默认就开启了多进程和缓存
6. HardSourceWebpackPlugin
HardSourceWebpackPlugin 为模块提供中间缓存,
缓存默认的存放路径是: node_modules/.cache/hard-source
缓存默认的存放路径是: node_modules/.cache/hard-source
首次构建时间变化不大,第二次开始,构建时间降低效果很明显
7. noParse
如果一些第三方模块没有AMD/CommonJS规范版本,可以使用 noParse 来标识这个模块,
这样 Webpack 会引入这些模块,但是不进行转化和解析,
从而提升 Webpack 的构建性能 ,例如:jquery 、lodash
这样 Webpack 会引入这些模块,但是不进行转化和解析,
从而提升 Webpack 的构建性能 ,例如:jquery 、lodash
如果你使用到了不需要解析的第三方依赖,
那么配置 noParse 很显然是一定会起到优化作用的
那么配置 noParse 很显然是一定会起到优化作用的
8. resolve
resolve 配置 webpack 如何寻找模块所对应的文件
resolve.moudles
resolve.extensions 配置,默认是 ['.js', '.json']
将频率最高的后缀放在第一位,
并且控制列表的长度,以减少尝试次数
并且控制列表的长度,以减少尝试次数
项目较小时,优化效果不明显
9. IgnorePlugin
webpack 的内置插件,作用是忽略第三方包指定目录
减小包体积
10. externals
可以将一些JS文件存储在 CDN 上(减少 Webpack打包出来的 js 体积),
在 index.html 中通过 <script> 标签引入
在 index.html 中通过 <script> 标签引入
希望在使用时,仍然可以通过 import 的方式去引用(如 import $ from 'jquery'),
并且希望 webpack 不会对其进行打包,此时就可以配置 externals
并且希望 webpack 不会对其进行打包,此时就可以配置 externals
11. DllPlugin
有些时候,如果所有的JS文件都打成一个JS文件,会导致最终生成的JS文件很大,
这个时候,我们就要考虑拆分 bundles
这个时候,我们就要考虑拆分 bundles
DllPlugin 和 DLLReferencePlugin 可以实现拆分 bundles,
并且可以大大提升构建速度,
DllPlugin 和 DLLReferencePlugin 都是 webpack 的内置模块
并且可以大大提升构建速度,
DllPlugin 和 DLLReferencePlugin 都是 webpack 的内置模块
使用 DllPlugin 将不会频繁更新的库进行编译,
当这些依赖的版本没有变化时,就不需要重新编译
当这些依赖的版本没有变化时,就不需要重新编译
配置
在一个新建的配置文件中,
把第三方库单独打包成一个动态链接库
把第三方库单独打包成一个动态链接库
打包后,生成动态链接库
dist
└── dll
├── manifest.json
└── react.dll.9dcd9d.js
└── dll
├── manifest.json
└── react.dll.9dcd9d.js
manifest.json 用于让 DLLReferencePlugin 映射到相关依赖上
在主配置文件中
在html文件中引入dll文件
<script src="/dll/react.dll.9dcd9d.js"></script>
效果
构建速度
构建时间缩短,
尤其是modules with no loaders,这项构建时间缩短较为明显
尤其是modules with no loaders,这项构建时间缩短较为明显
体积
打包后的bundle.js体积减少
12. optimization.splitChunks
抽离公共代码
抽离公共代码
抽离公共代码是对于多页应用来说的,
如果多个页面引入了一些公共模块,那么可以把这些公共的模块抽离出来,单独打包。
公共代码只需要下载一次就缓存起来了,避免了重复下载
如果多个页面引入了一些公共模块,那么可以把这些公共的模块抽离出来,单独打包。
公共代码只需要下载一次就缓存起来了,避免了重复下载
抽离公共代码对于单页应用和多页应该在配置上没有什么区别,
都是配置在 optimization.splitChunks 中
都是配置在 optimization.splitChunks 中
即使是单页应用,同样可以使用这个配置,
例如,打包出来的 bundle.js 体积过大,
我们可以将一些依赖打包成动态链接库,然后将剩下的第三方依赖拆出来。
这样可以有效减小 bundle.js 的体积大小
当然,你还可以继续提取业务代码的公共模块
例如,打包出来的 bundle.js 体积过大,
我们可以将一些依赖打包成动态链接库,然后将剩下的第三方依赖拆出来。
这样可以有效减小 bundle.js 的体积大小
当然,你还可以继续提取业务代码的公共模块
效果
配置DllPlugin 和 SplitChunks 后,General output time有所减少(不包含dll打包时间)
runtimeChunk
runtimeChunk 的作用是将包含 chunk 映射关系的列表从 main.js 中抽离出来,
在配置了 splitChunk 时,记得配置 runtimeChunk.
在配置了 splitChunk 时,记得配置 runtimeChunk.
optimization: {
runtimeChunk: {
name: 'manifest'
}
}
runtimeChunk: {
name: 'manifest'
}
}
终构建出来的文件中会生成一个 manifest.js
借助 webpack-bundle-analyzer 进一步优化
在做 webpack 构建优化的时候,vendor 打出来超过了1M,react 和 react-dom 已经打包成了DLL。
因此需要借助 webpack-bundle-analyzer 查看一下是哪些包的体积较大
因此需要借助 webpack-bundle-analyzer 查看一下是哪些包的体积较大
配置
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
。。。
plugins: [
//...
new BundleAnalyzerPlugin(),
]
。。。
plugins: [
//...
new BundleAnalyzerPlugin(),
]
npm run build 构建,
会默认打开: http://127.0.0.1:8888/,
可以看到各个包的体积
会默认打开: http://127.0.0.1:8888/,
可以看到各个包的体积
打包后的 vendor 太大了
使用 splitChunks 进行拆分
13.webpack自身的优化
tree-shaking
如果使用ES6的import 语法,
那么在生产环境下,会自动移除没有使用到的代码
那么在生产环境下,会自动移除没有使用到的代码
scope hosting 作用域提升
变量提升,可以减少一些变量声明。
在生产环境下,默认开启
在生产环境下,默认开启
babel 配置的优化
0 条评论
下一页
为你推荐
查看更多