前端进阶:一文轻松搞定webpack基础知识进阶与调优
Posted 前端知识营地
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端进阶:一文轻松搞定webpack基础知识进阶与调优相关的知识,希望对你有一定的参考价值。
前端进阶:一文轻松搞定webpack基础知识、进阶与调优
文章目录
文章目录
写在前面
Webpack简介
模块打包
资源输入输出
预处理器
代码分片
生产环境配置
写在前面
-
本文知识来源于作者对《webpack实战 入门、进阶与调优》的知识整理,为获得更好的阅读和观看体验,推荐访问我在wolai的读书笔记。webpack知识笔记 -
微信公众号用户可通过文末 阅读原文访问
Webpack简介
-
何为webpack? -
模块打包工具:其核心功能是解决模块之间的依赖,把各个模块按照特定的规则和顺序组织在一起,最终合并为一个或多个 js文件。 -
如果在一个页面中引入多个js文件会有什么缺点? -
通过模块的导入导出语句可以清晰地看到模块之间的依赖关系。 -
模块可以借助打包工具,将多个资源合并后加载。 -
模块之间作用域是相互隔离的,不会存在命名冲突。 -
需要手动维护js文件的加载顺序,如果文件之间有隐性的依赖关系,则很容易出现问题。 -
每个 script
标签都会向服务器请求一次静态资源,过多的请求会严重拖慢网页的渲染速度。 -
每个 script
标签中,顶层作用域都是全局作用域,很容易造成 作用域污染和命名冲突。 模块化可以有效的解决上述问题。 -
webpack的优势? -
支持多种模块标准:AMD / CommonJS / ES6模块。 -
有完备的代码分割解决方案,分割打包后的资源,首屏只加载必要的部分,提升首页渲染速度。 -
可以处理各种类型的资源:css / 图片 等。 -
庞大的社区支持。
模块打包
-
CommonJS -
最初为服务端设计,node.js版本。 -
每个文件即使一个文件,拥有独立的作用域。 -
导出是一个模块向外暴露自己的唯一方式。CommonJS中通过 module.exports导出模块中的内容。 -
CommonJS中使用 require进行模块的导入。 如果导入的模块是第一次被加载,这时会首先执行该模块,然后导出执行后的内容。如果模块曾经被加载过,则直接导出第一次加载时执行后的内容。(相当于是一个静态值了)
-
ES6模块 -
每个文件作为一个模块,每个模块拥有独立的作用域。 -
通过 exports导出 命名导出:exports { a, b } 默认导出:exports default a; (只能导出一个对象)
-
通过**import **导入,默认导出的变量,导入时可以随意命名,命名导出方式,导入时名称必须一致,可以使用as 重命名。 -
AMDAMD 与CommonJS以及ES6模块的最大区别在于AMD的模块加载方式是 异步的。 -
AMD 中使用define函数来定义模块,使用require来引用模块。 -
模块可以并行加载,并不会阻塞浏览器。 -
缺点:语法冗长,回调地狱 -
UMD -
并非模块标准,而是一组模块形式的集合,目标是使一个模块能够运行在各种环境。其手段是根据当前的全局对象中的值判断处于那种环境。当前是AMD环境,就以AMD的形式导出,当前是CommonJS就已以CommonJS的形式导出。 -
UMD一般先判断是否AMD环境。 -
CommonJS 与ES6模块的区别CommonJS 对模块依赖的解决是动态的,而ES6模块是静态的。模块导入时:CommonJS是值拷贝,而ES6则是只读的动态映射。 动态:模块的依赖关系建立在代码运行阶段 静态:模块的依赖关系建立在代码编译阶段
-
CommonJS引入模块时可以动态指定,例如使用if等条件语句引入不同的模块。 -
ES6模块相比CommonJS的优势 -
死代码检测和排除:通过静态分析工具检测出哪些模块没有被调用过。从而在打包时去掉未使用的模块,以减少资源包的体积。 -
模块变量类型检查:JS是动态类型语言,不会在代码执行前检查类型错误,ES6模块属于静态类型模块,有助于确保模块之间的传递的值或者接口类型是正确的。 -
编译器优化:CommonJS无论采用哪种方式,导入的都是一个 对象,而ES6模块直接导入 变量,减少应用层级,程序效率更高。 -
值拷贝与动态映射 -
在导入一个模块时,CommonJS导入的是一份导出值的 拷贝,允许对导入的值进行修改。 -
ES6导出的则是值得 动态映射,且该值是 只读的。(一改全改,但只能在模块内部改动)
资源输入输出
-
webpack 资源入口的作用 -
确定入口模块位置,告诉webpack从哪里开始打包 -
定义chunk name 。如果只有一个入口,那么默认为“main”,如果有多个入口,则需要为每个入口定义chunk name 作为唯一标识。 -
context 的作用 -
资源入口的 路径前缀,在配置时必须使用 绝对路径的形式。使得 entry的配置更加简洁。context可以省略,默认为当前项目的 根目录。 -
如何配置entry -
字符串类型入口: entry:'./src/index.js'
-
数组类型入口: entry:['babel-polyfill','./src/index.js']
( 多个资源预先合并,数组的最后一个元素作为实际的入口路径 ) -
对象类型入口:如果要定义 多入口,则必须使用对象的形式.对象属性名是**chunk name **, 对象的属性值是 入口路径 entry:{
index:['babel-polyfill','./src/index.js'],
lib:'./src/lib.js'
} -
函数类型入口: 返回上述任意一种类型即可. ( 使用函数的优点在于可以设置动态的逻辑来获取工程入口,同时函数支持返回一个 Promise对象来进行异步操作 ) -
如何配置资源出口:output对象 path: 资源的输出位置:是打包完成后资源产生的目录.通常为dist目录 publicPath: 资源的请求位置:指定间接资源的请求位置. |路径|说明|示例| |-|-|-| |html相关|以当前页面HTML 所在的路径加上相对路径,构成资源请求的实际url|// 假定当前HTML页面的路径为https://exmple.com/app/index.html
// 异步加载的资源名为 0.chunk.js
publicPath:"" // 实际路径为 https://exmple.com/app/0.chunk.js
publicPath:"./js" // 实际路径为 https://exmple.com/app/js/0.chunk.js
publicPath:"../assets/" // 实际路径为 https://exmple.com/assets/0.chunk.js
| |Host相关|若当前publicPath以/开始,则表示以当前host name 为基础路径|// 假定当前HTML页面的路径为https://exmple.com/app/index.html
// 异步加载的资源名为 0.chunk.js
publicPath:"/" // 实际路径为 https://exmple.com/0.chunk.js
publicPath:"/js/" // 实际路径为 https://exmple.com/js/0.chunk.js
publicPath:"/assets/" // 实际路径为 https://exmple.com/assets/0.chunk.js| |CDN相关|使用绝对路径配置publicPath.|// 假定当前HTML页面的路径为https://exmple.com/app/index.html
// 异步加载的资源名为 0.chunk.js
publicPath:"http://cdn.com" // 实际路径为 http://cdn.com/0.chunk.js
| -
html 示例 // 假定当前HTML页面的路径为https://exmple.com/app/index.html
// 异步加载的资源名为 0.chunk.js
publicPath:"" // 实际路径为 https://exmple.com/app/0.chunk.js
publicPath:"./js" // 实际路径为 https://exmple.com/app/js/0.chunk.js
publicPath:"../assets/" // 实际路径为 https://exmple.com/assets/0.chunk.js -
Host示例 // 假定当前HTML页面的路径为https://exmple.com/app/index.html
// 异步加载的资源名为 0.chunk.js
publicPath:"/" // 实际路径为 https://exmple.com/0.chunk.js
publicPath:"/js/" // 实际路径为 https://exmple.com/js/0.chunk.js
publicPath:"/assets/" // 实际路径为 https://exmple.com/assets/0.chunk.js -
CDN 相关 // 假定当前HTML页面的路径为https://exmple.com/app/index.html
// 异步加载的资源名为 0.chunk.js
publicPath:"http://cdn.com" // 实际路径为 http://cdn.com/0.chunk.js -
示例: -
filename: 控制输出资源的文件名,可以是一个相对路径. 模板变量:用于动态的为filename 命名. 作用1: 当有多个chunk存在时对不同的chunk进行区分 . 如[name]/ [chunkname] / [id] 对每个chunk来说都是不同的. 作用2: 控制客户端缓存,chunk改变或引起资源的重新加载,从而获取最新内容. ||| |-|-| |变量名称|功能描述| |[hash]|指代webpack此次打包所有资源生成的hash| |[chunkhash]|指代当前chunk内容的hash| |[id]|指代当前chunk的id| |[query]|指代filename配置项中的query| |[name]|指代chunk name ,最常用|
-
path: 指定资源 输出的位置,要求值必须为 绝对路径,webpack4之后默认为 dist目录 -
❗publicPath: 指定资源的 请求位置,注意与path区分.
预处理器
-
loader 概述 -
loader 本质上是一个函数 -
如何引入loader? -
modules.rules代表了模块的处理规则. // 所有css文件都用css-loader/style-loader处理处理
module:{
rules:[{
text:/\.css$/,
use:['style-loader','css-loader'] //先使用css-loader,然后再用style-loader处理,即从右往左处理
}]
} -
loader的常用配置 -
exclude: 用来排除指定的内容,可以使用字符串或者正则, 优先级比include高 -
include:用来包含指定的内容, 只能使用正则. -
issuer: 指定模块的加载者 -
enforce: 指定loader的执行顺序,默认为normal. |类型|说明| |-|-| |pre|在所有loader之前执行| |post|在所有loader之后执行| -
babel-loader -
[ ] babel-loader:使Babel与webpack协同工作的模块 -
[ ] @babel/core:Babel编译器的核心代码 -
[ ] @babel/preset-env: Babel官方推荐的预置器,可以根据用户设置的目标环境自动添加所需要的插件和补丁来编译ES6代码。 -
用途:用来将ES6+代码转换为ES5,使得我们可以在编码中使用最新的特效而不必担心平台兼容性问题。 -
安装: npm install babel-loader @babel/core @babel/preset-env
-
注意事项: -
通过exclude 排除对node_modules的编译 -
使用缓存,避免重复编译 -
禁用模块化转化(不禁用会将ES6模块转化为CommonJS。这将导致Webpack中的tree-shaking特性无法工作)
// babel 的配置示例
rules:{
test:/\.js$/, // 匹配所有的js
exclude:/node_modules/, // 忽略node_modules
use:{
laoder:"babel-loader",// 指定编译器
options:{
cacheDirectory:true,// 开启缓存
presets:[
'env',
{modules:fasle} // 禁用模块转化
]
}
}
}
代码分片
-
代码分片作用?实现代码的按需加载,提升首屏渲染速度。 -
[ ] 开发过程减少模块的重复打包,提升开发速度。 -
[ ] 减少整体资源的体积。 -
[ ] 分片后的代码可以更好的利用客户端缓存。 -
通过入口划分代码。将一些通用的库和不常变动的工具放到一个单独的入口中,由于资源不常变化,因此可以有效的利用缓存,减少资源请求。 -
CommonsChunkPluginwebpack 4 之前的版本可用,webpack 4 之后的版本改用SplitChunks -
提取Vendor: 将Vue/react等框架代码提取出来。 -
设置提取范围:通过chunks配置入口模块。 -
设置提取规则:通过minChunks配置提取规则。 // 配置案例
const webpack = require('webpack');
module.exports = {
entry:{
app:'./app.js',
vendor:['react'],
},
output:{
filename:'[name].js',
},
plugins:[
new webpack.optimize.commonsChunkPlugin({
name:"vendor", // 指定公共chunk的名字
filename:"vendor.js", // 指定提后后的资源文件名
chunks:['a','b'], // 设置提取范围
minChunks: 3, // 只有该模块被引入3次才会被提取为公共模块
})
]
} -
❗ SplitChunks参考文章:Webpack之SplitChunks插件用法详解 -
使用异步加载/按需加载 -
延时加载暂时用不到的模块。 -
webpack中延时加载的两种方式:import函数(推荐)和require.ensure。 -
import:通过js在页面的head标签中 动态插入一个script标签,从而实现动态加载的效果。 // webpack 中import函数的使用方法,注意和ES6模块的import语法做区分
import('./bar.js').then(({add})=>{
console.log(add(2,3));
})
生产环境配置
-
如何让webpack根据不同的环境采用不同的配置? -
使用相同的配置文件:在构建开始前将当前所属的环境作为一个变量传递进去,然后再webpack.config.js中通过条件判断语句使用不同的配置 // package.json
{
...
"script":{
"dev":"ENV=development webpack-dev-server",
"build":"ENV=production webpack"
}
}// webpack.config.js
const ENV = process.env.ENV;
const isProd = ENV==='production';
module.exports = {
output:{
filename:isProd?'bundle@[chunkhase].js':'bundle.js'
},
mode:ENV
} -
为不同的环境创建不同的配置文件:将配置新进不同的配置文件中,根据环境加载对应的配置文件。 // 开发环境配置:webpack.dev.config.js
// 生产环境配置:webpack.pro.config.js
// package.json
{
...
// 通过--congig 读取不同的配置文件
"scripts":{
"dev":"webpack-dev-server --config=webpack.dev.config.js",
"build":"webpack --config= webpack.pro.config.js"
}
} -
如何开启production模式?webpack 4 以上提供了**mode **参数,可以通过它直接切换打包模式。 -
如何为生产环境和本地环境添加不同的环境变量?webpack中可以使用DefinePlugin进行设置
上述代码中,必须使用// webpack.config.js
const webpack = require('webpack');
module.exports={
entry:'./app.js',
output:{
filename:'bundle.js'
},
mode:'production',
plugins:[
new webpack.DefinePlugin({
ENV:JSON.stringfy('prodution')
})
]
}JSON.stringfy
,因为替换环境变量时是对字符串类型的值进行 完全替换。加入不使用JSON.stringfy
,在替换后就会成为变量名而不是字符串。 -
source-map -
webpack 如何配置source map -
源码映射,帮助调试和定位错误,通常后缀 .map
-
打开浏览器开发者工具时就会加载对应的源码文件,不打开则不加载 module.exports = {
...
devtool:"source-map" // 开启源码视图
} -
JS资源压缩 -
JS压缩工具:UglifyJS (webpack 3 已集成) / terser (webpack 4 已集成) // webpack 4 配置压缩
module.exports = {
entry:"./app.js",
output:{
filename:"bundle.js",
},
optimization:{
minimize:true // 启用压缩
}
} -
CSS资源压缩 -
压缩css的步骤:压缩css的前提是使用 extract-text-webpack-plugin / mini-css-extract-plugin 提取样式,然后使用 optimize-css-assets-webapck-plugin
进行压缩 。const ExtractTextPlugin = require('extract-text-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webapck-plugin');
module.exports = {
module:{
rules:[
{
test:/\.css$/,
use:ExtractTextPlugin.extract({
fallback:'style-loader',
user:'css-loader',
})
}
]
},
plugins:[new OptimizeCssAssetsPlugin({
assetNameRegExp:/\.optimize\.css$/, // 生效范围
cssProcessor:require('cssnano'), // 压缩处理器,默认为cssnano
cssProcessorOptions:{ // 压缩处理器的配置
discardComments:{
remove:all
}
},
canPrint:true // 使是否显示log
})]
}
参考文章:
附录 Webpack之SplitChunks插件用法详解
以上是关于前端进阶:一文轻松搞定webpack基础知识进阶与调优的主要内容,如果未能解决你的问题,请参考以下文章
web前端进阶知识点 React专精webpack网络协议音视频等