1. 防止打包后出现缓存文件?
两种方案
- 使用 HASH:
filename:"main.[hash:数字].js"「默认名字」
- 可以控制 hash 值的长度:hash:你想要的长度
main.[hash:5].js
- 防止出现缓存,每次都会生成一个新的随机字符串,不会出现缓存问题
- 可以控制 hash 值的长度:hash:你想要的长度
- 使用插件:
clean-webpack-plugin
清除缓存文件- 如果没有修改文件,那么执行使用缓存文件,不会打包成新的文件
- 形成新的文件,删除旧的文件
2. 怎么配置本地服务和proxy 代理?
配置本地服务,有两种方案
-
通过
webpack-dev-server
可以打开一个本地服务-
配置
package.json
文件"scripts": { "start": "webpack-dev-server" }
- 执行
npm run start
- 通过
localhost:8080
在浏览器中打开 - 本地服务启动成功
- 执行
-
配置
webpack.config.js
devServer:{ port: 9000, open: true, compress: true, contentBase: resolve(__dirname, '../public') }
port
:9090
「修改服务的端口号」open
:tru
e「服务启动之后自动打开浏览器」- 或者是在
package.json
中添加--open
,也是可以实现的
- 或者是在
compress
:true
「是否开启静态压缩(gzip
)」contentBase
:resolve(__dirname, '../public')
public
文件下的资源都可以通过服务方式请求- 把
public
下的文件当做可访问的服务器中的文件
hot
:true
「热更新」- 不需要其他配置,webpack 自动处理
before:function(app){}
- 接口造假数据,在请求数据之前先走这里
-
-
安装
@webpack-cli/serve
- 配置
package.json
文件
"scripts": { "start": "webpack serve" }
-
执行
npm run start
-
通过
localhost:8080
在浏览器中打开 -
本地服务启动成功
- 配置
配置 proxy
代理
两种方式:
- 第一种方式
proxy: {
'api': {
target: "代理路径"
pathRewrite: {// 路径重写:凡是以 api 开头的,都被替换成:''
'^/api': ''
}
}
}
- 第二种方式
proxy: {
// 只要路径中包含:"/api" 的请求,都会被代理到你配置的路径中
"/api": "代理路径"
}
3. 合并配置文件
借助插件 webpack-merge
:两个对象合并成一个对象
使用方法
- 引入必须使用解构赋值的方式
let {merge} = require('webpack-merge');
merge(base, {
// 这里是需要合并的另一个配置文件
})
4. 多入口打包
-
入口文件
- entry 是一个对象,配置多个入口
entry:{ index: './src/index.js', other: './src/other.js' }
-
output
:出口- 对应多个出口:
name
「入口定义的名字」:index/other
- 对应多个出口:
output: {
filename: '[name].[hash:5].js'
}
- 几个页面
new
几个HtmlWebpackPlugin
5. webpack 插件
clean-webpack-plugin
:清除打包文件缓存
- 描述
- 在放入新打包的资源文件之前,先把旧的资源文件删除
- 引入
let {CleanWebpackPlugin} = require('clean-webpack-plugin')
- 使用方法:
plugins:[
new CleanWebpackPlugin()
]
- 配置项:
cleanOnceBeforeBuildPatterns
- 默认会全部删除旧的资源
- 指定某个文件资源不删除
plugins:[
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: [**/*, '!文件名']
})
]
html-webpack-plugin
:打包文件自动引入 HTML 中
描述:自动生成带有入口文件引用的 index.html 使用方法:
let HtmlwebpackPlugin = require("html-webpack-plugin");
// 配置
plugins: [
new HTMLWebpackPlugin({
template: 'HTML 文件地址',
filename: '打包之后的 HTML 文件名',
title: '设置 HTML 文件名'
})
]
配置项:
template
:HTML
文件地址- 如果不设置
template
,那么webpack
会自动生成一个HTML
文件
- 如果不设置
filename
:打包之后的HTML
文件名title
:设置HTM
L 文件名html
模板文件需要设置:
<%= htmlWebpackPlugin.options.title %>
<title>
<%= htmlWebpackPlugin.options.title %>
</title>
hash
:true
「给CSS
、JS
文件设置hash
值」- 打包之后的
HTML
文件中引入的JS
和CSS
会加hash
值,比如:
<script src="dist_index.js?fdfde3b822e33cb629a9"</script>
- 打包之后的
chunks: [index, other]
:指定当前模板引入哪个JS
- 多入口打包:
chunks:['index123','common']
- 多入口打包:
mini-css-extract-plugin
描述:将 CSS 提取为独立的文件的插件,对每个包含 CSS 的 JS 文件都会创建一个 CSS 文件,致辞按需加载
使用方法:们需要使用的是这个插件的 loader
,在 css-loader
之前添加
let MiniCssExtractPlugin = require("mini-css-extract-plugin");
plugins: [
new MiniCssExtractPlugin()
]
rules: [
{test: '/.\css$/', use: [
MiniCssExtractPlugin.loader, 'css-loader'
]}
]
- 配置项
filename:'css/[css文件名].css'
optimize-css-assets-webpack-plugin
:CSS 文件压缩「CSS 默认不压缩」
- 使用方法
- 引入:
let optimizationCssAssets = require('optimize-css-assets-webpack-plugin')
- 配置
optimization:{ minimizer: [ new optimizationCssAssets() ] }
- 引入:
terser-webpack-plugin
:处理 CSS 压缩之后,JS 不自动压缩的请情况
描述:使用 terser 来压缩 JS 使用方法:
- 引入:
let terserWebpackPlugin = require('terser-webpack-plugin')
- 配置
optimization:{
minimizer: [
new terserWebpackPlugin()
]
}
webpack-manifest-plugin
描述:生产资产的显示清单文件
optimize-css-assests-webpack-plugin
描述: 用于优化或者压缩 CSS 资源
6. 常用的 loader
CSS 中的常用 loader
css-loader
描述:加载 CSS,支持模块化、压缩、文件导入等
弊端:把所有的 CSS
代码全都插入到 JS
代码文件中
优化:使用插件:mini-css-extract-plugin
,把 CSS
单拎成一个文件
style-loader
描述:把 CSS 代码注入到 JS 中,通过 DOM 来操作去加载 CSS
less-loader
:处理 less
文件
描述:把 LESS 编译成 CSS
file-loader
描述:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
url-loader
描述: 在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
url-loader
会默认调用file-loader
,需要引入 这两个loader
- 把文件变成一个路径,是调用了 file-loader
配置项:options
name
:img/[name].[ext]- 原始名字叫什么,使用
loader
之后还叫什么「这是file-laoder
的配置」
- 原始名字叫什么,使用
- limit:限制文件大小「数字代表的是 bit 字节」
- 如果图片小于设置的这个限制,会转成
base64
,大于则默认会调用file-loader
,变成路径 - 图片转
base64
的优点- 变成
base64
的好处就是可以减少HTTP
请求 - 减少占用服务器资源
- 变成
- 如果图片小于设置的这个限制,会转成
postcss-loader
:自动添加 CSS
前缀
描述:可以处理 CSS
的兼容写法
- 在处理对应的
CSS
文件之前,先加上postcss-loader
rules: [
{test: '/.\css$/', use: [
MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'
]}
]
- 配置
postcss.config.js
- 需要安装:postcss-preset-env
- package.json 同级下建立一个文件:postcss.config.js『这个文件是 postcss 的配置文件』
module.exports = {
plugins: [
'post-preset-env'
]
}
- 设置浏览器的兼容版本「.browserslisttrc 文件」
- package.json 同级下建立一个文件:.browserslisttrc『这个文件是告诉 postcss 需要兼容浏览器』
- 文件内容:
cover 99.9%
JS 中常用的 loader
eslint-loader
描述:通过 ESlint 检查 JS 代码
处理 JS
高级语言转换为低级语言
- 需要借助
babel
babel-loader
@babel/core
@babel/preset-env
ES6
新语法需要安装:@babel/plugin-syntax-class-properties
插件来编译,babel-loader
目前不支持
class A {
constructor() {
this.a=677; // 支持
}
xxx=123;// 不支持
}
- @logger:装饰器,需要安装
@babel/plugin-proposal-decorators
插件 - async await ,需要安装的插件:
-
@babel/plugin-transform-runtime
-
@babel/runtime/
-
@babel/runtime-corejs3
-
配置项:
corjs
,是@babel/runtime-corejs3
核心库,把不兼容的语法全都转义成兼容语法,值为3
即可
-
babel
的plugins
和preset
的区别presets
:预设「插件的集合」presets
可以作为babel
插件的组合,提前准备好所需要的插件,如果局部使用插件,则不会放在预设中plugins
:插件,我们需要使用的,但是,预设中没有包含的插件
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: [
['@babel/plugin- proposal-decorators',{legacy: true}],
['@babel/plugin- proposal-class-properties', {loose: true}],
['@babel/plugin-transform-runtime',{ corejs: 3 }]
]
}
},
exclude: /node_modules/
}
// @babel/plugin-proposal-decorators 和 @babel/plugin- proposal-class-properties ,同时存在,其 decorators 插件先引入,class-properties 随后
7. Loader 和 Plugin 的不同
Loader
- Loader 是转换器。webpack 只能解析 JS 文件,如果想把其他文件也打包的话,就会用到 Loader
- Loader 的作用:让 webpack 具有加载和解析非 JS 文件的能力
Plugin
- Plugin 是插件。可以扩展 webpack 的功能,让 webpack 具有更多的灵活性。
8. webpack 的构建流程
-
初始化参数:拿到配置文件中的参数和 Shell 语句中「输入命令行时传递的参数:webpack --mode=development」读取并合并参数,拿到最终的参数
-
开始编译:用上一步拿到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;根据配置中的 entry 找到所有的入口文件
-
编译模块:从入口文件开始,调用所有配置的 Loader 对模块进行编译,通过递归查询该模块依赖的模块,然后在对模块进行编译,直至所有入口依赖的文件都经过了处理
-
完成模块编译:经过使用 Loader 编译完所有模块后,得到每一个模块被编译后的最终内容以及它们之间的依赖关系,根据入口和模块之间的依赖关系,组成一个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件假如到输出列表中;到这里是修改输出内容的最后机会
-
输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统中
9. source map 是什么?生产环境怎么用?
- source map 是为了解决开发代码与实际运行代码不一致时,帮助我们 debug 到原始开发代码的技术
- webpack 通过配置可以自动给我们 source maps 文件,map 文件是一种对应编译文件和源文件的方法
source map 的类型
- source-map:原始代码,会生成 map 格式的文件,有映射关系的代码「性能慢」
- eval-source-map:原始代码,会生成 source-map「最高的执行和最低的性能」
- cheap-module-eval-source-map:原始代码(只有行信息)「更高的质量和更低的性能」
- cheap-eval-source-map:转换代码(行内)「无法看到真正的源码」
- eval:生成代码
- cheap-source-map:转换代码(行信息)没有映射
- cheap-module-source-map:原始代码(只有行信息)
记不住?来看下面: 关键字介绍
- eval:使用 eval 包裹模块代码
- source-map:产生 .map 文件
- cheap:不包含列信息
- module:包含 loader 和 source-map
- inline:将 map 作为 DataURI 嵌入,不单独生成 map 文件
如何选 source-map 的类型
- 在源代码的列信息是没有意义的,只要有行信息就能完整的建立打包前后代码之间的依赖关系。所以,不管是开发还是生产环境都可忽略列信息。所以,cheap 属性可以忽略
- 需要定位 debug 到最原始的资源,而不是编译后的 JS 代码。所以,不能忽略掉 module
- 需要生成 .map 文件,所以得有 source-map 属性
选择类型: 开发环境:cheap-module-eval-source-map 生产环境:cheap-module-source-map
module.exports = {
devtool: 'none', // SourceMap
entry: './src/index.js', // 入口文件
output: {
filename: 'bundle.js', // 文件名
path: path.resolve(__dirname, 'dist') // 文件夹
}
}
10. 如何利用 webpack 来优化前端性能
压缩 JS
new TerserPlugin()
压缩 CSS
new OptimizeCssAssetsPlugin()
压缩图片
image-webpack-loader
清除无用的 CSS
单独提取 CSS:mini-css-extract-plugin
loader: MiniCssExtractPlugin.loader
并清除用不到的 CSS:purgecss-webpack-plugin
new PurgecssPlugin({
// 查找 src 目录下的所有文件,如果没有用到的文件清除
paths: glob.sync(`${PATHS.src}/**/*`, {nodir: true});
})
Tree Shaking
一个模块中会有很多方法,Tree Shaking 就是只把用到的方法打包,没用到的方法不会被打包进去
module: {
rules: [{
test: /\.js/,
use: [{
loader: 'bable-loader',
options: {
presets: [['@babel/preset-env', {'modules': false}]]
}
}]
}]
}
代码分割
描述:将代码分割成 chunks 语块,当代码运行到需要他们的时候在进行加载
分割的方式:
-
入口点分割:在 entry 中配置多个入口文件「适用于多页应用」
- 弊端:
- 如果入口文件中包含重复的模块,那么这些模块都会被打包到各个 bundle 中
- 不灵活,不能动态分割代码
- 弊端:
-
动态导入和懒加载
描述:需要什么功能模块就是加载对应的代码,也就是所谓的按需加载
使用方法:通过 import 关键字来实现模块的懒加载,遇到 import 会进行代码分割
// 只有在点击事件触发之后才会去加载 login 模块 root.addEventListener('click', ()=>{ import('./login').then(result=>{ console.log(result.data); }) })
React 中如何实现按需加载
Loading 组件渲染完成后再去渲染 Title 组件
const App = React.lazy(()=>{ // 懒加载 Title 模块 import(/* webpackChunkName: 'title' */'./components/Title') }) // render 函数 render() { return ( <> <Suspense fallback={<Loading />}> <Title /> <Suspense/> </> ) }
-
preload 预先加载
描述:preload 通常用于本页面要用到的关键资源,包括关键 JS、字体、CSS 等,preload 将会把资源的下载顺序放在前面「异步/延迟插入的脚本在网络优先级中是低的」,使关键资源提前下载,可以优化页面打开速度
使用方法:在资源上添加预先加载的注释,指明该模块需要立即被使用
<link rel='preload' as='script' href='utils.js'>
import( `./utils.js` /*webpackPreload: true*/ /*webpackChunkName: 'utils'*/ )
-
prefetch 预先拉取
描述:告诉浏览器未来可能会使用到某个资源,让浏览器闲下来的时候去加载相应的资源
使用方法
import( `./utils.js` /*webpackPrefetch: true*/ /*webpackChunkName: 'utils'*/ )
-
preload 和 preFetch 的区别
- preload:告诉浏览器页面必定需要的资源,浏览器一定会加载这些资源「当页面一定需要的资源,使用 preload」
- prefetch:告诉浏览器页面可能需要的资源,浏览器不一定会加载这些资源「当页面可能需要的资源,使用 prefetch」
-
提取公共代码
- 为什么提取?
- 一个页面中包含很多的公共代码,如果都包含进来可能会发生代码冲突
- 相同的代码被重复加载,会浪费用户的流量和服务器的成本
- 每个页面需要加载的资源太大,导致网页首屏加载慢,影响用户体验
- 如果抽离出公共代码,单独创建一个文件进行加载能进行优化,可以减少网络传输流量,降低服务器成本
- 如何提取?
- 页面之间的公用代码,单独创建成一个文件
- 每个页面单独生成一个文件
- 在需要的页面中引入公用文件
- 为什么提取?
-
代码分割的方式:splitChunks
- 这是一个分包的配置「从 node_modules 中单独拎出来,打包生成一个单独的文件」
- module:通过 import 语句引入的代码
- chunk:webpack 根据功能拆分出来的,包含三种情况
- 你的项目入口「entry」
- 通过 import() 动态引入的代码
- 通过 splitChunks 拆分出来的代码
- bundle:webpack 打包之后的各个文件,一般就是和 chunk 一对一的关系,bundle 就是对 chunk 进行编译压缩打包等处理后的文件
- 默认配置
- cacheGroups:缓存组:符合这些条件生成的包,在后续过程中可以直接走这个缓存「只生成一次,下次及以后都使用缓存」
- 遇到 import 就会进行分割,默认只分割异步的
splitChunks: { chunks: 'all', // 默认只分割异步的,all: 全部,initial: 同步,async: 异步 name: true,// 打包后的名字 page1~page2.chunk.js minSize: 0,// 代码块的最小尺寸,默认:30kb minChunks: 1,// 在分割前模块的被引用次数 maxAsyncRequests: 2,// 每个 import() 中的最大并行请求数量 maxInitialRequests: 4 // 每个入口的最大拆分数量 automaticNameDelimiter: '~',// 拆分后的 JS 命名规则page1~page2.chunk.js cacheGroups: { // 设置缓存组用来抽取不同规则的 chunk vendors: { chunks: 'all', test: /node_modules/,// 条件 priority: -10,// 优先级 ,如果一个 chunk 满足多个缓存组,会被拆分到优先级最高的缓存组中 }, commons: { chunks: 'all', minSize: 0, //最小提取的大小 minChunks: 1, //最小被几个 chunk 引用 priority: -20, reuseExistingChunk: true,// 如果引用了被抽取的 chunk,直接引用这个 chunk,不会重复打包代码 } } }
-
HASH
- 每次 webpack 构建的时候都会生成一个唯一的 hash 值
- 整个编译的过程,所有设置的 HASH,都会共享一个 HASH 值
- 只要有一个值发生变化,所有的文件都会重新生成新的 HASH 值
- 文件资源的缓存方式
- 一般 HTML 不缓存
- 第三方库:强缓存
- 其他资源文件:协商缓存
- 合理使用缓存的话,需要配置 hash,文件更新重新打包,没有更新则用原来的文件资源
output: { path: path.resolve(__dirname, 'dist'), filename: '[name].[hash].js', chunkFilename: '[name].[hash].chunk.js', publicPath: 'http://www.guyal.cn' } // MiniCssExtractPlugin:自行下载引入 plugins: [{ new MiniCssExtractPlugin({ filename: '[name].[hash].css' }) }]
-
ChunkHash:代码块 HASH
- 根据 chunk 生成 hash 值,来自同一个 chunk,则 hash 值就一样
- 代码块的模块发生改变了,HASH 值才会重新生成,否则 HASH 值不会发生变化
output: { path: path.resolve(__dirname, 'dist'), filename: '[name].[chunkhash].js', chunkFilename: '[name].[chunkhash].chunk.js' } // MiniCssExtractPlugin:自行下载引入 plugins: [{ new MiniCssExtractPlugin({ filename: '[name].[chunkhash].css' }) }]
-
ContentHash:代码块 HASH
- 根据内容生成的 hash 值文件
- 文件内容相同,HASH 值就相同
- 内容发生变化的时候,HASH 值才会发生变化
output: { path: path.resolve(__dirname, 'dist'), filename: '[name].[contenthash].js', chunkFilename: '[name].[contenthash].chunk.js' } // MiniCssExtractPlugin:自行下载引入 plugins: [{ new MiniCssExtractPlugin({ filename: '[name].[contenthash].css' }) }]
11. 如何提高 webpack 的构建速度
- 打包速度分析插件:speed-measure-webpack-plugin
- 优化方案
- extensions:在引入文件的时候,可以省略文件的后缀名;用的多的放前边,少的放后面
resolve: { extensions: ['.js', '.jsx', '.json', '.css'] }
- alias:配置绝对路径的别名,可以加快查找模块的速度;
resolve: { alias: { '@': path.resolve(__dirname, '../src') } }
-
externals:外部扩展
-
把项目中使用的包,从打包文件中提取出来,我们需要通过配置
externals
来实现,在HTML
中使用cdn
引入jQuery
-
打包体积减小,打包速度加快
-
配置
externals: { "jQuery", "jQuery" }
- 引入:
import $ from 'jQuery'
-
-
ProvidePlugin:
webpack
自带插件- 这样的配置可以让我们在项目中不用再引入:
import xxx from "xxx"
,直接使用对应的变量即可
webpack.ProvidePlugin({ "要在文件中使用的变量名":"安装的包的名字" })
配置插件
plugins: [ new webpack.ProvidePlugin({ '$': 'jQuery', '$$': 'jQuery' }) ], externals: { "jQuery", “jQuery” }
- 这样的配置可以让我们在项目中不用再引入:
-
resolve.alias
- 配置绝对路径的别名,可以加快模块的查找速度
resolve.alias = { 'xxx': 'xxx 的绝对路径' }
- 配置
resolve: { alias: { '@': path.resolve(__dirname, '../src') } }
- 引入
import xxx from '@/index'
优点
- 可以加快查找文件速度
- 提升构建速度
-
resolve.modules
- 当遇到没有写相对路径文件时,优先去
src
中查找,没有的话在去node_modules
中查找 - 配置:
resolve.modules = [path.resolve(__dirname, 'src'), 'node_modules']
- 当遇到没有写相对路径文件时,优先去
-
module.noParse
- 明确告诉 webpack 这俩包不依赖其他任何包
- 配置
module: { noParse: /jQuery|lodash/ }
-
definePlugin「定义全局变量」
- 定义在编译阶段加上的全局变量
- 配置
webpack.DefinePlugin({ xxx: JSON.stringify("xxx") })
new webpack.DefinePlugin({ BASEURL: "https://baidu.com" }) // BASEURL 此时是一个变量,需要把值转换为字符串: // JSON.stringify("https://baidu.com"), // 输出:"https://baidu.com"
-
thread-loader
- 多进程打包,提升构建速度「以前使用的 happypack 包,已经不维护了」
- 配置
module: { rules: [ { test: /\.js$/, include: path.resolve('src'), use: [ "babel-loader", "thread-loader" ] } ] }
优点:可以提升构建速度;「@vue/cli create-react-app 都已经内置了,平时我们不需要配置」
-
ignorePlugin
- 在打包时,忽略指定的文件「自己知道什么包用不上」
- 例如语言切换的包:moment
- 安装:yarn add moment
- 语言切换的包,但是:所有语言的包都会安装上
- 引用:
new webpack.IgnorePlugin(/local/, /moment/)
- 在打包时,忽略 moment 下的 local 这个文件
- 使用什么包引入什么包:
import 'moment/locale/zh-cn'
- 安装:yarn add moment
-
利用缓存
- babel-loader:开启缓存
- 把 babel-loader 执行的结果缓存起来,重新构建的时候读取缓存,提高打包速度
{ test: /\.js$/, use: [{ loader: 'babel-loader' }] }
- cache-loader
- 把消耗性能的 loader 之前加上 cache-loader,然后将结果缓存到磁盘中
{ test: /\.js$/, use: ['cache-laoder', ...loaders] }
- babel-loader:开启缓存
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!