跳到主要内容

webpack 性能优化

开启 sourcemap

配置 devtoolsourcemap 快熟定位错误代码源文件

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

webpack

完整参考: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 文件