【入口(entry)】:指示webpack应该使用哪个模块来作为构建其内部依赖图的开始。
string:"./src/index.js"
单入口,打包形成一个chunk,输出一个bundle文件,输出的文件名默认叫main.js
。
array:["./src/index.js", "./src/index.html"]
(dll就是此写法)
多入口,打包形成一个chunk,输出一个bundle文件,一般用于HTML文件的HMR功能。
object:{index: "./src/index.js", add: "./src.add.js"}
多入口,有多少个入口文件就形成几个chunk,输出多少个bundle文件,文件名称是对象中的key
特殊用法:
{
index: ["./src/index.js", "./src/count.js"],
add: "./src/add.js"
}
【输出(output)】:在哪里输出文件,以及如何命名这些文件。
output:{
filename: "js/[name].js",
path: resolve(__dirname, "build"), // 输出文件目录(将来所有资源输出的公共目录)
publicPath: '/', // 所有资源引入的公共路径前缀,一般用于生产环境下,所有引入的资源都加入此前缀
chunkFilename: "js/[name]_chunk.js", // 打包其他资源时使用此命名,若不配置此项,则使用filename命名
library: "[name]", // 一般用于dll打包所向外暴露的变量名
libraryTarget: "window/global/commonjs" // 向外暴露的变量名挂载到哪个全局变量
}
【loader】:处理那些非js文件(webpack自身只能解析js和json)。
1)webpack本身只能处理js、json模块,若要加载其他类型的文件或者模块,就需要使用对应的loader。它本身是一个函数,接收原文件作为参数,返回转换的结果。
2)loader一般以xxx-loader的方式命名,xxx代表了这个loader要做的转换功能,比如css-loader。
【插件(plugin)】:执行范围更广的任务,从打包到优化都可以实现。
1)插件可以完成一些loader完成不了的功能。
【模式(mode)】:有生产模式production和开发模式development。
1)开发依赖:帮助程序员加工代码的库,都是开发依赖。
2)生产依赖:帮助程序员实现功能效果的库,都是生产依赖。
1. devServer
安装webpack-dev-server
webpack@4、webpack-cli@3、webpack-dev-server@3 这三个版本相容。
局部安装:npm install webpack-dev-server@3 -D
全局安装:npm install webpack-dev-server@3 -g
配置:与五大核心概念平级
// webpack.config.js
module.exports = {
...
module: {},
// 开发服务器 devServer:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器~)
// 特点:只会在内存中编译打包,不会有任何输出
// 启动devServer的指令为:npx webpack-dev-server
devServer:{
contentBase: resolve(__dirname, "build"), // 运行代码的目录
watchContentBase: true, // 监视 contentBase 目录下的所有文件,一旦文件变化就会 reload
watchOptions: {
ignored: /node_modules/, // 忽略文件
}
compress: true, // 启动gzip压缩
port: 5000, // 端口号
host: "localhost", // 域名
open: true, // 自动打开浏览器
hot: true, // 开启HMR功能
clientLogLevel: "none", // 不要显示启动服务器日志信息
quiet: true, // 除了一些基本启动信息外,其他内容不要显示
overlay: false, // 若出错了,不要全屏提示
// 服务器代理,目的是解决开发环境跨域问题
proxy: {
// 一旦devServer(5000)服务器接收到 /api/xxx 的请求,就会把请求转发到另外一个服务器(3000)
"/api": {
target: "http://localhost:3000",
// 发送请求时,请求路径重写,将 /api/xxx --> /xxx(去掉/api)
pathRewrite: {
"^/api": ""
}
}
}
}
...
}
修改package.json中的script指令:"dev": "webpack-dev-server"
。
修改后运行指令:npm run dev
。
生产环境准备
新建config文件夹,重命名webpack.config.js为webpack.dev.js,放入config文件夹。
复制webpack.dev.js,重命名为webpack.prod.js,删除其中的devServer配置,因为这是开发环境特有的,生产环境不需要。
修改pack.json中的script指令:
"start": "webpack-dev-server --config ./config/webpack.dev.js",
"build": "webpack --config ./config/webpack.prod.js"
修改output中的path为:path: resolve(__dirname, '../build')
2. resolve
// webpack.config.js
mode: "development",
// 解析模块的规则
resolve: {
// 配置解析模块路径别名。优点:简写路径;缺点:没有路径提示
alias: {
$css: resolve(__dirname, "src/css")
},
// 配置省略文件路径的后缀名
extensions: [".js", ".json", ".jsx", ".css"],
// 告诉webpack解析模块是去找哪个目录(假如现在webpack.config.js处于第三级目录)
modules: [resolve(__dirname, "../../node_modules"), "node_modules"]
}
1. webpack打包的基本流程
2. loader和plugin的区别
3. live-reload与HMR
不同点:
live-reload(自动刷新):刷新整体页面,从而查看到最新代码效果,页面状态全部都是最新的。
HMR(热膜替换):没有刷新整个页面,只是加载了修改模块的打包文件并运行,从而更新页面的局部界面,整个界面的其他部分的状态还在。
相同点:代码修改后都会自动重新编译打包。
1. 初始化项目
npm init
或者yarn init
生成一个package.json文件2. 安装webpack
npm install webpack@4 webpack-cli@3 -g
全局安装作为指令使用。npm install webpack@4 webpack-cli@3 -D
本地安装,作为本地依赖使用。3. 运行
webpack ./src/js/app.js -o ./build/js/app.js --mode=development/production
命令即可完成打包。4. 结论
5. 缺点
6. 改善
7. webpack配置文件
规范:遵循commonJS规范,所有构建工具都是基于nodejs平台运行的。src文件则遵循ES6modeule规范。
目的:在项目根定义配置文件,通过自定义配置文件简化上述运行指定的操作。
文件名称:webpack.config.js。
文件内容:五大核心概念
// webpack.config.js
const {resolve} = require('path');
module.exports = {
mode:'development',
entry:'./src/js/app.js',
output:{
// __dirname: 代表当前文件的绝对路径,build: 代表文件夹
path: resolve(__dirname, 'build'),
// 输出的文件名
filename: 'built.js'
},
module:{
rules:[
// 在此处配置一个一个loader
]
},
plugin:[
// 此处专门用于配置插件,插件必须经过实例化这一环节
]
}
运行指令:webpack
,运行了该指令后会自动找webpack.config.js文件。
npm i style-loader@2 css-loader@5 -D
// webpack.config.js
module.exports = {
...
module: {
rules: [
{
// 匹配哪些文件
test: /\.css$/,
// 使用哪些loader处理,use数组中loader的执行顺序:从左到右,从下到上 依次执行
use: [
// 在head中创建style标签并将css-loader编译后的文件进行引入
'style-loader',
// 将css文件变成commonjs模块加载到js中,里面的内容是样式字符串
'css-loader'
]
}
]
}
...
}
npm i less@4 less-loader@7 -D
// webpack.config.js
module.exports = {
...
module: {
rules: [
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
// 将less翻译成css,less-loader依赖于less
'less-loader'
]
}
]
}
...
}
html-webpack-plugin
。npm i html-webpack-plugin@4 -D
// webpack.config.js
// 插件使用:1.下载 2.引入 3.实例化(new)
const HtmlWebPackage = require('html-webpack-plugin');
module.exports = {
...
plugins: [
// 功能:默认创建一个空的html文件,自动引入打包输出的资源(js/css)
// 需要有结构的html的话,则需要进行配置
new HtmlWebPackage({
// 赋值该文件的html文件中的结构
template: './src/index.html'
})
]
...
}
npm i file-loader@6 url-loader@4 -D
// webpack.config.js
module.exports = {
...
module: {
rules: [
{
test: /\.(jpg|png|gif)$/,
// 下列写法与此写法作用一致use:[{loader:'url-loader',options:{...}}]
// url-loader: 默认处理不了html中的img图片,url-loader依赖于file-loader
// url-loader相对于file-loader的优势是可以对图片进行动态转换base64编码(控制limit属性值可以控制阈值)
loader: 'url-loader',
options: {
// 图片大小小于 8kb,就会被base64处理;若大于 8kb 就用file-loader处理(相当于处理其他资源一样)
// 优点:减少请求数量(减轻服务器压力); 缺点:图片体积会更大(文件请求速度变慢)
limit: 8 * 1024,
// 打包后的文件命名取前10位hash值
name: '[hash:10].[ext]',
// 若打包后html中的图片的src='[object Module]'
// 解决办法:url-loader中加入一个配置 -> esModule: false即可
esModule: true
}
},
]
},
...
}
npm i html-loader@1 -D
module: {
rules: [
{
test: /\.html$/,
// 处理html文件中的img图片资源,主要负责引入img,从而能被url-loader进行处理
use: ['html-loader']
// 若打包后html中的图片的src='[object Module]',因为html-loader引入的图片是commonjs语法
// 解决办法:url-loader中加入一个配置 -> esModule: false即可
}
]
},
module: {
rules: [
{
// 要排除的文件
exclude: /\.(html|js|css|less|jpg|png|gif)$/,
// 用于处理其他资源,也可以处理图片资源,核心操作:提取资源到指定位置,且可以修改文件名等操作
loader: 'file-loader',
options: {
name: '[hash:10].[ext]',
// 输出路径
outputPath: '/assets/font'
}
}
]
},
安装:npm i mini-css-extract-plugin@1 -D
引入:const MiniCssExtractPlugin = require('mini-css-extract-plugin')
配置:
// webpack.config.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
plugins: [new MiniCssExtractPlugin()],
};
由于提取了独立的文件,要从外部引入,所以可能会有路径的问题。
解决方案:在output配置中,添加publicPath: '/'
,publicPath根据实际情况自行调整,若上线运行值为:/imgs,若本地右键运行值为:/build/imgs
安装:npm i postcss@8 postcss-loader@4 postcss-preset-env@7 -D
postcss-preset-env
:帮助postcss识别某些环境,从而加载指定的配置,精确到某个浏览器的版本。运作时,帮助postcss找到package.json中的browserslist里面的配置,并通过配置加载指定的css兼容性样式。
使用顺序:["css-loader", "postcss-loader", "less-loader"]
// webpack.config.js
// 设置node环境变量,使得postcss加载browserslist中的"development"兼容样式
process.env.NODE_ENV = 'development'
module.exports = {
module: {
rules: ['css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions:{
ident: 'postcss',
plugins: [
// postcss的插件
require('postcss-preset-env')
]
}
}
}
]
}
}
在package.json配置,在其中追加browserslist配置,通过配置加载指定的css兼容性样式(vue、react中的配置)
// package.json
"browserslist": {
// 开发环境
"development": [
"last 1 chrome version", // 最新版的chrome浏览器版本
"last 1 firefox version", // 最新版的firefox浏览器版本
"last 1 safari version"
],
// 生产环境:默认是生产环境
"production": [
">0.2%", // 兼容市面上99.8%的浏览器
"not dead", // "死去"的浏览器不做兼容,例如IE8
"not op_mini all", // 不做opera浏览器mini版兼容
]
}
browserslist是一个单独的库,被广泛用在各种设计浏览器或者移动端的兼容支持工具中。关于browerlist更详细的配置,参考https://github.com/browserslist/browerslist
。
安装:npm i optimize-css-assets-webpack-plugin@6 -D
。
// webpack.config.js
module.exports = {
plugins: [
new OptimizeCssAssetsWebpaclPlugin(),
]
}
概述:对js基本语法错误或者隐患,进行提前检查。
安装:npm i eslint@7 eslint-loader@4 -D
。
安装检查规则库:npm i eslint-config-airbnb-base@15 eslint-plugin-import@2
。
ellint-config-airbnb-base
定制了一套标准的、常用的js语法检查规则,推荐使用。
eslint-plugin-import
用于将js语法检查规则进行导入,配合eslint-loader使用。
// webpack.config.js
module:{
rules:[
{
test: /\.js$/,
exclude: /node_modules/,
// 优先执行,原因是eslint和babel都是对js文件进行处理,所以两者之间要指定执行的顺序
enforce: 'pre',
// 对js进行语法检查
loader: 'eslint-loader', // eslint-loader依赖于eslint
options: {
fix: true // 若有问题自动修复,重要!
}
}
]
}
// package.json
"eslintConfig": {
"extends": "airbnb-base", // 直接使用airbnb-base提供的语法规则
"env": {
"browser": true // 支持浏览器端全局变量
}
}
若出现:warning Unexpected console statement no-console
警告,意思是不应该在项目中写console.log();若想忽略,就在要忽略检查代码的上方输入一行注释: eslint-disable-next-line
即可。
npm i babel-loader@8 @babel/core@7 -D
兼容性处理方式
1. 基本js兼容性处理
安装:npm i @babel/preset-env@7 -D
// webpack.config.js
module: {
rules: [
test: /\.js$/,
exclude: /node_modules/,
use: [{
// 将es6语法转换为es5语法(即做浏览器的兼容性处理)
loader: "babel-loader",
options: {
// 预设:只是babel做怎么样的兼容性处理,@babel/preset-env处理最基本的兼容性问题
presets: ["@babel/preset-env"]
}
}]
]
}
问题:使用@babel/preset-env
只能处理简单的语法,promise等无法处理。
2. 全部js兼容性处理
安装:npm i @babel/polyfill -D
,此包要安装在生产依赖中,而非开发依赖。
// index.js 入口文件
import "@babel/polyfill"
问题:虽然可以完成高级es6语法的转换,但缺点是所有都转换,无法按需转换,生成的js体积大。
3. 按需处理js兼容性
安装:npm i core-js@3 -D
。
注意点:正常来讲,一个1文件只能被一个loader处理。当一个文件要被多个loader处理,那么一定要指定loader的执行顺序,故core-js兼容性处理
与eslint语法检查
的执行顺序不能颠倒,先执行语法检查后执行兼容性处理。因为core-js的作用原理是,要在入口文件中导入core-js
中相应的js文件;而eslint
中排除了node_modules
文件夹,当eslint
检查到core-js
导入的文件时,就会发生错误。
// webpack.config.js
module: {
rules: [
test: /\.js$/,
exclude: /node_modules/,
use: [{
loader: "babel-loader",
options: {
presets: [
[
"@babel/preset-env",
{
// 按需加载
useBuiltIns: "usage",
// 指定core-js版本
corejs: {
version: 3
},
// 指定兼容性做到哪个版本的浏览器
targets: {
chrome: "60",
firefox: "60",
ie: "9",
safari: "10",
edge: "17"
}
}
]
]
}
}]
]
}
直接修改webpack.prod.js中的mode为production即可。
若设置了模式为production,必须在new HtmlWebpackPlugin时添加配置minify: false。
new HtmlWebpackPlugin({
minify: {
collapseWhitespace: true, // 去除空格
removeComments: true // 去除注释
}
})
source-map:一种提供源代码到构建后代码映射技术,若构建后代码出错了,通过映射可以追踪源代码错误。它会生成一个 xxx.map
文件,里面包含源代码和构建后代码每一行、每一列的映射关系。当构建后代码出错了,会通过 xxx.map
文件,从构建后代码出错位置找到映射后源代码出错位置,从而让浏览器提示源代码文件出错位置,帮助我们更快的找到错误根源。
// webpack.config.js
module: {
...
},
plugins: [],
devtool: '[inline-|eval-|hidden-][nosources-][cheap-[module-]]source-map'
组合:[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
。
source-map
:生成外部.map文件,提示错误代码准确信息和源代码的错误位置。
inline-source-map
:在js文件中生成内联source-map,格式为base64,提示错误代码准确信息和源代码的错误位置。
eval-source-map
:在每个js文件中生成对应source-map,都在eval,提示错误代码准确信息和源代码的错误位置。
hidden-source-map
:生成外部文件,提示错误代码原因,但没有错误位置,不能追踪源代码错误,只能提示到构建后代码的错误位置。
nosource-source-map
:生成外部文件,提示错误代码原因,但是没有任何源代码信息。(用于生产环境)
cheap-source-map
:生成外部文件,提示错误代码原因和源代码的错误位置,只能精确到行。(默认精确到列)
cheap-module-source-map
:生成外部文件,提示错误代码原因和源代码的错误位置。除外,还会将loader的进行加入。
内联和外部:外部生成了.map文件,内联没有;内联构建速度更快。
开发环境:速度快,调试更友好(一般选择内联)
速度快(eval > inline > cheap > …)
eval-cheap-source-map > eval-source-map
调试更友好
source-map > cheap-module-source-map > cheap-source-map
综上:开发环境选择eval-source-map > eval-cheap-module-source-map
生产环境:源代码是否要隐藏?调试是否要友好?(内联会让代码体积变大,故在生产环境下不用内联)
隐藏:nosource-source-map 全部隐藏
,hidden-source-map 只会隐藏源代码,会提示构建后代码错误信息
调试:source-map > cheap-module-source-map
HMR:hot module replacement 热膜块替换,在devServer中添加hot: true
即可。
作用:一个模块发生变化,只会重新打包这一模块(而不是打包所有的模块),极大提升了构建速度。
样式文件:可以使用HMR功能,因为style-loader内部实现了热模块替换功能,在开发环境下,必须使用style-loader
而不能使用mini-css-extract-webpack-plugin
。
html文件:默认不能使用HMR功能(webpack5中默认能使用),修改的同时html文件的内容也不会发生变化(解决这个问题需要在entry入口,将html文件引入)。值得注意的是,html只有一个文件,没有必要进行HMR。
js文件:默认不能使用HMR功能,若要实现则需要修改js代码,添加支持HMR功能的代码。
// index.js 入口文件
// 兼容性处理
if(module.hot){ // 一旦 module.hot 为true,说明开启了HMR功能,就可以让HMR功能代码生效
// module.hot.accep会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建
module.hot.accept('./print.js');
}
注意:HMR功能只能处理非入口js文件。
上面这样写会很麻烦,所以实际开发我们会使用其他 loader 来解决,比如vue-loader
,react-hot-loader
。
// webpack.config.js
module: {
roules: [
{
loader: 'eslint-loader'
},
{
// oneOf:作用是当匹配到了符合自己的loader时就不再往下匹配了,
// 要求每个loader匹配的都不能重复,若重复,则提取出来,否则只有最先匹配的loader生效
oneOf: [
{
loader: ''
},
{
loader:''
}
]
}
]
}
eslint缓存
若是用plugin进行语法检查,写法如下:
// webpack.confg.js
plugins: [
new ESLintWebpackPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, "../src"),
exclude: "node_modules", // 默认值
cache: true, // 开启缓存
// 缓存目录
cacheLocation: path.resolve(__dirname, "../node_modules/.cache/.eslintcache"),
}),
]
babel缓存
babel缓存,目的是让第二次打包构建的速度更快,优化打包速度。
// webpack.config.js
{
loader: 'babel-loader',
options: {
presets: [],
// 开启babel缓存,第二次构建是,会读取之前的缓存
cacheDirectory: true,
// 关闭缓存文件的压缩,压缩会消耗时间,缓存在内存中,对上线无影响
cacheCompression: false
}
}
多进程打包需要借助thread-loader
来完成,进程启动大概需要600ms,进程通信也有开销,只有工作消耗时间比较长,才需要多进程打包,若滥用的话只会更慢。
// webpack.config.js
module:{
rules:[
{
// 一般情况下,主要是打包js文件,因为一个项目中js居多
test:/\.js$/,
use:[
{
// 多进程loader需要放置在功能loader的后面
loader:'thread-loader',
options:{
workers: 2 // 开启进程为2个
}
},
{
loader: 'babel-loader',
}
]
}
]
}
externals:打包的时候拒绝把第三方库打包进来,此时第三方库就需要在index.html中手动通过cdn的方式引进来。
// webpack.config.js
mode: "production",
externals:{
// 拒绝jQuery被打包进来
jquery: "jQuery" // jquery是库名:jQuery是npm下的包名
}
dll作用:默认情况下,第三方库在code split下是将项目所用的库都打包成一个js文件,那么这个js文件就非常大,利用dll技术可以将用到的第三方库打包成不同的js文件(即分别进行单独打包)以达到减少每个文件体积的目的。
externals和dll的区别:externals拒绝打包第三方库,使用时则通过cdn的方式引入(第三方库不需要部署在服务器);dll是提前将第三方库打包好,使用时直接引入打包好的即可(第三方库需要部署在服务器)。
配置,在webpack.config.js的同级目录下新建webpack.dll.js。单独打包第三方库,需运行webpack --config webpack.dll.js
// webpack.dll.js
const webpack = require("webpack") // webpack自带的插件,无需下载
module.exports = {
entry:{
jquery: ["jquery"], // 第一个jquery是最终打包生成的[name],数组里面的jquery是要打包的库
vue: ["vue"]
},
output:{
filename: "[name].js",
path: resolve(__dirname, "dll"),
library: "[name]_[hash]" // 打包的库里面向外暴露出去的内容叫什么名字
},
plugins:[
// 打包生成一个 manifest.json,提供和jquery映射,目的是webpack打包的时候标记此库无需打包
new webpack.DllPlugin({
name: "[name]_[hash]", // 映射库向外暴露的内容的名称
// 每个第三方库对应一个映射文件
path: resolve(__dirname, "dll/[name].manifest.json") // 输出文件路径
})
],
mode: "production"
}
webpack.config.js配置
// webpack.config.js
const webpack = require('webpack')
plugins:[
// 告诉webpack哪些库不需要打包,同时使用时的名称也得变
new webpack.DllReferencePlugin({
manifest: resolve(__dirname, "dll/jquery.manifest.json"), // 进行webpack.dll.js打包时生成的映射文件
manifest: resolve(__dirname, "dll/vue.manifest.json")
}),
// 每次打包时在html文件中引入dll文件夹里面的第三方资源
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname, "dll/jquery.js"),
filepath: resolve(__dirname, 'dll/vue.js'),
})
]
浏览器向服务器请求数据时,除了第一次向服务器索取数据,后面就强制走浏览器的缓存了(数据缓存未过期)。若此时服务器重新部署了新的资源,浏览器会请求不到新的文件资源。为了解决这个问题,可以对服务器的文件资源进行hsah值命名,作为文件资源的版本号。
配置
// webpack.config.js --> mode: "production"
module.exports = {
entry: "./src/js/index.[contenthash:10].js",
plugins: [
new MiniCssExtractWebpackPlugin({
filename: "css/built.[contenthash:10].css"
})
]
}
概述:有时候,我们一个模块向外暴露了n个函数、对象或者其他一些数据,但是我们只是用到了其中的几个,那在最终打包的时候,我们只希望把我们所用的打包进去,这时候就要用到tree-shaking,即去除无用代码,以达到减少代码体积的目的。
配置:同时满足两个条件webpack会自动开启tree-shaking
1)使用ES6模块化;2)开启production环境。
由于webpack版本的原因,打包的时候可能会出现去除入口文件引入的css、less、@babel/polyfill,为了防止出现此等原因,需要在package.json中配置"sideEffects":["*.css", "*.less"]
,这样子就不会把入口文件的css、less文件意外去除掉。
code split:代码分割,将一个js文件分割成多个js文件。默认情况下,在单入口文件下,项目中用到的js代码都会被打包成为一个bundle,这就会使得该bundle体积大。
方法一:从入口文件入手,采用多入口文件时,就不需要在index.js(原单文件入口时的入口文件)引入了。
// webpack.config.js
entry: './src/js/index.js' --> 单入口,对应单页面应用
entry: {
// 多入口:有多少个入口文件就输出多少个bundle
index: "./src/js/index.js",
test: "./src/js/test.js"
},
output: {
path: resolve(__dirname, "build"),
filename: "js/[name].[contenthash:10].js", --> 这样就可以避免每个js文件名字重复
}
缺点:有多少个js文件就需要在配置文件里面添加,重复修改,不够灵活。
方法二:配置optimization。(可以与多入口文件用)
若多个js文件中都引入了一个模块,打包时会将该模块都打包到各自引用的js文件(即每个js文件都会有该模块一样的代码),为了防止这种情况,可以将该模块单独打包到一个js文件,其他js文件要使用就引入即可,极大减少代码体积,提高代码复用率,此时就需要配置optimization。
若是多入口文件,则自动分析多入口chunk中有没有公共的文件,若有会打包成单独一个bundle。
若是单入口文件,则将项目用到的第三库中的代码单独打包成一个js文件最终输出。
// webpack.config.js
optimization: {
splitChunks: {
chunks: "all"
}
},
mode: "production"
方法三:import动态导入语法,能将某个文件单独打包。在单入口文件index.js中,通过js代码,通过按需导入方式,让某个js文件被单独打包成一个文件。再配合optimization。
解决动态导入import语法报错问题 --> 实际使用eslint-plugin-import
的规则解决的,故需要下载eslint-plugin-import
插件。
// index.js 单入口文件
/*webpackChunkName:'test'*/:为这个文件打包输出的时候命名,还需要配置output:{chunkFilename: [name].chunk.js}
import(/* webpackChunkName: 'test' */'.test')
.then((mul, count)=>{
// 文件加载成功
})
.catch(()=>{
// 文件加载失败
})
方案:单文件入口 + optimization + 入口文件import
懒加载:当文件使用时才加载。放在监听事件的回调函数中(异步事件)。
预加载(prefecth):会在使用之前提前加载js文件。(兼容性很差,慎用)
// index.js 入口文件
document.eventListen = function(){
// 该写法与code split中的按需导入一样
import(/*webpackChunName: 'test', webpackPrefetch: true*/'./test').then(()=>{
// 文件加载成功
})
}
预加载和正常加载区别:正常加载可以认为是并行加载(同一时间加载多个文件);预加载是其他资源正常加载后,浏览器空闲了,再偷偷加载资源。
PWA:渐进式网络开发应用程序(离线可访问技术)
PWA需要借助workbox插件来完成,workbox-webpack-plugin
。
// webpack.config.js
plugins:[
new WorkboxWebpackPlugin.GenerateSW({
// 作用:帮助serviceWorker快速启动,删除旧的serviceWorker。
// 打包后生成 serviceworker 配置文件
clientsClaim: true,
skipWaiting: true
})
]
配置注册serviceWorker,注册完成后需要运行在服务器
// index.js 入口文件
// 处理兼容性问题
if("serviceWorker" in navigator){
// 绑定监听事件,等待全局load加载完毕
window.addEventListener("load", ()=>{
// 全局load加载完毕后,开始注册serviceWorker。注册时传入的参数文件是workbox-webpack-plugin打包的chunk
navigator.serviceWorker.register("/service-worker.js").
then(()=>{
// 注册成功
})
.catch(()=>{
// 注册失败
})
})
}
代码优化
从 4 个角度对 webpack 和代码进行了优化:
提升开发体验
Source Map
让开发或上线时代码报错能有更加准确的错误提示。提升 webpack 提升打包构建速度
使用 HotModuleReplacement
让开发时只重新编译打包更新变化了的代码,不变的代码使用缓存,从而使更新速度更快。
使用 OneOf
让资源文件一旦被某个 loader 处理了,就不会继续遍历了,打包速度更快。
使用 Include/Exclude
排除或只检测某些文件,处理的文件更少,速度更快。
使用 Cache
对 eslint 和 babel 处理的结果进行缓存,让第二次打包速度更快。
使用 Thead
多进程处理 eslint 和 babel 任务,速度更快。(需要注意的是,进程启动通信都有开销的,要在比较多代码处理时使用才有效果)
减少代码体积
使用 Tree Shaking
剔除了没有使用的多余代码,让代码体积更小。
使用 @babel/plugin-transform-runtime
插件对 babel 进行处理,让辅助代码从中引入,而不是每个文件都生成辅助代码,从而体积更小。
使用 Image Minimizer
对项目中图片进行压缩,体积更小,请求速度更快。(需要注意的是,如果项目中图片都是在线链接,那么就不需要了。本地项目静态图片才需要进行压缩。)
优化代码运行性能
使用 Code Split
对代码进行分割成多个 js 文件,从而使单个文件体积更小,并行加载 js 速度更快。并通过 import 动态导入语法进行按需加载,从而达到需要使用时才加载该资源,不用时不加载资源。
使用 Preload / Prefetch
对代码进行提前加载,等未来需要使用时就能直接使用,从而用户体验更好。
使用 Network Cache
能对输出资源文件进行更好的命名,将来好做缓存,从而用户体验更好。
使用 Core-js
对 js 进行兼容性处理,让我们代码能运行在低版本浏览器。
使用 PWA
能让代码离线也能访问,从而提升用户体验。
Webpack4 :处理图片资源通过 file-loader
和 url-loader
进行处理。
Webpack5: 已经将两个 Loader 功能内置到 Webpack 里了,只需要简单配置即可处理图片资源。
// webpack.config.js
module:{
rules:[
{
test: /\.(png|jpe?g|gif|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024 // 小于10kb的图片会被base64处理
}
},
generator: {
// [query]: 添加之前的query参数
filename: "static/imgs/[hash:8][ext][query]",
},
}
]
}
// webpack.config.js
output: {
clean: true // 自动将上次打包目录清空
}
// webpack.config.js
module: {
rules: [
{
test: /\.(ttf|woff2?|mp4|mp3|avi)$/,
type: "asset/resource",
generator: {
filename: "static/media/[hash:10][ext][query]"
}
}
]
}
Eslint:可组装的 JavaScript 和 JSX 检查工具。这句话意思就是:它是用来检测 js 和 jsx 语法的工具,可以配置各项功能。使用 Eslint,关键是写 Eslint 配置文件,里面写上各种 rules 规则,将来运行 Eslint 时就会以写的规则对代码进行检查。
1. 配置文件
配置文件有很多种写法:
新建文件,位于项目根目录,.eslintrc
,.eslintrc.js
,.eslintrc.json
,以上文件区别在于配置格式不同,通常使用.eslintrc.js
。
package.json
中eslintConfig
:不需要创建文件,在原有文件基础上写Eslint会自动查找和读取它们。
// .eslintrc.js
module.exports = {
// 解析选项
parserOptions: {
ecmaVersion: 6, // ES语法版本
sourceType: "module", // ES模块化
ecmaFeatures: { // ES其他特性
jsx: true // 如果是React项目,就需要开启jsx语法
}
},
// 具体检查规则
rules: {
// "off"或0 -> 关闭规则;
// "warn"或1 -> 开启规则,使用警告级别的错误,warn不会导致程序退出;
// "error"或2 -> 开启规则,使用错误级别的错误,error被触发程序会退出。
},
// 继承其他规则
extends: [
// 开发中一点点写rules规则太费劲,所以有更好的办法,继承现有的规则
// 现有以下有名的规则
// Eslint官方规则:eslint:recommended
// Vue Cli官方规则:plugin:vue/essential
// React Cli官方规则:react-app
]
}
2. Webpack中使用
npm i eslint-webpack-plugin eslint -D
。// webpack.config.js
const EslintWebpackPlugin = require("eslint-webpack-plugin");
module.exports = {
plugins:[
new EslintWebpackPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, "src");
})
]
}
// .eslintrc.js
module.exports = {
// 继承Eslint规则
extends: ["eslint:recommended"],
env: {
node: ture, // 启用node中全局变量
browser: true, // 启用浏览器中全局变量
},
parserOptions: {
ecmaVersion: 6,
sourceType: "module"
},
rules: {
"no-var": 2, // 不能使用var定义变量
},
plugins: ["import"], // 解决动态导入语法报错
}
3. VSCode Eslint 插件
在VSCode中下载Eslint插件即可,不用编译就能看到错误,可以提前解决问题。但此时所有文件都会被Eslint插件所检查,dist目录下的打包文件就会报错,但只需检查src下面的文件,无需检查dist目录下的文件。此时要在项目根目录新建.eslintignore
文件。
// .eslintignore
# 忽略dist目录下的所有文件
dist
Babel:主要用于将ES6语法编写的代码转换为向后兼容的js语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
1. 配置文件
配置文件有很多种写法:
新建文件,位于项目根目录,babel.config.js
,babel.config.json
,.babelrc
,.babelrc.js
,.babelrc.json
。
package.json
中babel
:不需要创建文件,在原有的基础上配置。
Babel会查找和自动读取他们,所以以上配置文件只需存在一个即可。
// babel.config.js
module.exports = {
// 预设
presets: [],
}
presets预设:简单理解就是一组Babel插件,扩展Babel功能。
@babel/preset-env
:一个智能预设,允许使用最新的JavaScript。@babel/preset-react
:一个用来编译React jsx语法的预设。@babel/preset-typescript
:一个用来编译TypeScript语法的预设。2. 在Webpack中使用
下载npm i babel-loader @babel/core @babel/preset-env -D
。
// babel.config.js
module.exports = {
presets: [
"@babel/preset-env"
]
}
// webpack.config.js
module.exports = {
module: {
rules: [
{
test:/.\js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
}
}
下载npm i css-minimizer-webpack-plugin -D
。
// webpack.config.js
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");
plugins:[
new CssMinimizerWebpackPlugin(); // 压缩css
]
// 也可以将css压缩写到optimization.minimizer里面
optimization:{
minimizer:[new CssMinimizerWebpackPlugin()]
}
定义:多进程打包就是开启电脑的多个进程同时干一件事,速度更快。
作用:当项目越来越庞大时,打包速度越来越慢,甚至于需要一个下午才能打包出来代码。这个速度是比较慢的。我们想要继续提升打包速度,其实就是要提升 js 的打包速度,因为其他文件都比较少。而对 js 文件处理主要就是 eslint 、babel(babel多进程打包跟webpack4一样)、Terser(js压缩工具) 三个工具,所以我们要提升它们的运行速度。我们可以开启多进程同时处理 js 文件,这样速度就比之前的单进程打包更快了。
下载包npm i -D thread-loader terser-webpack-plugin
。
// webpack.config.js
// nodejs核心模块,直接使用
const os = require("os");
// cpu核数
const threads = os.cpus().length;
// 压缩js的插件,在该插件内进行压缩js代码的多进程配置
const TerserWebpackPlugin = require("terser-webpack-plugin");
plugins: [
new ESLintWebpackPlugin({
threads, // 为语法检查开启多进程
})
],
optimization: {
minimizer: [
// 当mode: "production"时,会默认开启js代码压缩,但是需要进行其他配置时,就需要重新写了
new TerserWebpackPlugin({
parallel: threads // 开启js代码压缩多进程
})
]
}
@babel/plugin-transform-runtime
: 禁用了 Babel 自动对每个文件的 runtime 注入,而是引入 @babel/plugin-transform-runtime
并且使所有辅助代码从这里引用。
Babel 为编译的每个文件都插入了辅助代码,使代码体积过大!Babel 对一些公共方法使用了非常小的辅助代码,比如 _extend
。默认情况下会被添加到每一个需要它的文件中,将这些辅助代码作为一个独立模块,来避免重复引入,从而减少代码体积。
下载npm i @babel/plugin-transform-runtime -D
// webpack.config.js
{
loader: "babel-loader",
options: {
plugins: ["@babel/plugin-transform-runtime"], // 减少代码体积
}
}
开发如果项目中引用了较多图片,那么图片体积会比较大,将来请求速度比较慢。我们可以对图片进行压缩,减少图片体积。
注意:如果项目中图片都是在线链接,那么就不需要了。本地项目静态图片才需要进行压缩。
下载包:npm i -D image-minimizer-webpack-plugin imagemin
。
无损压缩:npm i -D imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo
。
有损压缩:npm i -D imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo
。
// webpack.config.js
const ImageMinimizerWebpackPlugin = require("image-minimizer-webpack-plugin");
optimization: {
minimizer: {
// 压缩图片
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminGenerate,
options: {
plugins: [
["gifsicle", { interlaced: true }],
["jpegtran", { progressive: true }],
["optipng", { optimizationLevel: 5 }],
[
"svgo",
{
plugins: [
"preset-default",
"prefixIds",
{
name: "sortAttrs",
params: {
xmlnsOrder: "alphabetical",
},
},
],
},
],
],
},
},
}),
}
}
此时运行会出错,需要安装两个文件到 node_modules 中才能解决
jpegtran.exe:需要复制到 node_modules\jpegtran-bin\vendor
下面,官网http://jpegclub.org/jpegtran/
。
optipng.exe:需要复制到 node_modules\optipng-bin\vendor
下面,官网https://optipng.sourceforge.net/
。