webpack 性能优化
开启 sourcemap
配置 devtool
为 sourcemap
快熟定位错误 代码源文件
module.exports = {
// 生产环境是否开启,根据具体需要,一把如果有监控系统,建议开启
devtool: 'source-map',
}
这样入口 index.js
就会生产同样的映射关系文件 index.js.map
具体配置参考:https://webpack.docschina.org/configuration/devtool/#devtool
使用 webpack-dev-server
服务
提高本地开发效率:热更新,本地数据 mock
配置 package.json
使用 webpack-dev-server
{
"scripts": {
"serve": "webpack-dev-server"
},
}
然后在 wepack.config.js
配置
module.exports = {
devServer: {
port: 8081, // 端口
open: true, // 自动打开浏览器
},
}
本地数据 mock 测试,安装 express 和 axois
npm i -D express axios
新建根目录文件 server.js
, 执行 node server.js
开启服务端
const express = require('express')
const app = express()
app.get('/api/info', (req, res) => {
res.json({
name: 'webpack'
})
})
app.listen('8090')
在入口 index.js
中测试
import axios from "axios";
axios.get('/api/info').then(res => {
console.log(res)
})
此时会报错,还需要配置代理访问,在 wepack.config.js
配置
module.exports = {
devServer: {
proxy: { // 代理访问
'/api': {
target: 'http://localhost:8090/'
}
}
},
}
babel
工具
用于编译 js 的,默认支持 js, json 模块,对于目标浏览器,我们需要转换:
flow -> js
jsx -> js
ts -> js
es6+ -> js
那么走起,首先安装 babel
工具
npm i -D babel-loader @babel/core @babel/preset-env @babel/polyfill core-js
babel v7 之后,都是以
@babel
开头的仓库
@babel/preset-env
只做语法转换,es6+ -> es5
@babel/polyfill
包含 ecma 新特性的库,可以使旧浏览器支持原生较新的功能
在 wepack.config.js
配置使用
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: {
loader: "babel-loader",
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
}
使用 polyfill
垫片
比如在入口文件 index.js
添加
import "@babel/polyfill"; // require("@babel/polyfill");
从 Babel 7.4.0 开始直接包含
core-js/stable
(以 polyfill ECMAScript 功能)和regenerator-runtime/runtime
(需要使用转译的生成器函数)
import "core-js/stable";
import "regenerator-runtime/runtime";
配置 polyfill
按需加载
目前还是推荐使用
@babel/polyfill
+core-js 2.x
,core-js 3.x
新功能暂时用不上,引入太大
在 wepack.config.js
配置使用
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: {
loader: "babel-loader",
options: {
presets: [
[
'@babel/preset-env', // 只做语法转换,es6+ -> es5
{
targets: { // 类似 browserslist 可以写成 "targets": "> 0.25%, not dead"
edge: '17',
firefox: '60',
chrome: '67',
safari: '11'
},
corejs: 2, // 新版本需要指定核心库版本,3.x 版本太大了
useBuiltIns: 'usage', // 按需加载 entry/usage/false
}
]
]
}
}
}
]
}
}
useBuiltIns
选项时 babel 7
的新功能,这个选项告诉 babel 如何配置 @babel/polyfill
entry
在 webpack 入口文件import "@babel/polyfill"
一次,babel 会根据你的使用情况 导入polyfill
垫片,没有使用的功能不会导入usage
不需要引入,全自动检测,还是需要安装@babel/polyfill
false
使用import "@babel/polyfill"
会全部加载(不推荐)
以上配合导致配置文件越来越长,可以根目录新建 .babelrc
(或 babel.config.js
)文件,把上面的 options
配置放进来
{
"presets": [
[
'@babel/preset-env',
// 只做语法转换,es6+ -> es5
{
"targets": {
// 类似 browserslist 可以写成 "targets": "> 0.25%, not dead"
"edge": '17',
"firefox": '60',
"chrome": '67',
"safari": '11'
},
"corejs": 2,
// 新版本需要指定核心库版本,3.x 版本太大了
"useBuiltIns": 'usage'
// 按需加载 entry/usage/false
}
]
]
}
配置文件 wepack.config.js
就简化了
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: {
loader: "babel-loader"
}
}
]
}
}
这样的效果一样的
集成 react
框架
首先安装 react 框架和依赖
npm i -D react react-dom @babel/preset-react
在 .babelrc
中配置
{
"presets": [
"@babel/preset-react"
]
}
入口文件 index.js
测试代码
import React, {Component} from "react";
import ReactDom from 'react-dom'
class App extends Component{
render(){
return <div> helle world </div>
}
}
ReactDom.render(<App/>,document.getElementById('app'))
运行 npm run dev
,打开 /dist/index.html
显示 hello world
如果像集成 vuejs 可以参考: https://vue-loader.vuejs.org/zh/guide/
如何编写一个 plugin
webpack
生命周期概念
-> 启动 webpack -> 读取配置(A 插件告知 webpack 运行到哪个阶段触发 A)
在入口 index.js
中放入一段代码
const webpack = require('webpack')
const options = require('../webpack.config')
const compiler = webpack(options) // compiler.hooks
Object.keys(compiler.hooks).forEach(hookName=>{
if(compiler.hooks[hookName].tap){
compiler.hooks[hookName].tap('anyString',()=>{
console.log(`run -> ${hookName}`)
})
}
})
compiler.run()
执行 node ./src/index.js
打印出 webpack 所有的生命钩子
D:\www\codepress\webpack>node ./src/index.js
run -> beforeRun
run -> run
run -> normalModuleFactory
run -> contextModuleFactory
run -> beforeCompile
run -> compile
run -> thisCompilation
run -> compilation
run -> make
run -> normalModuleFactory
run -> contextModuleFactory
run -> beforeCompile
run -> compilation
run -> finishMake
run -> afterCompile
[BABEL] Note: The code generator has deoptimised the styling of D:\www\codepress\webpack\node_modules\lodash\lodash.js as it exceeds the max
of 500KB.
[BABEL] Note: The code generator has deoptimised the styling of D:\www\codepress\webpack\node_modules\terser\dist\bundle.min.js as it exceed
s the max of 500KB.
run -> finishMake
run -> afterCompile
run -> shouldEmit
run -> emit
run -> assetEmitted
run -> assetEmitted
run -> assetEmitted
run -> afterEmit
run -> done
run -> afterDone
完整参考:https://webpack.docschina.org/api/compiler-hooks/#hooks
根目录新建 myPlugins/txt-webpack-plugin.js
插件文件,编写插件内容
// 插件的结构
module.exports = class texWebpackPlugin {
// apply
apply(compiler) {
// 钩入 hooks
// 异步钩子使用 tapAsync
compiler.hooks.emit.tapAsync('texWebpackPlugin', (compilation, cb) => {
// console.log(compilation.assets)
compilation.assets['lzw.txt'] = {
source: function () {
return 'hello lzw.'
},
size: function () {
return 1024
}
}
cb()
})
// 同步钩子使用 tap, 没有 cb 了
compiler.hooks.compile.tap('texWebpackPlugin', (compilation) => {
console.log(compilation)
})
}
}
然后就可以在 wepack.config.js
中使用
const texWebpackPlugin = require('./myPlugins/txt-webpack-plugin')
module.exports = {
plugins: [
new texWebpackPlugin(),
]
}
运行 npm run dev
,结果会生成 /dist/lzw.txt
文件