1、前端代码为什么要进行构建和打包?
代码层面:
- 可以使代码体积更小(tree-shaking,压缩,合并),加载更快
- 可以编译高级语言或语法(TS,ES6+,模块化,scss)
- 可以检查错误及实现兼容性(polyfill,postcss,eslint)
开发流程层面:
- 可以统一高效的开发环境
- 可以统一构建流程和产出标准
- 可以集成公司的构建规范(提测,上线)
2、webpack的一些核心概念?
- entry:入口,指示webpack应该使用哪个模块来作为构建其内部依赖图的开始
- output:输出结果,告诉webpack在哪里输出它所创建的bundle,以及如何命名这些文件
- module:webpack里一切皆模块,一个模块对应一个文件,webpack会从配置的entry开始递归找出所有依赖的模块
- chunk:代码块,一个chunk由多个模块组合而成,用于代码合并与分割。
- loader:模块转换器,让webpack能够去处理js,json以外的其他类型文件,并将他们转化为有效模块
- plugin:扩展插件,在webpack运行生命周期会广播出许多事件,plugin可以监听这些事件,在合适的时机通过webpack提供的API改变输出结果。例如打包优化,资源管理
- mode:模式,告知webpack使用相应模式的内置优化
3、loader和plugin的区别是什么?常见的loader和plugin有哪些?
- loader:模块转化器,例如less-loader可以将less转化成css。ts-loader,babel-loader,file-loader等
- plugin:扩展插件,例如 html-webpack-plugin生成html文件,例如clean-webpack-plugin删除output.path中文件,mini-css-extract-plugin抽离css, hot-module-replacement-plugin配置热更新splitChunksPlugin进行代码分割等。
- loader运行在打包文件之前,plugins在整个编译周期都会起作用
4、webpack和Babel有什么区别?
- Babel是js新语法编译工具,不关心模块化
- webpack是打包构建工具,是多个loader和plugin的集合
5、webpack常见的性能优化有哪些?
- 优化babel-loader
- 使用ignorePlugin
- 使用noparse
- 使用happyPack多进程打包
- 使用parallelUglifyPlugin多进程压缩
- 自动刷新/热更新 (开发环境)
- DllPlugin (开发环境)
- 小图片base64编码
- bundle加hash
- 懒加载
- 提取公共代码(splitChunk)
- IgnorePlugin
- 使用production
- 使用scope Hoisting
6、如何产出一个lib?
output:{
// lib的文件名
filename:'lodash.js',
// 输出lib到dist目录下
path:distPath,
// lib的全局变量名
library:'lodash'
}
7、webpack如何抽离并压缩css文件?
通过MiniCssExtractPlugin.loader(不再用style-loader)
plugins选项中:
plugins:[
new CleanWebpackPlugin(), // 每次都清空output.path 文件夹
new MiniCssExtractPlugin({
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
}),
]
optimization选项中:
optimization:{
minimizer:[
new TerserPlugin({}),
new CssMinimizerPlugin(),
]
}
8、webpack如何配置多入口?
entry里配置多个入口
entry:{
index:path.join(srcPath,'index.js'),
other:path.join(srcPath,'other.js')
}
output: name变量区分多入口
output:{
filename:'[name].[contentHash:8].js',
path:distPath
}
plugins: 配置多入口
plugins:[
new HtmlWebpackPlugin({
template:path.join(srcPath,'index.html'),
filename:'index.html',
chunks:['index']. // 只引用index.js
}),
new HtmlWebpackPlugin({
template:path.join(srcPath,'other.html'),
filename:'other.html',
chunks:['other']. // 只引用other.js
})
]
9、webpack如何懒加载?
import('./danamic.js').then(res=>{
})
// 会返回一个promise
10、webpack如何处理jsx和vue?
react使用@babel/preset-react. + babel-loader
vue则使用vue-loader
11、module,chunk,bundle分别是什么,有什么区别?
module:各个源码文件,webpack中一切皆模块,能被import或者export的js,css,图片
chunk:多模块合并成的,还在内存中没产出的。当module源文件传到webpack打包时,webpack会根据文件引用关系生成chunk文件,webpack会对chunk文件进行一些操作
bundle: 最终的输出文件。webpack处理好chunk文件后,最终会输出bundle文件,这个bundle文件包含了经过加载和编译的最终源文件,所以它可以直接在浏览器中运行。
12、webpack优化构建速度的方式
- 优化babel-loader: ①开启缓存 ② 明确范围
-
{ test:/\.js$/, use:['babel-loader?cacheDirectory'], // 开启缓存 include:path.resolve(__dirname,'src'), // 明确范围 exclude:path.resolve(__dirname,'node_modules') // 二者选其一即可 }
- 使用ignorePlugin 避免引入无用模块 (直接不引入,代码中没有)
- 使用noParse避免重复打包。(例如 XXX.min.js) (引入但不打包)
- 使用happyPack多进程打包
- 使用ParallelUglifyPlugin多进程压缩js
- 使用HotModuleReplacementPlugin开启热更新(仅开发环境)
- 使用DllPlugin动态链接库插件 (仅开发环境)
13、happyPack是什么?
多进程打包。提升打包速度。
{
test:/\.js$/,
use:['happypack/loader?id=babel'],
include:path.resolve(__dirname,'src'),
}
new HappyPack({
id:'babel',
loaders:['babel-loader?cacheDirectory']
})
14、ParallelUglifyPlugin是什么?
多进程压缩输出js代码
new ParallelUglifyPlugin({
uglifyJS:{
output:{
beautify:false, // 最紧凑的输出
comments:false, //删除所有注释
},
compress:{
drop_console:true,
collapse_vars: true,
}
}
})
15、webpack如何配置热更新?
自动刷新:保存了代码之后,整个网页全部刷新,状态会丢失
热更新:新代码生效,但网页不刷新,状态不丢失
配置了devSever选项后,默认就会开启自动刷新。
const HotModuleReplacementPlugin = require('webpack/lib/HotModuleReplacementPlugin')
module.exports = merge(webpackCommonConfig,{
mode:'development',
entry:{
index:[
'webpack-dev-server/client?http://localhost:8080',
'webpack/hot/dev-server',
path.join(srcPath,'index.js')
],
},
module:{
rules:[
{
test:/\.js$/,
loader:['babel-loader?cacheDirectory'],
include:srcPath
}
]
},
plugins:[
new webpack.DefinePlugin({
ENV:JSON.stringify('development')
}),
new HotModuleReplacementPlugin(), //. ------配置热更新插件
],
devServer:{
port:8080,
progress:true,//显示打包进度条
contentBase:distPath, //根目录
open:true,//自动打开浏览器
compress:true, // 开启gzip压缩
hot:true, // -----------开启热更新
proxy:{ // 配置代理
/**********将本地/api/xxx代理到 localhost:3000/api/xxx **/
'/api':'http://localhost:3000',
/*****将本地的 /api2/xxx代理到 localhost:3000/xxx. ***/
'/api2':{
target:'http://localhost:3000'
}
}
}
})
16、什么是DllPlugin?
前端框架如Vue,React项目体积大,构建慢;但是他们的版本比较稳定,不常升级。使用DllPlugin则同一个版本只需构建一次即可,不用每次都重新构建。
webpack已经内置了DllPlugin
DllPlugin -- 打包出dll文件
DllReferencePlugin -- 使用dll文件
17、webpack性能优化产出代码的方式
体积更小;合理分包,不重复加载;速度更快,内存使用更小
①小图片base64
{
test:/\.(png|jpg|jpeg|gif)$/,
use:{
loader:'url-loader',
options:{
// 小于5kb的图片用base64格式产出,否则依然用file-loader的形式产出URL格式
limit: 5 * 1024,
outputPath:'/img1/'
}
}
}
②bundle加hash
根据文件的内容算出一个8位的hash值,文件内容发生变化,hash值就会变,hash值不变则会命中缓存
filename:'[name].[contentHash:8].js'
③懒加载
④使用mode:'production' 打包代码 (会自动压缩代码,vue,react会自动删掉调试代码,会自动启用tree-shaking)
⑤
⑥
18、什么是tree-shaking?
- 在打包时移除掉javascript上下文中无用的代码,从而优化打包结果。
- ES Module才能让tree-shaking生效,commonjs不行。
19、ES6 Module和CommonJs的区别?
- ES6 Module静态引入,编译时引入
- CommonJs动态引入,执行时引入
- 只有ES6 Module才能静态分析,实现tree-shaking.
20、什么是scopeHoisting?
- 默认情况下,经过webpack打包后的模块资源会被组织成一个个函数形式,这种结构会导致大量作用于包裹代码,从而导致体积增大;运行代码时创建的函数作用域变多,内存开销变大。
- scope hoisting将符合条件的多个模块合并到同一个函数空间内,从而使打包出来的代码体积更小,内存开销也随之变小。
- 原理:分析出模块之间的依赖关系,将打散的模块合并到一个函数中。
- 由于scopeHoisting需要分析出模块之间的依赖关系,因此要采用ES6模块化语句。
- 自动启用:mode设置为production
- 手动启用: 配置ModuleConcatenationPlugin插件
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatentionPlugin') module.exports = { resolve:{ // 针对 npm中的第三方模块优先采用jsnext:main中指向的ES6模块化语法的文件 mainFields:['jsnext:main','browser','main'] }, plugins:[ new ModuleConcatentationPlugin() ] }
21、babel什么作用?babel-polyfill是什么?
用于将ES6+语法编写的代码转换为向后兼容的js语法,以便能够运行在当前或旧版本的浏览器或者其他环境中。
babel-polyfill是polyfill和core.js/regenerator的集合,但是在Babel7.4以后被弃用。推荐直接使用core.js(集成了ES6+语法的补丁)和regenerator。
22、什么是babel-runtime?
引入babel-polyfill会有一定副作用,例如引入新的全局对象,比如Promise,WeakMap等;或者是修改了现有的全局对象,例如修改了Array、String等的原型链。在引用开发中影响不大,但如果是在开发库、工具时引入polyfill,则会有潜在问题。
babel-runtime,将开发者依赖的全局内置对象,抽取成单独的模块,并通过模块导入的方式引入,避免了对全局作用域的修改。因此如果是开发工具和库,可以使用babel-runtime.
23、babel-polyfill和Babel- runtime有什么区别?
babel-polyfill会污染全局,Babel- runtime不会污染全局,如果是产出第三方lib要用babel-runtime
24、为什么proxy不能被polyfill?
proxy功能无法用Object.defineProperty模拟,