一、webpack 性能优化之 PWA
-
创建空文件夹,通过
npm init
命令初始化package.json
文件,通过npm install webpack webpack-cli -g
命令全局下载webpack
和webpack-cli
,通过npm install webpack webpack-cli -D
命令本地下载webpack
和webpack-cli
,通过npm i html-webpack-plugin mini-css-extract-plugin optimize-css-assets-webpack-plugin -D
命令下载html-webpack-plugin
、mini-css-extract-plugin
和optimize-css-assets-webpack-plugin
,通过npm i eslint-loader eslint -D
命令下载eslint-loader
和eslint
,通过npm i eslint-config-airbnb-base eslint-plugin-import eslint -D
命令下载eslint-config-airbnb-base
、eslint-plugin-import
和eslint
,eslint-config-airbnb-base
依赖于eslint-plugin-import
和eslint
,通过npm i postcss-loeader postcss-preset-env -D
命令下载postcss-loeader
和postcss-preset-env
,通过npm i css-loader less-loader -D
命令下载css-loader
和less-loader
,通过npm i url-loader file-loader html-loader -D
命令下载url-loader
、file-loader
和html-loader
,通过npm i babel-loader @babel/core @babel/preset-env -D
命令下载babel-loader
、@babel/core
和@babel/preset-env
,通过npm i @babel/polyfill core-js -D
命令下载@babel/polyfill
和core-js
,通过npm i workbox-webpack-plugin -D
命令下载workbox-webpack-plugin
。 -
在里面创建
src
文件夹,里面创建css
和js
文件夹,index.html
。在css
中创建index.css
,在js
中创建index.js
,代码如下所示:
- index.js
import '../css/index.css'; import { mul } from './test.js' function sum(...args) { return args.reduce((p, c) => p + c, 0); } // eslint-disable-next-line console.log(mul(2, 3)); // eslint-disable-next-line console.log(sum(1, 2, 3, 4)); if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker .register('/service-worker.js') .then(() => { console.log('sw注册成功了~'); }) .catch(() => { console.log('sw注册失败了~'); }); }); }
- test.js
export function mul(x, y) { return x * y; } export function count(x, y) { return x - y; }
- index.css
html, body { margin: 0; padding: 0; height: 100%; background-color: deeppink; }
- index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>webpack</title> </head> <body> <h1>hello cache</h1> </body> </html>
- 创建
webpack.config.js
文件,通过require
引入path
、html-webpack-plugin
、mini-css-extract-plugin
、optimize-css-assets-webpack-plugin
和workbox-webpack-plugin
。通过process.env.NODE_ENV
定义nodejs
环境变量:决定使用browserslist
的哪个环境。定义一个复用loader
,在下面匹配.css
和.less
文件的时候,需要重复使用。通过MiniCssExtractPlugin.loader
提取css
单独文件,使用css-loader
,使用postcss-loader
做css
的兼容性处理,同时使用postcss-preset-env
做css
的按需加载的兼容性处理,options
是postcss-loader
的配置项,ident
是固定写法,plugins
是使用postcss-preset-env
这个插件。entry
是入口文件,output
是出口文件,filename
是输出的文件名,path
是文件的输出路径。module
是loader
的配置,rules
是详细的loader
配置。在rules
中使用oneOf
,以下的loader
只会匹配一个,不能有两个配置处理同一种类型文件,如果有需要提取出来。第一个规则是匹配以.css
结尾的文件,通过use
使用commonCssLoader
这个抽出复用的loader
。第二个规则是匹配以.less
结尾的文件,通过use
使用commonCssLoader
这个抽出复用的loader
和less-loader
。第三个规则是匹配以.js
结尾的文件,通过exclude
不包括node_modules
,通过enforce
优先执行,通过loader
使用eslint-loader
,通过options
设置自动修复eslint
的错误。第四个规则是匹配以.js
结尾的文件,通过exclude
不包括node_modules
,通过loader
使用babel-loader
,通过options
进行配置,presets
为预设,指示babel
做怎么样的兼容性处理,使用babel/preset-env
这个预设,cacheDirectory
是开启babel
缓存,第二次构建时,会读取之前的缓存。通过useBuiltIns
按需加载,通过corejs
指定core-js
版本,通过targets
指定兼容性做到哪个版本浏览器。js
兼容性处理需要使用到babel-loader
、@babel/core
和@babel/preset-env
。如果是基本js
兼容性处理,需要使用@babel/preset-env
,但是只能转换基本语法,如promise
高级语法不能转换。如果使用全部js
兼容性处理,需要使用@babel/polyfill
,但是只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了,所有就需要做兼容性处理的就做做兼容性处理的就做,使用core-js
。第五个规则是匹配以.jpg
、png
和gif
格式的图片资源,通过loader
使用url-loader
,url-loader
依赖于file-loader
。进行options
配置,设置limit
为8 * 1024
,图片大小小于8kb
,就会被base64
处理,给图片做优化。这样做的优点是减少请求数量(减轻服务器压力),缺点是图片体积会更大(文件请求速度更慢)。设置esModule
为false
,由于url-loader
默认使用es6
模块化解析,而html-loader
引入图片是commonjs
,解析时会出问题为[object Module]
,所以关闭url-loader
的es6
模块化,使用commonjs
解析。设置name
为[hash:10].[ext]
,给图片进行重命名,因为图片值为hash
,[hash:10]
取图片的hash
的前10
位,[ext]
取文件原来扩展名。第三个规则是匹配以.html
结尾的文件,使用loader
为html-loader
,处理html
文件的img
图片(负责引入img
,从而能被url-loader
进行处理)。第六个规则是匹配以.html
结尾的文件,通过loader
使用html-loader
。第七个规则是匹配其它资源,通过loader
使用file-loader
,通过options
进行输出路径配置,输出文件夹为media
。plugins
里面是一些插件配置,通过MiniCssExtractPlugin
插件提取css
以及对输出的css
文件进行重命名,通过OptimizeCssAssetsWebpackPlugin
对css
文件进行压缩,通过HtmlWebpackPlugin
复制里面的文件,并自动引入打包输出的所有资源(JS/CSS
),进行minify
配置,通过collapseWhitespace
移除空格,通过removeComments
移除注释,压缩html
文件。通过WorkboxWebpackPlugin
插件的GenerateSW
生成一个serviceworker
配置文件,通过设置clientsClaim
为true
,帮助serviceworker
快速启动,通过设置skipWaiting
为true
,删除旧的serviceworker
。设置mode
为production
,在生产环境下webpack
会自动压缩js
代码,设置devtool
为source-map
,提供源代码到构建后代码映射技术,代码如下所示:
const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
process.env.NODE_ENV = 'production'
const commonCssLoader = [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
require('postcss-preset-env')()
]
}
}
]
module.exports = {
entry: 'src/js/index.js',
output: {
filename: 'js/built.[contenthash:10].js',
path: resolve(__dirname, 'build')
},
module: {
rules:[
{
test: /\.js$/,
exclude: /node_modules/,
enforce: 'pre',
loader: 'eslint-loader',
options: {
fix: true
}
},
{
oneOf: [
{
test: /\.css$/,
use: [...commonCssLoader]
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader']
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: {
version: 3
},
targets: {
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17'
}
}
],
cacheDirectory: true
}
},
{
test: /\.(jpg|png|gif)/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:].[ext]',
outputPath: 'imgs',
esModule: false
}
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
test: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media'
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/built.[contenthash:10].css'
}),
new OptimizeCssAssetsWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
}),
new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true,
skipWaiting: true
})
],
mode: 'production',
devtool: 'source-map'
}
PWA
是渐进式网络开发应用程序,离线可访问,需要用到workbox-webpack-plugin
这个插件。通过WorkboxWebpackPlugin.GenerateSW
生成配置文件,在js
中注册serviceWorker
,处理兼容性问题。这样当在有网的情况下就会生成serviceWorker
配置文件,当在没网的情况下,就会访问这个serviceWorker
,实现离线访问。代码必须运行在服务器上,为了解决eslint
不认识window
、navigator
全局变量,需要修改package.json
中eslintConfig
配置,代码如下所示:
"eslintConfig": {
"extends": "airbnb-base",
"env": {
"browser": true
}
}
- 在命令行输入
webpack
命令,资源就会进行打包,以及离线实现离线访问的功能。
二、webpack 性能优化之多进程打包
- 在上面的
PWA
项目的基础上,进行修改。通过npm i thread-loader -D
命令下载thread-loader
。在需要使用到多进程打包的地方,进行使用thread-loader
,在babel-loader
中使用多进程打包,通过options
设置workers
,进程个数,代码如下所示:
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'thread-loader',
options: {
workers: 2
}
},
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: {
version: 3
},
targets: {
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17'
}
}
],
cacheDirectory: true
}
}
],
}
- 开启多进程打包需要使用
thread-loader
,程启动大概为600ms
,进程通信也有开销,只有工作消耗时间比较长,才需要多进程打包。
三、webpack 性能优化之 externals
-
创建空文件夹,通过
npm init
命令初始化package.json
文件,通过npm install webpack webpack-cli -g
命令全局下载webpack
和webpack-cli
,通过npm install webpack webpack-cli -D
命令本地下载webpack
和webpack-cli
,通过npm i html-webpack-plugin -D
命令下载html-webpack-plugin
插件。 -
在项目文件中,创建
src
文件夹,里面创建index.js
和index.html
文件,代码如下所示:
- index.js
import $ from 'jquery'; console.log($);
- index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=q, initial-scale=1.0"> <title>webpack</title> </head> <body> <h1 id="title">hello webpack</h1> <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script> </body> </html>
- 在主目录,创建
webpack.config.js
文件,通过require
引入path
和html-webpack-plugin
。entry
是入口文件,output
是出口文件,filename
是打包输出后的文件名,path
是输出路径。module
是loader
的配置,rules
是详细的loader
配置。plugins
里面是plugins
的配置。html-webpack-plugin
是默认会创建一个空的HTML
,自动引入打包输出的所有资源(JS/CSS
)。如果只使用HtmlWebpackPlugin()
,那么就会在build
中自动生成一个index.html
,是无结构的html
文件。如果需要有结构的HTML
文件,那么就需要配置HtmlWebpackPlugin
的template
,复制里面的文件,并自动引入打包输出的所有资源(JS/CSS
)。mode
是设置模式,production
是开发模式。通过externals
忽略库名和npm
包名等,比如拒绝jQuery
被打包进来,代码如下所示:
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname, 'build')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'production',
externals: {
jquery: 'jQuery'
}
}
-
externals
可以忽略打包的包,比如在构建项目的时候,将第三方的包进行移除打包,可以极大的提示打包的构建速度。externals
是更加适用于CDN
打包。 -
在命令行输入
webpack
命令,html
资源就会进行打包,同时在build
文件夹中,多出一个built.js
的打包输出文件。
四、webpack 性能优化之 dll
- 在上面的
externals
项目的基础上,进行修改。新建webpack.dll.js
文件,使用dll
技术,对某些库(第三方库:jquery、react、vue...
)进行单独打包。引入path
和webpack
,entry
是入口文件,要打包的库是jquery
。output
是出口文件,filename
是输出的文件名,path
是输出路径,library
是打包的库里面向外暴露出去的内容叫什么名字。通过entry
和output
,将jQuery
打包进来并且输出出去。在plugins
中放的是插件,通过webpack.DllPlugin
包生成一个manifest.json
,提供和jquery
映射,name
是映射库的暴露的内容名称,path
是输出文件路径。通过plugins
生成manifest.json
, 建立与jquery
之间的映射关系。设置mode
为production
,代码如下所示:
const { resolve } = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
jquery: ['jquery']
},
output: {
filename: '[name].js',
path: resolve(__dirname, 'dll'),
library: '[name]_[hash]'
},
plugins: [
new webpack.DllPlugin({
name: '[name]_[hash]',
path: resolve(__dirname, 'dll/manifest.json')
})
],
mode: 'production'
}
- 修改
webpack.config.js
文件,通过npm i add-asset-html-webpack-plugin -D
命令下载add-asset-html-webpack-plugin
,并且通过require
引入 到里面。在plugins
中,通过webpack.DllReferencePlugin
告诉webpack
哪些库不参与打包,同时使用时的名称也得变, 通过AddAssetHtmlWebpackPlugin
将某个文件打包输出去,并在html
中自动引入该资源,代码如下所示:
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname, 'build')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new webpack.DllReferencePlugin({
manifest: resolve(__dirname, 'dll/manifest.json')
}),
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname, 'dll/jquery.js')
})
],
mode: 'production'
}
- 在命令行输入
webpack
命令,html
资源就会进行打包,同时会多出dll
文件目录,里面有jquery.js
和manifest.json
文件。