webpack
- webpack已是前端打包构建的不二选择
- 每日必用,面试必考
- 成熟的工具,重点在于配置和使用,原理并不高优(只在开发环境使用,线上不用)
讲解范围
- 基本配置
- 高级配置
- 优化打包效率
- 优化产出代码
- 构建流程概述
- babel
webpack面试题
- 前端代码为何要进行构建和打包
- module chunk bundle分别什么意思,有何区别
- loader和plugin的区别
- webpack如何实现懒加载
- webpack常见性能优化
- babel-runtime和babel-polyfill的区别
webpack基本配置
-
vue-cli create-react-app
-
常用上述脚手架,而不会自己配置webpack?CLI工程师…
-
则面试不会通过
-
拆分配置和merge
拆分配置和merge
- common - 公共配置
- dev - development配置
- prod - production配置
在development或者production模式中引入webpack-merge模块,将common配置merge进来
const webpackCommonConf = require('./webpack.common.js') // 引入公共模块文件
const { smart } = require('webpack-merge')
module.exports = smart(webpackCommonConf, {
mode: 'development',
module: {
rules: []
},
plugins: [
new webpack.DefinePlugin({
// window.ENV = 'development'
ENV: JSON.stringify('development')
})
]
})
-
Path - 文件路径可单独配置文件夹
-
本地代理配置,底层用到了http-proxy-middleware
// 设置代理 proxy: { // 将本地/api/xxx代理到localhost: 3000/api/xxx '/api': 'http://localhost: 3000', // 将本地/api2/xxx代理到localhost: 3000/xxx '/api2': { target: 'http://localhost: 3000', pathRewrite: { '/api2': '' } } }
-
loader的执行顺序是从后往前
rules: [ { test: /\.css$/, // loader的执行顺序是 从后往前 // postcss 浏览器兼容性写法 需要在postcss.config.js中引入autoprefixer loader: ['style-loader', 'css-loader', 'postcss-loader'] } ]
-
对图片的处理 dev和prod分开处理
小于5kb的图片用base64格式产出,否则依旧沿用file-loader的形式,生成url链接,放在特定的img文件夹中
rules: [ { test: /\.(png|jpg|jpeg|gif)$/, use: { limit: 5*1024, outputPath: '/img1/', } } ]
-
prod环境output使用contentHash
output: { filename: 'bundle.[contentHash:8].js', path: distPath, }
webpack高级配置
-
基本配置只能做demo,不能做线上项目
-
面试考察基本配置,只是为了快速判断你是否用过webpack
-
以下高级配置,也是通过面试的必要条件
-
多入口
-
抽离css文件(用MiniCssExtractPlugin),压缩css
-
抽离公共代码和第三方模块
-
懒加载
-
处理jsx
-
处理vue
module chunk bundle的区别
- module - 各个源码文件,webpack中一切皆模块
- chunk - 多模块合并成的,如entry import() splitChunk
- bundle - 最终的输出文件
webpack性能优化
- 大厂必考&社区热议话题
- 优化打包构造速度 - 开发体验和效率
- 优化产出代码 - 产品性能
webpack性能优化 - 构建速度
- 优化babel-loader
- IgnorePlugin
- noParse 不去管哪些
- happyPack 多进程打包工具
- ParallelUglifyPlugin 多进程代码压缩js
- 自动刷新
- 热更新
- DllPlugin
优化babel-loader
{
test: /\.js$/,
use: ['babel-loader?cacheDirectory'], // 开启缓存
include: path.resolve(__dirname, 'src'), // 明确范围,也可用排除范围exclude,二者选其一即可
// exclude: path.resolve(__dirname, 'node_modules')
}
happyPack多进程打包
- js单线程,开启多进程打包
- 提高构建速度(特别是多核CPU)
关于开启多进程
- 项目较大,打包较慢,开启多进程能提高速度
- 项目较小,打包很快,开启多进程会降低速度(进程开销)
- 按需使用
自动刷新
热更新
- 自动刷新:整个网页全部刷新,速度较慢
- 自动刷新:整个网页全部刷新,状态会丢失
- 热更新:新代码生效,网页不刷新,状态不丢失
DllPlugin动态链接库插件
-
前端框架如vue React,体积大,构建慢
-
较稳定,不常升级版本
-
同一个版本只构建一次即可,不用每次都重新构建
-
打包
-
引用
webpack优化构建速度(可用于生产环境)
- 优化babel-loader
- IgonrePlugin
- noParse
- happyPack
- ParallelUglifyPlugin
webpack优化构建速度(不用于生产环境)
- 自动刷新
- 热更新
- DllPlugin
webpack性能优化 - 产出代码
效果
- 体积更小
- 合理分包,不重复加载
- 速度更快、内存使用更少
优化方式
- 小图片base64编码 - 小于5kb或3kb的图片用base64格式产出
- bundle 加 hash output中filename加contentHash: 8位数 目的是为了命中缓存
- 懒加载 - import 原理同组件或路由异步加载
- 提取公共代码 - 分两块 一个是第三方库 一个是项目中的公共代码 chunks;
- IngorePlugin - moment库
- 使用cdn加速 - 产出代码引用的是cdn的链接
- 使用production模式
- 使用Scope Hosting
使用production模式
优点
- 自动开启压缩代码
- vue react等会自动删掉调试代码(如开发环境的warning )
- 启动Tree-Shaking 按需引入// ES6 Module才能让tree-shaking生效 用commonjs不生效
es6 Module和Commonjs区别
- ES6 module静态引用,编译时引入
- commonjs动态引用,执行时引入
- 只有es6 module才能静态分析,实现tree-shaking
// commonjs动态引入
let apiList = require('../config/api.js');
if (isDev) {
// 可以动态引入,执行时引入
apiList = require('../config/api_dev.js');
}
// es6 module
import apiList from '../config/api.js'
if (isDev) {
// 编译时报错,只能静态引入
import apiList from '../config/api_dev.js'
}
scope hosting
效果
- 代码体积更小
- 创建函数作用域更少
- 代码可读性更好
// hello.js
export default 'Hello 双越'
// main.js
import str from './hello.js'
console.log(str)
默认会打包成两个函数
开启scope hosting之后 会被打包成一个函数
[
(function(module, __webpack_exports__, __webpack_require__) {
var hello = ('Hello 双越');
console.log(hello);
})
]
实现方式
通过ModuleConcatenationPlugin
cosnt ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin')
module.exports = {
resolve: {
// 针对 NPM 中的第三方模块优先采用jsnext:main中指向的ES6模块化语法的文件
mainFields: ['jsnext:main', 'browser', 'main']
},
plugin: [
// scope hosting
new ModuleConcatenationPlugin(),
]
}
babel
-
前端开发环境必备工具
-
同webpack,需要了解基本的配置和使用
-
面试考察概率不高,但要求必会
-
环境搭建 & 基本配置
-
babel-polyfill
-
babel-runtime
babel环境搭建和基本配置
- 环境搭建
- .babelrc配置
- presets和plugins (presets 预设了大部分plugins)
// .babelrc
{
"presets": [
"@babel/preset-env",
// 按需引用
{
"useBuiltIns": "usage",
"corejs": 3 // 版本
}
],
"plugins": []
}
babel-polyfill是什么
- 什么是Polyfill - 补丁/兼容 (判断原型链上是否有某个方法 没有的话用polyfill写法)
- core.js 和 regenerator(标准库 集成了es6+的语法)
- babel-polyfill 即两者的集合
- babel7.4之后弃用babel-polyfill
- 推荐直接使用core-js和regenerator
- 但并不影响面试会考察它
Babel-polyfill按需引入
- 文件较大
- 只有一部分功能,无需全部引入
- 配置按需引入
babel-polyfill的问题
- 污染全局环境
- 如果做一个独立的web系统,则无碍
- 如果做一个第三方lib,则会有问题
babel-runtime
- 不污染全局环境
- 原理: 更名 如Promise 更名为 _Promise等 不污染全局环境
babel总结
- babel只解析语法 如箭头函数; Promise、includes等API解析不了(语法是符合es5语法规范);
- 要解析Promise等API,需要使用babel-polyfill(是对core-js和regenerator的包装,缺点是会污染全局环境,如果是第三方lib库需要配合babel-runtime使用,原理是重命名,只使用babel-runtime时,实例方法无法工作。如果不是第三方lib,可直接按需引入babel-polyfill; useBuiltIns取usage实现按需引入)
babel-runtime的优缺点
优点:
- 不会污染全局变量
- 多次使用只会打包一次
- 依赖统一按需引入,无重复引入,无多余引入
- 避免 babel 编译的工具函数在每个模块里重复出现,减小库和工具包的体积
缺点:
- 不支持实例化方法,如 Array.includes()
- 如果使用的API用的次数不是很多,那么
transform-runtime
引入polyfill的包会比不是transform-runtime
时大
前端为何要进行打包和构建(使用webpack的优点)
代码方面
-
体积更小,加载更快(tree-shaking 压缩 合并)
-
编译高级语言或语法(ts es6+ 模块化 scss)
-
兼容性和错误检查(polyfill postcss eslint)
开发流程(前端工程化)
- 统一、高效的开发环境
- 统一的构建流程和产出标准
- 集成公司构建规范(提测、上线等)
module chunk bundle的区别
- module - 各个源码文件,webpack中一切皆模块
- chunk - 多模块合并而成,如entry import() splitChunk
- bundle - 产出文件
loader和plugin的区别
- loader 模块转换器 如less -> css
- Plugin 扩展插件,如HtmlWebpackPlugin
常见loader和plugin有哪些
loader
- babel-loader
- url-loader
- file-loader
- scss-loader
- css-loader
- style-loader
plugin
-
html-webpack-plugin
-
dll-plugin
-
IgnorePlugin
-
ModuleConcatenationPlugin
-
…
babel和webpack的区别
-
babel - js新语法编译工具,不关心模块化
-
webpack - 打包构建工具,是多个loader和plugin的集合
如何产出一个lib
- 参考dll
output: {
library: "library", // 引入方式 script 标签
libraryTarget: 'umd' // 引入方式 模块化 如es6 cmd amd
}
babel-polyfill 和 babel-runtime
- Babel-polyfill会造成全局污染
- babel-runtime不会造成全局污染
- 产出第三方lib要用babel-runtime
webpack如何实现懒加载
- import()
- 结合vue react异步组件
- 结合vue-router react-router异步加载路由
为何proxy不能被polyfill
- 如class可以用function模拟
- 如promise可以用callback来模拟
- 但proxy的功能用Object.defineProperty无法模拟