webpack
webpack复习
webpack基本配置
拆分配置 - 公共配置 + 生产环境配置 + 开发环境配置 使用merge
webpack-dev-server 启动本地服务
在公共中引入babel-loader处理es6
webpack高级配置
多入口文件
enty 入口为一个对象 里面的key为入口名 value为入口文件路径 例如 path.join(srcPath, ‘index.js’)
output 输出打包文件 也是一个对象 filename使用 [name].[contentHash:8].js 动态命名 path为打包到哪个路径
使用hash做文件名 是因为当文件内容没有改变的时候 hash是不变的 文件名不变 引入的时候就会走缓存 页面加载快
entry: {
index: path.join(srcPath, 'index.js'),
other: path.join(srcPath, 'other.js')
},
output: {
filename: '[name].[contentHash:8].js',
path:distPath
}
plugins 中的new HtmlWebpackPlugin生成html文件 此时也要写多个
template表示打包的文件
filename表示打包后的名称
chunks表示使用哪个入口文件来打包 如果不设置 则所有入口文件都会被引入
plugins: [
new HtmlWebpackPlugin({
template: path.join(srcPath, 'index.html'),
filename: 'index.html',
chunks: ['index']
}),
new HtmlWebpackPlugin({
template: path.join(srcPath, 'other.html'),
filename: 'other.html',
chunks: ['other']
})
]
抽离css
使用MiniCssExtractPlugin 替换 style-loader 将css抽离为一个单独的css文件
设置 new MiniCssExtractPlugin 抽离css
optimization 用于优化打包结果的对象
module: {
rules:{
{
test: /\.less$/,
loader: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
'postcss-loader'
]
}
}
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/main.[countenHash:8].css'
})
],
optimization: {
// 压缩css
minimizer: [
new TerserJSPlugin(),
new OptimizeCSSAssetsPlugin()
]
}
抽离公共代码
抽离打包时公共的包例如lodash
optimazation:{
// 分割代码块
splitChunks:{
chunks: 'all',
/*
initial 入口chunk 对于异步导入的文件不处理
async 异步chunk 只对异步导入的文件处理
all 全部thunk
*/
// 缓存分组
cacheGroups: {
// 第三方模块
vendor: {
name: 'vendor', //chunk 名称
priority: 1, //权限更高 优先抽离 重要!!!
test: /node_modules/, // 命中条件
minSize: 0, //大小限制 太小就不用单独打包了
minChunks: 1, //最少重复用过几次 公共模块复用过一次就要单独打包 因为公共组件一般都很大
},
// 公共的模块
common: {
name: 'common', //chunk名称
priority: 0, //优先级
minSize: 0, //公共模块大小限制
minChunks: 2 // 公共模块最少复用几次
}
}
}
}
懒加载
import 语法
处理jsx和处理vue
对于jsx配置babel
.babelrc
{
"presets": ["@babel/preset-env"]
}
对于vue
使用vue-loader
module chunk bundle的区别
module - 各个源码文件 webpack中一切皆模块
chunk - 多模块合并成的 如entry import() splitChunk
bundle - 最终的输出文件
webpack性能优化
优化构建速度
优化babel-loader
开启缓存 只要是es6代码没有改的 就不会在重新编译 就会缓存 在第二次进行编译的时候 针对没有改的部分 启用缓存就不会重新编译
include 和 exclude 确定范围
{
test: /\.js$/,
use: ['babel-loader?cacheDirectory'], //开启缓存
include: path.resolve(__dirname, 'src'), //明确范围
// 排除范围 include 和 exclude 两着选一个即可
exclude: path.resolve(__dirname, 'node_modules')
}
IgnorePlugin 避免引入无用模块
import moment from ‘moment’
默认会引入所有语言JS代码 代码过大
plugins: [
// 忽略moment下的/locale目录
new webpack.IgnorePlugin(/\.\/locale/, /moment/)
]
import moment from 'moment'
import 'moment/locale/zh-cn' // 手动引入中文包
noParse 避免重复打包
module.exports = {
module: {
// 忽略对 `react.min.js` 文件的递归解析处理
noParse: [/react\.min\.js$/],
}
}
IgnorePlugin 直接不引入 代码中没有 而且优化产出体积
noParse引入 但不打包
happyPack多进程打包
JS单线程 开启多进程打包
提高构建速度(特别是多核CPU)
module: {
rules: [
//js
{
test: /\.js$/,
// 把对.js文件的处理转交给id为babel的HappyPack实例
use: ['happypack/loader?id=babel'],
include: srcPath,
}
]
},
plugins:[
// happyPack 开启多进程打包
new HappyPack({
// 用唯一的标识符 id 来代表当前的HappyPack 是哦用来处理一类特定的文件
id: 'babel',
// 如何处理.js文件 用法和loader配置中一样
loaders: ['babel-loader?cacheDirectory']
})
]
ParallelUglifyPlugin 多进程压缩JS
webpack内置 Uglify工具压缩JS
JS是单线程的 开启多进程压缩更快
和happyPack同理
plugins:[
// 使用ParallelUglifyPlugin 进行压缩输出的JS代码
new ParallelUglifyPlugin ({
//传递给UglifyJS的参数
// (还是使用UglifyJS压缩 只不过帮助开启了多进程)
uglifyJS: {
output: {
beautify: false, //最紧凑的输出
comments: false, //删除所有注释
},
compress: {
// 删除所有的 `console` 语句 可以兼容ie浏览器
drop_console: true,
// 内嵌定义了但是只用到一次的变量
collapse_vars: true,
// 提取出出现多次但是没有定义成变量去引用的静态变量
reduce_vars: true,
}
}
})
]
关于开启多进程
当项目较大时 打包较慢 开启多进程能提高速度
当项目较小时 打包很快 开启多进程会降低速度(进程开销)
按需使用
自动刷新
module.export = {
watch: true, // 开启监听 默认为false
// 注意 开启监听后 webpack-dev-server 会自动公开其刷新浏览器
// 监听配置
watchOptions: {
ignored: /node_modules/, // 忽略哪些
// 监听到变化后会等300ms再去执行动作 防止文件更新太快导致重新编译频率太高
aggregateTimeout: 300, // 默认为300ms
// 判断文件是否发生变化是通过不停的去询问系统指定文件有没有变化实现的
poll: 1000 // 默认每隔1000毫秒询问一次
}
}
热更新
自动刷新:整个页面全部刷新 速度较慢
自动刷新:整个页面全部刷新 状态会丢失
热更新:新代码生效 网页不刷新 状态不丢失
entry:{
index: [
'webpack-dev-server/client?http://localhost:8080/',
'webpack/hot/dev-server',
path.join(srcPath, 'index.js')
],
other: path.join(srcPath, 'other.js')
},
plugins: [
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'
}
}
DllPlugin 动态链接库插件
前端框架如vue react 体积大 构建慢
较稳定 不常升级版本
每次npm run dev都要重新构建 vue react
同一个版本只构建一次即可 不用每次都重新构建
webpack已内置 DllPlugin支持
DllPlugin - 打包出dll文件
DllReferencePlugin - 使用dll文件
webpack.dll.js
const path = require('path')
const DllPlugin = require('webpack/lib/DllPlugin')
const {srcPath, distPath} = require('./paths')
module.exports = {
mode: 'development',
// JS执行入口文件
enrty: {
// 把React相关模块放到一个单独的动态连接库
react: ['react', 'react-dom']
},
output: {
// 输出的动态链接库的文件名称 [name]代表当前动态链接库的名称
// 也就是 entry 中配置的react 和 polyfill
filename: '[name].dll.js',
// 输出的文件都放到dist目录下
path: distPath,
// 存放动态链接库的全局变量名称 例如对应react来说就是_dll_react
// 之所以在前面加上_dell_是为了防止全局变量冲突
library: '_dll_[name]'
},
plugins: [
// 接入 DllPlugin
new DllPlugin({
// 动态链接库的全局变量名称 需要和output.library中的一致
// 该字段的值也就是输出的manifest.json文件中的name字段的值
// 例如 react.manifest.json中就有"name": "_dll_react"
name: '_dll_[name]',
// 描述动态链接库的manifest.json文件输出时的文件名称
path: path.join(distPath, '[name].manifest.json')
})
]
}
webpack --config build/webpack.dll.js 打包
最后只要在index.html中引用react.dll.js就可以
<script src="./react.dll.js"></script>
在webpack.dev.js中引入
const path =- require('path')
const webpack = require('webpack')
const {smart} = require('webpack-merge')
const webpackCommonConf = require('./webpack.common.js')
const {srcPath, distPath} = require('./paths')
// 引入DllReferencePlugin
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin')
module.exports = smart(webpackCommonConf, {
mode: 'development',
module: {
reles: [
{
test: /\.js$/,
loader: ['babel-loader'],
include: srcPath,
exclude: /node_modules/ //不要再转换node_modules
}
]
},
plugins: [
// 告诉webpack使用了哪些动态链接库
new DllReferencePlugin({
// 描述 react动态链接库的文件内容
manifest: require(path.join(distPath, 'react.manifest.js'))
})
]
})
webapck性能优化 - 产出代码
小图片base64编码
{
test: /\.(png|jpg|jpeg|gif)$/,
use: {
loader: 'url-loader',
options: {
// 小于5kb 的图片用Base64格式产出
// 否则依然延用file-loader的形式产出url格式
limit: 5 * 1024,
// 打包到img目录下
outputPath: '/img/',
}
}
}
bundle + hash
filename: '[name].[contentHash:8].js'
懒加载
提取公共代码
IngorePlugin
使用CDN加速
1、
output: {
publicPath: 'cdn地址' //修改搜友静态文件url的前缀
}
2、把打包的结果上传到cnd地址上
使用production
将mode设置为production后会自动压缩代码
vue react 等会自动删除调试代码 (如开发环境下的错误提示)
启动Tree-Shaking 【只有ES6 Module 才能让tree-shaking生效 commonjs不行】
module.exports = {
mode: 'production'
}
ES6 Module 和 Commonjs的区别
ES6 Module静态引入 编译时引用
Commonjs动态引入 执行时引入
只有ES6 Module才能静态分析 实现Tree-Shaking
let apiList = require('./config/api.js')
if(isDev){
// 可以动态引入 执行时引入
apiList = require('./config/api_dev.js')
}
import apiList from './config/api.js'
if(isDev){
// 编译时报错 只能静态引入
import apiList from './config/api_dev.js'
}
使用Scope Hosting
一个文件打包成一个函数
当文件多时 函数也会多 占内存大
想要多个文件合并成一个函数 使用Scope Hosting
代码体积更小
创建函数作用域更小
可读性更好
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin')
module.exports = {
resolve: {
// 针对 Npm中的第三方模块优先采用jsnext:main 中指向的ES6模块化语法的文件
mainFields: ['jsnext:main', 'browser', 'main']
},
plugins: [
// 开启 Scope Hoisting
new ModuleConcatenationPlugin()
]
}
babel环境搭建
presets 一些常用plugins的集合 组合的预设 不用写很多plugin
.babelrc
{
"presets": [
[
"@babel/preset-env"
]
]
}
babel-polyfill
什么是polyfill ? - 补丁
core-js和regenerator
core-js标准库 所有补丁代码
regenerator 支持 generator语法补全core-js
babel-polyfill就是core-js和regenerator的集合
babel7.4弃用了babel-polyfill 推荐直接使用core-js和regenerator
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
]
]
}
问题:
污染全局环境
解决:
babel-runtime
babel-runtime
{
"plugins":[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": 3,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
}
前端为何要进行打包和构建
体积更小(Tree-Shaking、压缩、合并) 加载更快
编译高级语言或语法(TS、ES6+、模块化、SCSS)
兼容性和错误提示(Polyfill、postcss、eslint)
统一、高效的开发环境
统一的构建流程和产出标准
loader和plugin的区别
loader模块转换器 如less -> css
plugin 扩展插件 转换完做一些扩展 如HtmlWebpackPlugin 将js或css塞进一个html文件里
bable和webpack的区别
babel-JS新语法编译工具 不关心模块化
webpack- 打包构建工具 是多个loader plugin的集合
如何产出一个lib
babel-polyfill和babel-runtime的区别
babel-polyfill会污染全局
babel-runtime不会污染全局
产出第三方lib要用babel-runtime
为何Proxy不能被Polyfill
如Class可以用function模拟
如Promise可以用callback来模拟
但Proxy的功能用Object.defineProperty无法模拟