webpack
常见题
前端为何要构建和打包
- 统一、高效的开发环境
- 统一的构建流程和产出标准
- 集成公司构建规范(提测、上线…)
- 体积更小(Tree-Shaking、压缩、合并),加载更快
- 编译高级语言或语法(TS,ES6+,模块化,scss)
- 兼容性和错误检查(Polyfill、postcss、eslint)
webpack
webpack是一个模块化打包JS的工具,在webpack中一切皆是模块。
webpack核心概念:enrty、module、chunk、loader、plugin
module、chunk、bundle 分别是什么意思,有何区别?
- module:各个源码文件,webpack中一切皆模块
- chunk:webpack处理的代码块,可由多个module模块合并成的(entry、import()、splitChunk都产出chunk)
- bundle:webpack最终打包输出文件
module,chunk 和 bundle 其实就是同一份逻辑代码在不同转换场景下的取了三个名字:
我们直接写出来代码文件的是 module,webpack 处理时是 chunk,最后生成浏览器可以直接运行的 bundle。
webpack如何实现懒加载?
import()
函数require.ensure()
- 结合Vue React 异步组件 react 通过react.lazy()
- 结合Vue-router React-router 异步加载路由
babel-runtime和babel-polyfill的区别?
babel-polyfill
相当于补丁,es6所有新语法和API集合,核心是由core.js、regenerotor 组成
但babel-polyfill会污染全局环境,babel-runtime
可以帮助ployfill解决这个问题
loader和plugin的区别
- loader模块
转换器
,如less->css - plugin
扩展
webpack功能的插件,如HtmlWebpackPlugin
常见loader以及他们的作用:
babel-loader
:把 ES6 转换成 ES5file-loader
:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件 (处理图片和字体)url-loader
:与 file-loader 类似,区别是用户可以设置一个阈值,大于阈值会交给 file-loader 处理,小于阈值时返回文件 base64 形式编码 (处理图片和字体)sass-loader、less-loader
:将SCSS/SASS/less代码转换成CSScss-loader
:加载 CSS,支持模块化、压缩、文件导入等特性style-loader
:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSSpostcss-loader
:扩展 CSS 语法,使用下一代 CSS,可以配合 autoprefixer 插件自动补齐 CSS3 前缀vue-loader
:加载 Vue.js 单文件组件ts-loader
: 将 TypeScript 转换成 JavaScriptimage-loader
:加载并且压缩图片文件
常见plugin以及他们的作用:
ignore-plugin
:忽略部分文件html-webpack-plugin
:生成html文件,同时把构建好入口js文件引入到生成的html文件中clean-webpack-plugin
: 目录清理HappyPack
:多进程打包webpack-parallel-uglify-plugin
: 多进程执行代码压缩,提升构建速度HotModuleReplacementPlugin
:webpack自带插件,实现热更新mini-css-extract-plugin
: 分离样式文件,CSS 提取为独立文件,抽离css到单独文件dllplugin
:动态链接库ModuleConcatenationPlugin
:开启Scope Hostingterser-webpack-plugin
:压缩jsoptimize-css-assets-webpack-plugin
:压缩css
babel和webpack的区别
- babel是JS新语法编译工具,不关心模块化,负责对其他类型的资源进行转译的工作。
- webpack是打包构建工具,是多个loader plugin 的集合
为什么Proxy不能被ployfill
像class 可以用 function 模拟,promise 可以用callback 模拟,但proxy 的功能用object.defineProperty 无法模拟
webpack执行流程
webpack从启动到结束会依次执行以下流程:
初始化参数:解析webpack配置参数(读取与合并参数),生产 Compiler(编译) 实例
注册插件:调用插件的apply方法,给插件传入compiler实例的引用,插件通过compiler调用Webpack提供的API,让插件可以监听后续的所有事件节点。
开始编译:读取入口文件,加载所有配置的插件,执行对象的 run 方法开始执行编译
解析文件:使用loader将文件解析成抽象语法树 AST
生成依赖图谱:找出每个文件的依赖关系(遍历)
输出:根据转换好的代码,生成 chunk
生成最后打包的文件
ps:由于 webpack 是根据依赖图动态加载所有的依赖项,所以,每个模块都可以明确表述自身的依赖,可以避免打包未使用的模块。
webpack核心概念
Entry(入口):Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。
Module(模块):在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
Chunk(代码块):webpack处理的代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
Loader(模块转换器):用于把模块原内容按照需求转换成新内容。
Plugin(扩展插件) :在 Webpack 构建流程中的特定时机会广播出对应的事件,插件可以监听这些事件,并改变输出结果
webpack 最出色的功能之一就是,除了 JavaScript,还可以通过 loader 引入任何其他类型的文件。
多入口
- entry 中设置多个入口
- output 根据多个入口产出多个js
- plugins 中 HtmlWebpackPlugin生成html,并给每个html 插入 js
抽离css
- 下载
MiniCssExtractPlugin
抽离css到单独文件,并配置webpack.prod.js的 plugins - module.rules中涉及css 的部门,配置loader
- optimization优化 optimize-css-assets-webpack-plugin压缩css
公共代码、第三方代码
在optimization . splitChunks 进行分割
在 HtmlWebpackPlugin . chunks 中进行引入 js
热更新步骤
webpack的热更新又称热替换(Hot Module Replacement),缩写为HMR。 这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。
首先要知道server端和client端都做了处理工作
-
第一步 webpack 对文件系统进行 watch 打包到内存中
在 webpack 的 watch 模式下,文件系统中某一个文件发生修改,webpack 监听到文件变化,根据配置文件对模块重新编译打包,并将打包后的代码保存在内存中。(不生成文件的原因就在于访问内存中的代码比访问文件系统中的文件更快,而且也减少了代码写入文件的开销。)
-
第二步 devServer 通知浏览器端文件发生改变
当我们在启动 devServer 的时候,sockjs 在服务端和浏览器端建立了一个 webSocket 长连接,将 webpack 编译和打包的各个阶段状态告知浏览器,浏览器端根据这些 socket 消息进行不同的操作。
-
第三步 webpack-dev-server/client 接收到服务端消息做出响应
在dev-server/client 端负责消息的传递而不负责新模块的获取,而把这些工作又交回给了webpack,webpack/hot/dev-server 的工作就是根据dev-server/client 传给它的信息以及 dev-server 的配置决定是刷新浏览器呢还是进行模块热更新。
-
第四步 webpack 接收到最新 hash 值验证并请求模块代码
热更新由HMR runtime来实现,HotModuleReplacement.runtime (简称 HMR runtime)接收到传递给他的新模块的 hash 值,检测是否有新的更新,检查过程中通过 JsonpMainTemplate.runtime(简称 jsonp runtime) 向 server 端发送 Ajax 请求,服务端返回一个 json,该 json 包含了所有要更新的模块的 hash 值,然后该模块再次通过 jsonp 请求,获取到最新的模块代码。
-
第五步 更新模块间的依赖引用
HMR将会对新旧模块进行对比,决定是否更新模块,在决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用。
-
最后一步,当 HMR 失败后,回退到 live reload 操作,也就是进行浏览器刷新来 取最新打包代码。
webpack与grunt、gulp的不同?
三者都是前端构建工具,grunt和gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量化的任务还是会用gulp来处理,比如单独打包CSS文件等。
grunt和gulp是基于任务和流(Task、Stream)的。类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。
webpack 是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。
webpack生产优化
优化产出代码,提升产品性能
-
优化babel-loader(ES6,7->ES5)
- 开启缓存
- 明确范围
-
bundle打hash(文件内容无变化即hash值不变,命中缓存,加载更快)
-
小图片用base64编码
-
ignorePlugin(避免)
忽略第三方包里的指定目录,让指定的目录不被打包。如果代码中用到可单独引入
-
noParse(忽略依赖)
webpack在处理时会判断是否有依赖包,而对于JQuery、lodash这种类库一般不会引入其他包,所以添加noParse不去解析模块中的依赖关系,提高打包效率
-
happyPack(多进程)
项目大,打包慢,开启多进程能提高速度
-
ParallelUglifyPlugin(多进程+压缩)
多进程压缩
-
CDN加速
设置 publicPath 属性给引用包的地方添加前缀 ,上传包到CDN服务器
(CDN:部署在各地的边缘服务器负载均衡,用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率)
-
启用production模式
- 自动压缩代码
- vue、react等会自动删除调试代码
- 启动tree-shaking(没用到的函数打包时会被删除)
-
Scope Hosting
将多个函数合并起来,在一个函数中执行,作用域数量少了,不需要频繁跨作用域。
-
抽离css
MiniCssExtractPlugin
抽离css到单独文件 -
抽离公共代码、抽离第三方模块
配置optimization的splitChunks抽离代码
webpack 开发优化
优化构建速度,提高开发体验和效率
-
自动刷新
-
热更新(不刷新能更新)
-
DllPlugin(动态链接库)
利用
DllPlugin
和DllReferencePlugin
预编译资源模块,DllPlugin
来对那些我们引用但是绝对不会修改的npm包来进行预编译,DllReferencePlugin
将预编译的模块加载进来。
ES6 Moudle和commonjs引入区别
- ES6 Moudle 是静态引入,编译时引入。
- commonjs 是动态引入,执行时引入。
webpack基本配置
拆分配置和marge
- webpack.common.js 公共配置
- webpack.dev.js 开发配置
- webpack.prod.js 生产配置
开发打包
webpack.common.js + webpack.dev.js
生产打包
webpack.common.js + webpack.prod.js
module.exports = {
module: {
rules: [
{
test: /\.js$/,
loader: "babel-loader",
//include和exclude用一项即可
//include:path.resolve(__dirname,'src'),
exclude: /node_modules/
},
{
test: /\.css$/,
//loader的解析都是从右至左的
loader: ["style-loader", "css-loader", "postcss-loader"]
//postcss-loader:兼容不同浏览器,自动添加前缀。
//可创建postcss.config.js配置文件专门定义 postcss-loader 的配置
//style-loader:把css放到页面style标签
},
{
test: /\.less$/,
loader: ["style-loader", "css-loader", "less-loader"]
},
]
},
};
postcss.config.js: postcss-loader的配置文件
//下载autoprefixer引入
module.exports = {
plugins: [require('autoprefixer')]
}
同时配合babel-loader的也有配置文件(.babelrc)。在Babel编译时会读取.babelrc里的设置处理代码。
假设dev配置如下:
module.exports = {
rules: [
{
test: /\.(png|jpg|gif|jpeg)$)/,
use: "file-loader",
},
}
假设prod配置如下:
module.exports = {
rules: [
{
test: /\.(png|jpg|gif|jpeg)$)/,
use: {
loader:"url-loader",
options:{
limit:5*1024,
outputPath:'/img/'
}
},
},
}
//url-loader:功能类似于 file-loader,但是在文件大小(单位 byte)低于limit时,图片以base64产出;否则依然用file-loader产出格式。
配置多入口
三步:
- entry 中设置多个入口
- output 根据多个入口产出多个js
- plugins 中 HtmlWebpackPlugin给每个html 插入 js
//给两个入口index.html,other生成不同的js文件
entry:{
index:path.join(srcPath,'index.js'),
other:path.join(srcPath,'other.js')
},
output:{
filename:'[name].[contentHash:8].js',
path:disPath
},
plugins:[
//每个入口都生产HtmlWebpackPlugin实例
new HtmlWebpackPlugin({
template:path,join(srcPath,'index.html'),
filename:'index.html',
chunks:['index']
//chunks属性意思为index.html引入index.js文件
//chunks指定.html可引入的js文件。chunks选项可以从entry中选择。
}),
new HtmlWebpackPlugin({
template:path,join(srcPath,'other.html'),
filename:'other.html',
chunks:['other']
//chunks属性意思为other.html引入other.js文件
})
]
chunks
chunks
entry的文件将生成chunks
抽离css文件
抽离css文件,通常用在prod环境中,dev环境没必要抽离css,用style-loader就可以。
- 下载
MiniCssExtractPlugin
抽离css到单独文件,并配置webpack.prod.js的 plugins - module.rules中涉及css 的部门,配置loader
- optimization优化压缩js、css代码 【terser-webpack-plugin 压缩js】【optimize-css-assets-webpack-plugin压缩css】
webpack.prod.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');抽离css
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');压缩css
const TerserPlugin = require('terser-webpack-plugin');//压缩js
//module.rules
rules:[
{
test:/\.css$/,
loader:[
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader'
]
},
{
test:/\.less/,
loader:[
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
'postcss-loader'
]
}
]
plugins:[
//把css代码抽离到css/main.**.css文件中
new MiniCssExtractPlugin({
filename:'css/main.[contentHash:8].css'
})
]
//优化
optimization:{
minimizer:[new TerserPlugin(),new OptimizeCssAssetsPlugin()]
}
抽离公共代码、抽离第三方模块
通常在prod 生产环境。将多次被引入的文件抽离出来。
optimization:{
//分割代码块
splitChunks: {
chunks: 'all',//导入文件类型:处理所有导入文件
//缓存分组
cacheGroups: {
//第三方模块
vendor: {
name:'vendor',//chunk名称
test: /[\\/]node_modules[\\/]/,//匹配模块路径
priority: 1,//值越大权限越大,越先被抽离
minSize: 30000,//大小限制,体积>30kb抽离
minChunks: 1,//最少复用次数,用过一次就抽离
},
//公共模块
common: {
//文件体积size>=2kb&&被引用次数>=2的文件都抽离打包到common.js
name:'common'
minChunks: 2,
priority: 0,
minSize:2*1024,
minChunks: 2
}
}
}
}
chunks
chunks
代码块
目前为止涉及产出chunks的有entry、splitChunks
多入口自定义引入chunk
针对前面配置的多入口,可稍作修改,假设other没有用到第三方模块代码(vendor),那我们可在HtmlWebpackPlugin里不做vendor代码块的引入。
plugins:[
//每个入口都生产HtmlWebpackPlugin实例
new HtmlWebpackPlugin({
template:path,join(srcPath,'index.html'),
filename:'index.html',
chunks:['index','vendor','common'] //****
}),
new HtmlWebpackPlugin({
template:path,join(srcPath,'other.html'),
filename:'other.html',
chunks:['other','common'] //****
})
]
动态加载(懒加载)
不需在webpack配置。
webpack 提供了两个类似的技术。
第一种,也是推荐选择的方式是,使用符合 ECMAScript 提案 的 import()
语法 来实现动态导入。
// import(".../../..")
//可以这样
import(".../../..").then(res=>{res.default.**});
//注意得到的数据是存在.then的回调函数的res.default属性里
//(vue)可以这样
const UserDetails = () => import(/* webpackChunkName: "group-user" */ './views/UserDetails')
第二种,则是 webpack 的遗留功能,使用 webpack 特定的 require.ensure
。
//require.ensure()参数含义
require.ensure(
[], // 依赖
() => {// 回调函数,在这里按需引入模块和处理逻辑
require('@/components/home')
},
'demo') // webpackChunk名,同名的打包进同个bundle
)
https://webpack.docschina.org/guides/code-splitting/#dynamic-imports
// r就是resolve
const list = r => require.ensure([], () => r(require('../components/list/list')), 'list');
// 路由也是正常的写法 这种是官方推荐的写的 按模块划分懒加载
const router = new Router({
routes: [
{
path: '/list/blog',
component: list,
name: 'blog'
}
]
})
vue-router 路由懒加载
https://router.vuejs.org/zh/guide/advanced/lazy-loading.html
const UserDetails = () => import(/* webpackChunkName: "group-user" */ './views/UserDetails')
//用特殊的注释语法来提供 chunk name
const router = createRouter({
// ...
routes: [{ path: '/users/:id', component: UserDetails }],
})
React 路由懒加载 React.lazy()
//使用React.lazy导入OtherComponent组件
const OtherComponent = React.lazy(() => import('./OtherComponent'));
chunks
异步加载也会生产chunk
处理JSX、Vue
//JSX
下载@babel/preset-react
在.babelrc中presets:[“@babel/preset-react”]
webpack的babel-loader执行时会读取.babelrc文件配置进行解析代码
//Vue
rules:[
{
test:/.vue/,
loader:[‘vue-loader’],
include:srcPath
}
]
详细内容百度吧
module、chunk、bundle的区别
- module:各个源码文件
- chunk:代码块可由多个module模块合并成的(entry、import()、splitChunk都产出chunk)
- bundle:最终输出文件
webpack性能优化
- 开发环境上:优化构建速度,提高开发体验和效率
- 生产环境上:优化产出代码,提升产品性能
常见优化构建速度
- 优化babel-loader(ES6,7->ES5)
- ignorePlugin(避免)
- noParse(忽略)
- happyPack(多进程)
- ParallelUglifyPlugin(多进程+压缩)
- 自动刷新
- 热更新(不刷新能更新)
- DllPlugin(动态链接库)
其中可用于生产环境的有:1、2、3、4、5
开发环境的有:6、7、8
优化babel-loader
- 开启缓存
- 明确范围
{
test: /\.js$/,
use: ["babel-loader?cacheDirectory"],//开启缓存
exclude: /node_modules/,//明确范围(exclude和include二选一)
},
ignorePlugin
webpack内置插件ignorePlugin,作用是忽略第三方包里的指定目录,让指定的目录不被打包
例如:momont时间格式化依赖包中,在使用时,通常只用到中文,默认全部语言都会被打包,所以忽略momont中所有语言包,在用到中文的地方单独引入依赖,如此一来既可以显示中文,又能忽略不必要的包。
plugins:[
//momont库里 ./locale 目录内容打包时会被忽略
new Webpack.IgnorePlugin(/\.\/locale/,/momont/)
]
import momont from 'momont';
//代码用到时,要单独引入
import 'momont/locale/zh-cn';
momont.locale('zh-cn');
noParse
module的属性
对于某些类库本身不存在依赖包,所以不解析模块中的依赖关系,提高打包效率
场景:webpack在处理时会判断是否有依赖包,而对于JQuery、lodash这种类库一般不会引入其他包,所以添加noParse不去解析
module:{
noParse:/jquery|lodash/,//不去解析jquery、lodash中的依赖库
}
happyPack多进程打包
项目大,打包慢,开启多进程能提高速度
项目小,打包快,开启多进程会降低速度(产生进程开销)
所以看情况开启
//下载happyPack
let HappyPack = require('happypack');
module.exports = {
...
module:{
rules:[
{
test:/\.js$/,
//将js文件的处理交给id为babel的HappyPack实例
use:'HappyPack/loader?id=babel',
exclude: /node_modules/
}
]
},
plugins:[
new HappyPack({
id:'babel',//标识符id
loader:['babel-loader?cacheDirectory'],
})
]
}
ParallelUglifyPlugin 多进程压缩
webpack自带压缩配置UglifyJS,但它是单线程压缩,需要下载webpack-parallel-uglify-plugin 插件(原理同HappyPack)实现多进程。
所以是用UglifyJS压缩,webpack-parallel-uglify-plugin多进程
// 引入 ParallelUglifyPlugin 插件
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
plugins:[
new ParallelUglifyPlugin({
// 传递给 UglifyJS的参数如下:
uglifyJS: {
output:{
beautify: false,//紧凑输出
comments: false//删除注释
},
compress:{
drop_console: true,//删除console
collapse_vars: true,//内嵌定义只用到一次的变量
reduce_vars: true,//提取出现了多次但是没有定义成变量去引用的静态值,
//var a=1,b=2;var c=a+b => var c=3
}
}
})
]
自动刷新
整个页面全部刷新,速度较慢,状态丢失。
devServer有自动刷新功能
热更新
所谓的热更新指对数据变化的局部进行更新,页面不刷新,状态不丢失。
- 配置webpack
- 需热更新的代码添加热更新判断
//1.添加webpack的内置插件HotModuleReplacementPlugin
const HotModuleReplacementPlugin=require('webpack/lib/HotModuleReplacementPlugin');
module.exports = {
devServer:{
port:3001,
open:true,
contentBase: './dist',
//3.启用热更新
hot:true,
},
...
plugins:[
//2.热更新插件
new HotModuleReplacementPlugin(),
]
}
//是否热更新
if(module.hot){
module.hot.accept('./resource.js',() => {
//如果'./resource.js'修改代码了,那么在热更新完成之后可以在回调函数里做一些事情
console.log('文件更新了');
let str = require('./resource.js');//不能使用import,因为import只能写在页面顶端
console.log(str.default);
})
}
DllPlugin 动态链接库
作用:因架构稳定,不常升级版本,同版本一般只构建一次,不需每次构建,在开发环境产出一次dll即可。
例如像react、react-dom、vuex、vue-router …框架依赖包,都可以打成dll 文件包,然后动态链接引入即可。如果第二次打包,那么发现react和react-dom已经被打包好了,那么就不需要再打包了,这样就大大提升了性能。
总结流程:
1.使用webpack内置DllPlugin,打包生成dll文件
2.在webpack.dev.js 中使用DllReferencePlugin引入dll索引文件(.manifest.json)
3.最后在主页面引入生成的dll.js文件
创建一个webpack.dll.js
打包脚本
const DllPlugin = require('webpack/lib/DllPlugin');
module.exports = {
entry:{
//将react相关模块放到单独的动态链接库
react:['react','react-dom']
},
output:{
filename:'[name].dll.js',//产生的文件名_dll_react.js
path:path.resolve(__dirname,'dist'),
//存放dll库的全局变量名,react->_dll_react,前面加_dll_为防全局变量冲突
library:'_dll_[name]'
},
plugins:[
//接入DllPlugin
new DllPlugin({
name:'_dll_[name]',//name要与output中的library同名
path:path.resolve(__dirname,'dist','[name].manifest.json'),
//rect.manifest.json文件中name字段为_dll_react,也就是name属性
})
]
}
webpack.dev.js
//使用DllReferencePlugin告诉有哪些dll
plugins:[
new webpack.DllReferencePlugin({
//索引位置
manifest:path.resolve(__dirname,'dist','react.manifest.json')
}),
]
主页面引入
//index.html
<script src="./react.dll.js"></script>
生产优化
目标:
- 体积更小
- 合理分包,不重复加载
- 速度更快,内存使用更少
优化:
- 小图片用base64编码
- bundle打hash(文件内容无变化即hash值不变,命中缓存,加载更快)
- 懒加载
- 提取公共代码
- ignorePlugin
- CDN加速
- 使用production
- scope Hosting (改作用域)
CDN加速
- 设置 publicPath 属性给引用包的地方添加前缀
- 上传包到CDN服务器
//可给js,css,img 设置
publicPath: 'http://cdn.abc.com'
最终生成文件中效果
<script src="http://cdn.abc.com/common.js"></script>
使用production
mode:production可以实现
- 自动压缩代码
- vue、react等会自动删除调试代码
- 启动tree-shaking(没用到的函数打包时会被删除)
注意:必须使用ES6 Module Tree-shaking才能生效;commonjs不能生效
module.exports = {
mode:'production'
}
Tree-shaking 必须是用ES6 Module 方式引入的原因,可以通过他们的导入区别看出
ES6 Moudle和commonjs引入区别
ES6 Moudle 是静态引入,编译时引入。
commonjs 是动态引入,执行时引入。
只有 ES6 Moudle 才能静态分析,实现Tree-shaking,因为commonjs 动态引入,打包时不能明确
if(){
list=require("...")
}
//编译Error ,ES6 Moudle 是能静态引入
if(){
import list from "..."
}
Scope Hosting
将多个函数合并起来,在一个函数中执行,用于自动简化代码,减少变量;
场景:
两个文件A.js、B.js 并且 B引用 A 。
在未开启Scope Hosting 前会产出A、B两个包;开启后,只会生成一个包,包中有A、B两个内容,作用域数量少了,不需要频繁跨作用域。
好处:
- 代码体积更小
- 创建函数作用域更少
- 代码可读性更好
步骤:
- 引入插件,开启(第三方模块开启条件要采用ES6的模块化语法,如果没采用ES6模块化语法可能无法实现)
- 设置开启条件 mainFields
const ModuleConcatenationPlugin =require('webpack/lib/optimize/ModuleConcatenationPlugin');
module.exports={
resolve:{
//针对 NPM的第三方模块优化采用‘jsnext:main’ 中指向的ES6模块化语法文件
mainFields:['jsnext:main','browser','main']
},
plugins:[
//开启 Scope Hosting
new ModuleConcatenationPlugin()
]
}
Babel
将ES6代码转为可兼容的JS代码。(语法解析)
webpack中babel-loader会读取.babelrc文件配置,去处理JS代码。
开发环境devDependencies
- @babel/cli 命令行工具
- @babel/core
- @babel/plugin-transform-runtime
- @babel/preset-env
生产环境dependencies
- @babel/polyfill
- @babel/runtime
.babelrc文件
文件内容分了两部分:
- presets
- plugins
//基本格式如下
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"debug": true
}
]
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 3 // 指定 runtime-corejs 的版本,目前有 2 3 两个版本
}
]
]
}
presets
presets(预设):babel插件组合
@babel/preset-env
解析语法插件,它包含常用的ES6语法,像箭头函数等语法都可用它解析。
如果preset中还不能满足,可以在plugins添加插件。
注意
promise 等 语法和**新的API **需要polyfill 处理
@babel/polyfill
补丁,所有新语法和API集合,它核心是由core.js、regenerotor 组成
core.js 含新语法和API,但不支持generator函数,所有regenerotor 填补corejs的空缺。
在Babel7.4后弃用babel-polyfill,推荐直接用core.js、regenerotor 。但面试还会考babel-polyfill。
注意
Babel只做语法解析(ES5的语法规范),不处理模块化(如:import操作),所以需要配合webpack处理
@babel/polyfill按需引入
{
"presets": [
[
"@babel/preset-env",
//@babel/polyfill按需引入
{
"useBuiltIns": "usage",//按需
"corejs": 3//corejs版本
}
]
],
}
//polyfill核心是corejs,直接用corejs
@babel/polyfill存在问题
污染全局环境
解决方案:@babel/plugin-transform-runtime(开发环境),@babel/runtime(生产环境)
@babel/plugin-transform-runtime、@babel/runtime
- 下载@babel/plugin-transform-runtime,@babel/runtime。前者运行在编译时,后者运行在运行时
- 配置
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"helpers": true,
"regenerator": true,
"useESModules":false,
"corejs": 3 // 指定 runtime-corejs 的版本,目前有 2 3 两个版本
}
]
]
}