Loaders
1、文件Loader
url-loader
像 file loader
一样工作,可以返回 data URL
{
test: /.(jpg|png|gif|jpeg)$/,
loader: 'url-loader',
options:{
name: 'img/[name]-[hash:8].[ext]',
outputPath: 'img',
esModule: false
}
}
file-loader
将文件发送到输出文件夹,并返回相对URL
{
exclude: /.(html|js|jpg|css|less)$/,
loader: 'file-loader',
options: {
name: '[hash:8].[ext]',
outputPath: 'media'
}
}
2、转换编译(Transpiling)
Name | Description |
---|---|
script-loader |
在全局上下文中执行一次 JavaScript 文件(如在 script 标签),不需要解析 |
babel-loader |
加载 ES2015+ 代码,然后使用 Babel 转译为 ES5 |
ts-loader |
或 awesome-typescript-loader 像 JavaScript 一样加载 TypeScript 2.0+ |
coffee-loader |
JavaScript一样加载 CoffeeScript` |
3、模板(Templating)
Name | Description |
---|---|
html-loader |
导出 HTML 为字符串,需要引用静态资源 |
pug-loader |
加载 Pug 模板并返回一个函数 |
jade-loader |
加载 Jade 模板并返回一个函数 |
markdown-loader |
将 Markdown 转译为 HTML |
4、样式
style-loader
将模块的导出作为样式添加到DOM
中css-loader
解析CSS
文件后,使用import
加载,并且返回CSS
代码less-loader
加载和转译LESS
文件sass-loader
加载和转译SASS/SCSS
文件postcss-loader
使用PostCSS
加载和转译
yarn add postcss-loader postcss-preset-env
stylus-loader
加载和转译Stylus
文件
5、清理和测试(Linting)
eslint-loader
使用 ESLint语法检查
设置代码检查的规则:
在package.json里面添加:
eslintConfig:{
extends: 'airbns'
}
添加依赖文件
yarn add eslint-config-airbnb-base eslint-plugin-import eslint -D
webpack.config.js 文件中
{
test: /.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
fix: true //自动格式化代码
}
}
eslint-loader
使用 ESLint兼容性检查
1、 preset-env 只能检查简单的语法。
2、@babel/polyfull 能解决兼用性问题。但是全部引入体积太大了。
3、 core-js第三方按需加载的包
添加依赖文件
npm install babel-loader @babel/core @bebel/preset-env -D
webpack.config.js文件中
{
test: /.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
@babel/preset-env,
{
useBuiltIns: 'usage',//按需加载
corejs: {
versions: 3//指定版本
},
targets: {
chrome: 60 //指定浏览器的版本
}
}
]
}
}
Plugins
1、HtmlWebpackPlugin
简单创建 HTML 文件
plugins: [
new HtmlWebpackPlugin({
title: '',
filename: '',
template: './src/index.html',
publicPath: '',
favicon: '',
neta: {}
})
]
压缩HTML文件
plugins:[
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
})
]
2、MinCssExtractPlugin
单独提取css文件
plugins: [
new MinCssExtractPlugin({
filename: 'css/style.css',
chunkFilename: ''
})
]
配合loader
,postcss-loader
做浏览器的兼容性。添加了postcss-loader
后,要在package.json
里面配置browserslist:{development: {}, production: {}}
对象。
{
test: /.css$/,
use: [
//style-loader
MinCssExtractPlugin.laoder,
css-loader,
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => require('postcss-preset-env')()
}
}
]
}
3、OptimizeCssAssetsWebpackPlugin 压缩css
plugins:[
new OptimizeCssAssetsWebpackPlugin()
]
一些小的优化点
resolve.extensions
:用来表明文件后缀列表,默认查找顺序是 ['.js', '.json'],如果你的导入文件没有添加后缀就会按照这个顺序查找文件。我们应该尽可能减少后缀列表长度,然后将出现频率高的后缀排在前面。
resolve.alias
:可以通过别名的方式来映射一个路径,能让 Webpack 更快找到路径。
module.exports ={
// ...省略其他配置
resolve: {
extensions: [".js",".jsx",".json",".css"],
alias:{
"jquery":jquery
}
}
};
提高 Webpack 打包速度
1、优化Loader搜索范围(include,exclude
)
合理的使用exclude或者include的配置,来尽量减少loader被频繁执行的频率。当loader执行频率降低时,也会提升webpack的打包速度。比如:
对于Loader
来说,影响打包效率首当其冲必属 Babel 了。因为 Babel 会将代码转为字符串生成 AST,然后对 AST 继续进行转变最后再生成新的代码,项目越大,转换代码越多,效率就越低。当然了,我们是有办法优化的。
首先我们可以优化 Loader 的文件搜索范围,在使用loader时,我们可以指定哪些文件不通过loader处理,或者指定哪些文件通过loader处理。
module.exports = {
module: {
rules: [
{
// js 文件才使用 babel
test: /.js$/,
use: ['babel-loader'],
// 只处理src文件夹下面的文件
include: path.resolve('src'),
// 不处理node_modules下面的文件
exclude: /node_modules/
}
]
}
}
2、cache-loader缓存loader处理结果
缓存分为 babel缓存和文件资源缓存
Babel 缓存 cacheDirectory: true
{
cacheDirectory: true
}
文件资源的缓存
1.
hash
每次webpack构建时都会生成一个唯一的hash值 问题: 应为js和css同时使用一个hash值,如果重新打包就会导致缓存失效。
2.chunkhash
根据chunk生产的hash值,如果打包来源于同一个chunk,那么同一个文件的js和css的hash值就时一样的。
3.contenthash
根据文件的内容生成hash值,不同文件的hash值是不一样的。
module.exports = {
module: {
rules: [
{
// js 文件才使用 babel
test: /.js$/,
use: [
'cache-loader',
...loaders
],
}
]
}
}
3、使用多线程处理打包
module: {
rules: [
{
test: /.js$/,
// 把对 .js 文件的处理转交给 id 为 babel 的 HappyPack 实例
use: ['happypack/loader?id=babel'],
exclude: path.resolve(__dirname, 'node_modules'),
},
{
test: /.css$/,
// 把对 .css 文件的处理转交给 id 为 css 的 HappyPack 实例
use: ['happypack/loader?id=css']
}
]
},
plugins: [
new HappyPack({
id: 'js', //ID是标识符的意思,ID用来代理当前的happypack是用来处理一类特定的文件的
threads: 4, //你要开启多少个子进程去处理这一类型的文件
loaders: [ 'babel-loader' ]
}),
new HappyPack({
id: 'css',
threads: 2,
loaders: [ 'style-loader', 'css-loader' ]
})
]
4、DllPlugin&DllReferencePlugin 成动态链接库
DllPlugin
可以将特定的类库提前打包成动态链接库,在一个动态链接库中可以包含给其他模块调用的函数和数据,把基础模块独立出来打包到单独的动态连接库里,当需要导入的模块在动态连接库里的时候,模块不用再次被打包,而是去动态连接库里获取。这种方式可以极大的减少打包类库的次数,只有当类库更新版本才有需要重新打包,并且也实现了将公共代码抽离成单独文件的优化方案。
这里我们可以先将react、react-dom
单独打包成动态链接库,首先新建一个新的webpack
配置文件:webpack.dll.js
const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');
module.exports = {
// 想统一打包的类库
entry:['react','react-dom'],
output:{
filename: '[name].dll.js', //输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称
path:path.resolve(__dirname,'dll'), // 输出的文件都放到 dll 目录下
library: '_dll_[name]',//存放动态链接库的全局变量名称,例如对应 react 来说就是 _dll_react
},
plugins:[
new DllPlugin({
// 动态链接库的全局变量名称,需要和 output.library 中保持一致
// 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
// 例如 react.manifest.json 中就有 "name": "_dll_react"
name: '_dll_[name]',
// 描述动态链接库的 manifest.json 文件输出时的文件名称
path: path.join(__dirname, 'dll', '[name].manifest.json')
})
]
}
然后我们需要执行这个配置文件生成依赖文件:
webpack --config webpack.dll.js --mode development
接下来我们需要使用 DllReferencePlugin 将依赖文件引入项目中
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin')
module.exports = {
// ...省略其他配置
plugins: [
new DllReferencePlugin({
// manifest 就是之前打包出来的 json 文件
manifest:path.join(__dirname, 'dll', 'react.manifest.json')
})
]
}
5、noParse 可以用于配置那些模块文件的内容不需要进行解析(即无依赖)
module.noParse
属性,可以用于配置那些模块文件的内容不需要进行解析(即无依赖) 的第三方大型类库(例如jquery
,lodash
)等,使用该属性让 Webpack
不扫描该文件,以提高整体的构建速度。
module.exports = {
module: {
noParse: /jquery|lodash/, // 正则表达式
// 或者使用函数
noParse(content) {
return /jquery|lodash/.test(content)
}
}
}
6、IgnorePlugin IgnorePlugin
用于忽略某些特定的模块
IgnorePlugin用于忽略某些特定的模块,让webpack 不把这些指定的模块打包进去。
module.exports = {
// ...省略其他配置
plugins: [
new webpack.IgnorePlugin(/^./locale/,/moment$/)
]
}
webpack.IgnorePlugin()参数中第一个参数是匹配引入模块路径的正则表达式,第二个参数是匹配模块的对应上下文,即所在目录名。
7、打包文件分析工具
webpack-bundle-analyzer
插件的功能是可以生成代码分析报告,帮助提升代码质量和网站性能。
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports={
plugins: [
new BundleAnalyzerPlugin({
generateStatsFile: true, // 是否生成stats.json文件
})
// 默认配置的具体配置项
// new BundleAnalyzerPlugin({
// analyzerMode: 'server',
// analyzerHost: '127.0.0.1',
// analyzerPort: '8888',
// reportFilename: 'report.html',
// defaultSizes: 'parsed',
// openAnalyzer: true,
// generateStatsFile: false,
// statsFilename: 'stats.json',
// statsOptions: null,
// excludeAssets: null,
// logLevel: info
// })
]
}
使用方式:
"generateAnalyzFile": "webpack --profile --json > stats.json", // 生成分析文件
"analyz": "webpack-bundle-analyzer --port 8888 ./dist/stats.json" // 启动展示打包报告的http服务器
8、费时分析
speed-measure-webpack-plugin
,打包速度测量插件。这个插件可以测量webpack构建速度,可以测量打包过程中每一步所消耗的时间,然后让我们可以有针对的去优化代码。
const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin');
const smw = new SpeedMeasureWebpackPlugin();
// 用smw.wrap()包裹webpack的所有配置项
module.exports =smw.wrap({
module: {},
plugins: []
});
减少 Webpack 打包后的文件体积
1、对图片进行压缩和优化
image-webpack-loader这个loder可以帮助我们对打包后的图片进行压缩和优化,例如降低图片分辨率,压缩图片体积等。
module.exports ={
// ...省略其他配置
module: {
rules: [
{
test: /.(png|svg|jpg|gif|jpeg|ico)$/,
use: [
'file-loader',
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 65
},
optipng: {
enabled: false,
},
pngquant: {
quality: '65-90',
speed: 4
},
gifsicle: {
interlaced: false,
},
webp: {
quality: 75
}
}
}
]
}
]
}
};
2、删除无用的CSS样式
有时候一些时间久远的项目,可能会存在一些CSS样式被迭代废弃,需要将其剔除掉,此时就可以使用purgecss-webpack-plugin插件,该插件可以去除未使用的CSS,一般与 glob、glob-all 配合使用。
注意:此插件必须和CSS代码抽离插件mini-css-extract-plugin配合使用。
例如我们有样式文件style.css:
body{
background: red
}
.class1{
background: red
}
这里的.class1显然是无用的,我们可以搜索src目录下的文件,删除无用的样式。
const glob = require('glob');
const PurgecssPlugin = require('purgecss-webpack-plugin');
module.exports ={
// ...
plugins: [
// 需要配合mini-css-extract-plugin插件
new PurgecssPlugin({
paths: glob.sync(`${path.join(__dirname, 'src')}/**/*`,
{nodir: true}), // 不匹配目录,只匹配文件
})
}),
]
}
3、以CDN方式加载资源
我们知道,一般常用的类库都会发布在CDN上,因此,我们可以在项目中以CDN的方式加载资源,这样我们就不用对资源进行打包,可以大大减少打包后的文件体积。
以CDN方式加载资源需要使用到add-asset-html-cdn-webpack-plugin插件。我们以CDN方式加载jquery为例:
const AddAssetHtmlCdnPlugin = require('add-asset-html-cdn-webpack-plugin')
module.exports ={
// ...
plugins: [
new AddAssetHtmlCdnPlugin(true,{
'jquery':'https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js'
})
],
//在配置文件中标注jquery是外部的,这样打包时就不会将jquery进行打包了
externals:{
'jquery':'$'
}
}
4、开启Tree Shaking (去除无用的代码,减少代码的体积。前提条件: 1.模块必须是ES6模块,2.必须是production环境)
Tree-shaking,摇晃树。顾名思义就是当我们摇晃树的时候,树上干枯的没用的叶子就会掉下来。类比到我们的代码中就是将没用的代码摇晃下来,从而实现删除代码中未被引用的代码。
这个功能在webpack4中,当我们将mode设置为production时,会自动进行tree-shaking。
在package.json 种配置
sideEffects: false
所有的代码都可以被tree-shaking 问题是: 可能吧css文件和@babel/polyfill
等副作用全部干掉。要小心使用。
解决方法{sideEffects: ['*.css']}
5、按需加载&动态加载
必大家在开发单页面应用项目的时候,项目中都会存在十几甚至更多的路由页面。如果我们将这些页面全部打包进一个文件的话,虽然将多个请求合并了,但是同样也加载了很多并不需要的代码,耗费了更长的时间。那么为了首页能更快地呈现给用户,我们肯定是希望首页能加载的文件体积越小越好,这时候我们就可以使用按需加载,将每个路由页面单独打包为一个文件。在给单页应用做按需加载优化时,一般采用以下原则:
- 对网站功能进行划分,每一类一个chunk
- 对于首次打开页面需要的功能直接加载,尽快展示给用户,某些依赖大量代码的功能点可以按需加载
- 被分割出去的代码需要一个按需加载的时机
动态加载目前并没有原生支持,需要babel的插件:plugin-syntax-dynamic-import
。安装此插件并且在.babelrc中配置:
{
// 添加
"plugins": ["transform-vue-jsx", "transform-runtime"],
}
列如下面的实例:index.js
let btn = document.createElement('button');
btn.innerHTML = '点击加载视频';
btn.addEventListener('click',()=>{
import(/* webpackChunkName: "video" */'./video').then(res=>{
console.log(res.default);
});
});
document.body.appendChild(btn);
webpack.config.js
module.exports = {
// ...
output:{
chunkFilename:'[name].min.js'
}
}
6、这样打包后的结果最终的文件就是 video.min.js,并且刚启动项目时不会加载该文件,只有当用户点击了按钮时才会动态加载该文件。
代码分割 code-slpit
1、根据入口文件构建分割代码。
- 单入口打包,构建的就是一个文件,。
module.exports = {
enrty: './src/index.js',
output: {
filename: 'js/[name].js',
path: path.resolve(__dirname, 'dist')
}
}
2.多入口打包就会产生多个代码块
module.exports = {
enrty: {
index: './src/index.js',
home: './src/home.js'
},
output: {
filename: 'js/[name]-[hash:8].js',
path: path.resolve(__dirname, 'dist')
}
}
2、用webapck自带的 optimization
1.来吧node_modules 单独打包到一个chunk输出 2.自动分析多入口文件种有没有公共的文件,如果有会打包成一个单独的chunk 。
optimization: {
splitChunks: {
chunks: 'all'
}
}
3、用js代码让文件单独打包成一个chunk 默认webapck会按文件的id打包生成文件名称,可以用 /webpackChunkName: 'abc'/ 修改生成的文件名。
import(/*webpackChunkName: 'abc'*/'xxx.js').then(() => {
console.log('success')
}).catch(err){
console.log('error')
}