往期回顾:
从0搭建自己的webpack开发环境(一)
从0搭建自己的webpack开发环境(二)
从0搭建自己的webpack开发环境(三)
从0搭建自己的webpack开发环境(四)
前四篇文章我们已经掌握了webpack各种常见的配置,这一片文章我们来看看如何实现webpack中的优化。
我们先来编写最基本的webpack配置,然后依次实现其中的各种优化。
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = mode => {
return {
mode: mode,
entry: "./src/main.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist")
},
module: {
rules: [
{
test: /.(png|jpg|gif)$/,
use: "file-loader"
},
{
test: /.js$/,
use: "babel-loader" // .babelrc已经配置支持react
},
{
test: /.css$/,
use: [
mode !== "development"
? MiniCssExtractPlugin.loader
: "style-loader",
"css-loader"
]
}
]
},
plugins: [
new PurgecssPlugin({
paths: glob.sync(`${path.join(__dirname, "src")}*`, { nodir: true }) // 不匹配目录,只匹配文件
}),
mode !== "development" &&
new MiniCssExtractPlugin({
filename: "css/[name].css"
}),
new HtmlWebpackPlugin({
template: "./src/template.html",
filename: "index.html"
})
].filter(Boolean)
};
};
.babelrc配置文件
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
1.删除无用的Css样式
先来看看编写的代码
import './style.css'
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(hello,document.getElementById('root'));
body{
background: red
}
.class1{
background: red
}
这里的.class1显然是无用的,我们可以搜索src目录下的文件,删除无用的样式
const glob = require('glob');
const PurgecssPlugin = require('purgecss-webpack-plugin');
// 这里需要配合mini-css-extract-plugin插件
mode !== "development" && new PurgecssPlugin({
paths: glob.sync(`${path.join(__dirname, "src")}*`, { nodir: true }) // 不匹配目录,只匹配文件
}),
2.图片压缩插件
将打包后的图片进行优化
npm install image-webpack-loader --save-dev
在file-loader之前使用压缩图片插件
loader: "image-webpack-loader",
options: {
mozjpeg: {
progressive: true,
quality: 65
},
// optipng.enabled: false will disable optipng
optipng: {
enabled: false,
},
pngquant: {
quality: [0.90, 0.95],
speed: 4
},
gifsicle: {
interlaced: false,
},
// the webp option will enable WEBP
webp: {
quality: 75
}
}
3.CDN加载文件可以发现图片大小是否有了明显的变化
我们希望通过cdn的方式引入资源
const AddAssetHtmlCdnPlugin = require('add-asset-html-cdn-webpack-plugin')
new AddAssetHtmlCdnPlugin(true,{
'jquery':'https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js'
})
但是在代码中还希望引入jquery来获得提示
import $ from 'jquery'
console.log('$',$)
但是打包时依然会将jquery进行打包
externals:{
'jquery':'$'
}
在配置文件中标注jquery是外部的,这样打包时就不会将jquery进行打包了
4.Tree-shaking && Scope-Hoisting 4.1 Tree-shaking顾名思义就是将没用的内容摇晃掉,来看下代码:
main.js
import { minus } from "./calc";
console.log(minus(1,1));
calc.js
import {test} from './test';
export const sum = (a, b) => {
return a + b + 'sum';
};
export const minus = (a, b) => {
return a - b + 'minus';
};
test.js
export const test = ()=>{
console.log('hello')
}
console.log(test());
观察上述代码会发现我们主要使用minus方法,test.js代码其实是有副作用的!
默认mode:production时,会自动tree-shaking,但是打包后'hello'依然会被打印出来,这时候我们需要配置规避副作用!
在package.json中配置
"sideEffects":false,
如果这样设置,默认就不会导入css文件啦,因为我们引入css也是通过import './style.css'的;
这里重点就来了,tree-shaking主要针对es6模块,我们可以使用require语法导入css,但是这样用起来又有点格格不入,所以我们配置css文件不起副作用。
"sideEffects":[
"** './video').then(res=>{
console.log(res.default);
})
7.打包文件分析工具这样打包后的结果最终的文件就是 video.min.js
安装webpack-bundle-analyzer插件
npm install --save-dev webpack-bundle-analyzer
安装后使用插件
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
mode !== "development" && new BundleAnalyzerPlugin()
默认就会展现当前应用的分析图表
8.SplitChunks我们再来看下SplitChunks这个配置,他可以在编译时抽离第三方模块、公共模块。
先将项目配置成多入口文件
entry:{
a:'./src/a.js',
b:'./src/b.js'
}
我们让a,b两个模块同时引用jquery,这里别忘了去掉之前的externals配置
配置SplitChunks插件
默认配置在此,我们一个个描述下含义
splitChunks: {
chunks: 'async', // 分割异步模块
minSize: 30000, // 分割的文件最小大小
maxSize: 0,
minChunks: 1, // 引用次数
maxAsyncRequests: 5, // 最大异步请求数
maxInitialRequests: 3, // 最大初始化请求数
automaticNameDelimiter: '~', // 抽离的命名分隔符
automaticNameMaxLength: 30, // 名字最大长度
name: true,
cacheGroups: { // 缓存组
vendors: { // 先抽离第三方
test: /[\/]node_modules[\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20, // 优先级
reuseExistingChunk: true
}
}
}
我们将async改为initial
我们在为每个文件动态导入lodash库,并且改成async
import('lodash')
为每个入口引入c.js,并且改造配置文件
splitChunks: {
chunks: 'all',
name: true,
cacheGroups: {
vendors: {
test: /[\/]node_modules[\/]/,
priority: -10
},
default: {
minSize:1, // 不是第三方模块,被引入两次也会被抽离
minChunks: 2,
priority: -20,
}
}
}
9.热更新(不是林更新)这样再反过来看chunks的参数是不是就了然于胸了呢?
模块热替换(HMR - Hot Module Replacement)是webpack提供的最有用的功能之一。它允许在运行时替换、添加、删除各种模块,而无需进行完全刷新重新加载整个页面。
-
保留在完全重新加载页面时丢失的应用程序的状态;
-
只更新改变的内容,以节省开发时间;
-
调整样式更加快速,几乎等同于就在浏览器调试器中更改样式。
启用热更新,默认样式可以支持热更新,如果不支持热更新则可以采用强制刷新。
devServer:{
hot:true
}
new webpack.NamedModulesPlugin(),
让js支持热更新
import sum from './sum';
console.log(sum(1,2));
if(module.hot){ // 如果支持热更新
module.hot.accept(); // 当入口文件变化后重新执行当前入口文件
}
10.IgnorePlugin
配置忽略 import和require语法
new webpack.IgnorePlugin(/^./locale$/, /moment$/)
11.费时分析
可以计算每一步执行的运行速度
const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin');
const smw = new SpeedMeasureWebpackPlugin();
module.exports =smw.wrap({
...
});
12.noParse
module.noParse,对类似jq这类的依赖库,内部不会引用其他库,那么我们在打包的时候就没有必要再去解析,这样能够增加打包速率。
noParse:/jquery/
13.resolve
resolve: {
extensions: [".js",".jsx",".json",".css"],
alias:{},
modules:['node_modules']
},
14.include/exclude
在使用loader时,可以指定哪些文件不通过loader,或者指定哪些文件必须通过loader。
{
test: /.js$/,
use: "babel-loader",
// include:path.resolve(__dirname,'src'),
exclude:/node_modules/
},
15.happypack
多线程打包,我们可以将不同的逻辑交给不同的线程来处理。
npm install --save-dev happypack
使用插件
const HappyPack = require('happypack');
rules:[
{
test: /.js$/,
use: 'happypack/loader?id=jsx'
},
{
test: /.less$/,
use: 'happypack/loader?id=styles'
},
]
new HappyPack({
id: 'jsx',
threads: 4,
loaders: [ 'babel-loader' ]
}),
new HappyPack({
id: 'styles',
threads: 2,
loaders: [ 'style-loader', 'css-loader', 'less-loader' ]
})
16.总结
至此,我们五篇连载《从0搭建自己的webpack开发环境》先要告一段落了,希望大家可以通过这些文章对webpack能有全新的认识,对工作能更有帮助。



