开发环境性能优化
- 优化打包构建速度
- HMR
- 优化代码调试
- source-map
生产环境性能优化
- 优化打包构建速度
- oneOf
- babel缓存
- 多进程打包
- externals
- dll
- 优化代码运行的性能
- 缓存(hash-chunkhash-contenthash)
- tree shaking
- code split
- 懒加载/预加载
- pwa
/**
* 开发环境:
* 1. 优化打包构建速度
* 性能优化1:
* HMR:hot module replacement 热模块替换 / 模块热替换
* 作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块),极大提升构建速度
* 方式:在devServer配置hot: true
* 样式文件:可以使用HMR功能,因为style-loader内部实现了
* js文件:默认不能使用HMR功能
* html文件:默认不能使用HMR功能,同时会导致问题:html文件不能热更新(不用做HMR功能,因为它改变了都要重新刷新页面)
* 解决:修改entry入口,将html引入
*
* 2. 优化代码调式
* 性能优化2:
* devtool --> eval-source-map source-map
*
* 生产环境:
* 1. 优化打包构建速度
* 性能优化1:rules中增加oneOf,以下模块只匹配一个,不用全部去匹配
* 性能优化2:开启babel缓存 cacheDirectory: true (更新js时不用每个模块都是编译) --> 让第二次打包构建速度更快
* 性能优化3:多进程打包
性能优化4:externals忽略jquery打包,但是要使用CDN引入 ---> 不需要打包,CDN引入
性能优化5:dll单独打包第三方库, ---> 需要打包一次,运行webpack --config webpack.dll.js,后面不需要打包
*
* 2. 优化代码运行性能:
* 性能优化:1: 文件资源缓存
* hash:每次webpack构建时会生成唯一的hash值,
* 问题:因为js和css同时使用一个hash值,如果重新打包会使缓存失效(可能我就修改了一个文件)
* 解决:chunkhash: 根据chunk生成的hash值,如果打包来源于同一个chunk, 那么hash值就一样
* 问题: js和css的hash值还是一样的
因为css是在js中被引入的,所以同属于一个chunk
contenthash: 根据文件的内容生成hash值。不同文件hash值一定不一样
--> 让代码上线运行缓存更好使用
性能优化2:tree shaking: 去除无用代码
前提:1、必须使用ES6模块化 2、开启production环境
作用: 减少代码体积
在package.json中配置
"sideEffects": false 所有代码都没有副作用(都可以进行tree shaking)
问题:可能会把css / @babel/polyfill (副作用)文件干掉
"sideEffects": ["*.css", "*.less"]
性能优化3:optimization,将node_modules中代码单独打包一个chunk最终输出
性能优化4:文件懒加载,把文件放在异步回调函数中
性能优化5:pwa离线文件加载 service-worker
*/
// resolve用来拼接绝对路径的方法
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
const webpack = require('webpack')
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
// 设置 nodejs 环境变量 ,设置为开发环境。postcss-loader默认是生产环境
// process.env.NODE_ENV = 'development'
// css-loader代码复用
const commonCssLoader = [
// 创建style标签,将js中的样式资源插入进去,添加到head中生效
// 'style-loader',
// MiniCssExtractPlugin.loader,
// 配置MiniCssExtractPlugin的options
{
loader: MiniCssExtractPlugin.loader,
options: { // 解决打包后img路径不对问题
publicPath: '../'
}
},
// 将css文件变成commonjs模块加载到js中,里面的内容是样式字符串
'css-loader',
/**
* css兼容性处理:postcss --> postcss-loader postcss-preset-env
* 帮postcss找到package.json中browerslist里面的配置,通过配置加载指定的css兼容性样式
*
*/
// 使用loader的默认配置
// 'postcss-loader',
// 修改loader配置
// 将css做兼容性处理
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
// postcss的插件
require('postcss-preset-env')()
]
}
}
]
module.exports = {
// 入口起点
// entry: ['./src/js/index.js', './src/index.html'],
// 单入口,最终输出只有一个bundle
entry: './src/js/index.js',
// 多入口,会打包成两个js文件
// entry: {
// index: './src/js/index.js',
// print: './src/js/print.js'
// },
// 输出
output: {
// [name]:输出文件名
filename: 'js/[name].[contenthash:10].js',
// 输出路径
// __dirname node.js变量,代表当前文件的目录的绝对路径
path: resolve(__dirname, 'build')
},
// loader的配置
module: {
rules: [
/* 性能优化:提取到外层,解决oneOf问题
语法检查: eslint-loader eslint
注意:只检查自己写的源代码,第三方的库是不用检查的
设置检查规则:
package.json 中 eslintConfig 中设置~
"eslintConfig": {
"extends": "airbnb-base"
}
airbnb --> eslint-config-airbnb-base eslint-plugin-import eslint
*/
{
test: /\.js$/,
// 排除node_modules,不进行检查
exclude: /node_modules/,
// 优先执行(当一个文件被多个loader执行,那么一定要指定loader执行顺序,先执行eslint-loader,再执行babel-loader)
enforce: 'pre',
loader: 'eslint-loader',
options: {
// 自动修复 eslint 的错误
fix: true
}
},
{
// 以下loader只匹配一个
// 注意:不能有两个配置处理同一个文件,否则第一个匹配上了,就不会向下匹配,第二个会失效,
// 解决办法:提取到外层
oneOf: [
{
// 匹配哪些文件
test: /\.css$/,
// 使用哪里loader进行处理
// use数组中loader执行顺序: 从右到做,从下到上依次执行
use: [...commonCssLoader]
},
{
// 匹配哪些文件
test: /\.less$/,
// 使用哪里loader进行处理
// use数组中loader执行顺序: 从右到做,从下到上依次执行
// 使用多个loader就使用user,使用一个则使用loader
use: [...commonCssLoader, 'less-loader']
},
{
// 处理图片资源
test: /\.(jpg|png|gif)$/,
// 下载url-loader file-loader
loader: 'url-loader',
options: {
// 图片大小小于8kb,就会被base64处理
// 优点:减少请求数量(减轻服务器压力)
// 缺点:图片体积会更大(文件请求速度更慢)
limit: 8 * 1024,
// 问题:因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
// 解析出问题:[object Module]
// 解决:关闭url-loader的es6模块化,使用commonjs解析
esModule: false,
// 给图片进行重命名
// [hash:10]取图片的hash的前10位
// [ext] 取文件原扩展名
name: '[hash:10].[ext]',
outputPath: 'imgs'
}
},
{
test: /\.html$/,
// 处理html中的img图片,负责引入img,从而被url-loader进行处理
loader: 'html-loader'
},
{
// 打包其他资源(除去html/js/css/less资源以外的资源)
exclude: /\.(css|less|js|json|html|jpg|png|gif)$/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]',
outputPath: 'media'
}
},
/**
* js兼容性处理:
* 1、基本的js兼容性处理 --> @babel/preset-env
* 问题:只能转换基本的语法,如promise高级语法不能转换
* 2、全部js兼容性处理 --> @babel/polyfill
* 问题:我们只要解决部分兼容性问题,但是所有兼容性代码都引入,体积太大
* 3、按需加载 --> core-js
*/
{
test: /\.js$/,
// 排除node_modules,不进行检查
exclude: /node_modules/,
use: [
/*
开启多进程打包。
进程启动大概为600ms,进程通信也有开销。
只有工作消耗时间比较长,才需要多进程打包
*/
{
loader: 'thread-loader',
options: {
workers: 2 // 进程2个
}
},
{
loader: 'babel-loader',
options: {
// 预设:指示 babel 做怎么样的兼容性处理
presets: [
[
'@babel/preset-env',
{
// 按需加载
useBuiltIns: 'usage',
// 指定 core-js 版本
corejs: {
version: 3
},
// 指定兼容性做到哪个版本浏览器
targets: {
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17'
}
}
]
],
// 开启babel缓存
// 第二次构建时,会读取缓存,不用没有js模块都去编译
cacheDirectory: true
}
}
]
}
]
}
]
},
// plugins的配置
plugins: [
// 默认创建一个空的HTML,自动引入打包输出的所有资源(JS/CSS)
// 需求:需要有结构的HTML
new HtmlWebpackPlugin({
// 复制 ./src/index.html 文件,并自动引入打包输出所有资源
template: './src/index.html',
// 压缩 html 代码
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
}),
// 从js中分离css
new MiniCssExtractPlugin({
// 给css重命名
filename: 'css/built.[contenthash:10].css'
}),
// 压缩css
new OptimizeCssAssetsWebpackPlugin(),
new WorkboxWebpackPlugin.GenerateSW({
/*
1. 帮助serviceworker快速启动
2. 删除旧的 serviceworker
生成一个 serviceworker 配置文件~
*/
clientsClaim: true,
skipWaiting: true
}),
// 告诉webpack哪些库不参与打包,同时使用时的名称也得变~
new webpack.DllReferencePlugin({
manifest: resolve(__dirname, 'dll/manifest.json')
}),
// 将某个文件打包输出去,并在html中自动引入该资源
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname, 'dll/jquery.js')
}),
// 打包后先清除build中文件
new CleanWebpackPlugin()
],
/*
1. 可以将node_modules中代码单独打包一个chunk最终输出
2. 自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk
*/
optimization: {
splitChunks: {
chunks: 'all'
}
},
// 使用CDN引入,拒绝打包
externals: {
// 拒绝jQuery被打包进来
jquery: 'jQuery'
},
// mode的配置
mode: 'development', // 开发模式
// mode: 'production' // 生产模式,会自动压缩js代码
// 开发服务器devServer:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器)
// 特点:只会在内存中编译打包,不会有任何输出
// 启动devServer指令为:npx webpack-dev-server
devServer: {
// 项目构建后路径
contentBase: resolve(__dirname, 'build'),
// 启动gzip压缩
compress: true,
// 端口号
port: 3000,
// 自动打开浏览器
open: true,
// 开启HMR功能
// 当修改了webpack配置,新配置想生效,必须重启webpack
hot: true
},
devtool: 'eval-source-map'
}
/*
source-map: 一种 提供源代码到构建后代码映射 技术 (如果构建后代码出错了,通过映射可以追踪源代码错误)
[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
source-map:外部
错误代码准确信息 和 源代码的错误位置
inline-source-map:内联
只生成一个内联source-map
错误代码准确信息 和 源代码的错误位置
hidden-source-map:外部
错误代码错误原因,但是没有错误位置
不能追踪源代码错误,只能提示到构建后代码的错误位置
eval-source-map:内联
每一个文件都生成对应的source-map,都在eval
错误代码准确信息 和 源代码的错误位置
nosources-source-map:外部
错误代码准确信息, 但是没有任何源代码信息
cheap-source-map:外部
错误代码准确信息 和 源代码的错误位置
只能精确的行
cheap-module-source-map:外部
错误代码准确信息 和 源代码的错误位置
module会将loader的source map加入
内联 和 外部的区别:1. 外部生成了文件,内联没有 2. 内联构建速度更快
开发环境:速度快,调试更友好
速度快(eval>inline>cheap>...)
eval-cheap-souce-map
eval-source-map
调试更友好
souce-map
cheap-module-souce-map
cheap-souce-map
推荐 --> eval-source-map / eval-cheap-module-souce-map
生产环境:源代码要不要隐藏? 调试要不要更友好
内联会让代码体积变大,所以在生产环境不用内联
nosources-source-map 全部隐藏
hidden-source-map 只隐藏源代码,会提示构建后代码错误信息
推荐 --> source-map / cheap-module-souce-map
*/