跳到主要内容

webpack

成熟的工具,重点在于配置、使用、性能优化,原理不是高优

为什么需要 webpack? webpack作用

  • 优化打包效率
  • 优化打包后代码: 压缩代码,整合代码,以网页加载更快
  • 工程化,构建流程

image.png

🤔思考问题

  • module chunk bundle 分别是什么意思?有何区别?
  • loader和plugin的区别?
  • webpack如何实现懒加载
  • babel-runtime 和 babel-polyfill的区别

关于webpack5

基本配置

安装配置

初始化安装

npm install webpack webpack-cli html-webpack-plugin webpack-dev-server -D

打包命令

  • webpack

拆分配置和merge

  • webpack.common.js 公共配置
  • webpack.dev.js 开发环境
  • webpack.prod.js 生产环境

启动本地服务 dev-server 命令

  • webpack-dev-server(webpack4)
  • webpack serve(webpack5)

Module

rule

module: {
rules: [
{
test: /\.js$/,//Include all modules that pass test assertion.
use: ['babel-loader'],
include: srcPath,
exclude: /node_modules/
}
]
},

解析ES6 -> 编译为ES5

修改配置文件.babelrc

{
"presets": ["@babel/preset-env"]
}

@babel/preset-env 是 babel 7 架構下的一組 preset,能讓你用最新的 JavaScript 語法寫程式,並且智慧地根據瀏覽器的環境引入需要的 polyfill,節省手動管理 syntax transform 的時間,還能夠減少 bundle 檔案大小

devServer

  • compress
    • 启动gzip compressinon
  • hot
    • 启用 webpack 的 热模块替换 特性
  • open
    • 告诉 dev-server 在服务器已经启动后打开浏览器。
  • port
    • 指定监听请求的端口号
  • proxy
    • 代理 解决开发时的跨域问题 image.png

hot hotReload 和 liveReload 的区别

  • devServer.hot(Hot Module Replacement, HMR): webpack dev server 的一个功能

    • 热更新,只更新改动的部分,不会刷新整个页面。JS 模块层面
    • 保留在完全重新加载页面期间丢失的应用程序状态。
  • devServer.liveReload

    • 热刷新,整个页面刷新
  • hotReload

    • 是 vue-loader 的选项

    • 热更新,只更新改动的部分,不会刷新整个页面。Vue 组件层面(修改 .vue 文件)

    • 保留在完全重新加载页面期间丢失的应用程序状态。

      • 点击按钮修改,后再去修改 vue 组件

        • 开启:会保存修改后的 message
        • 关闭:会恢复 message 初始化的值
      • 源码

        <template>
        <div>
        <h1>{{ message }} - change</h1>
        <button @click="changeMessage">Change Message </button>
        </div>
        </template>

        <script>
        export default {
        name: 'App',
        data() {
        return {
        message: 'Hello, World!',
        };
        },
        methods: {
        changeMessage() {
        this.message = 'Updated Message';
        document.getElementsByTagName('h1')[0].style.color = 'red';
        },
        },
        };
        </script>

        <style scoped>
        h1 {
        color: blue;
        }
        </style>
    • 配置

           chainWebpack: config => {
      config.module
      .rule('vue')
      .use('vue-loader')
      .loader('vue-loader')
      .tap((options) => {
      options.hotReload = true
      return options
      })
      },

      image.png

注意📢:在关闭 vue-loader 的时候开启 hot,会导致无法热重载。

解析图片文件

// import img
function insertImgElem(imgFile) {
const img = new Image();
img.src = imgFile;
document.body.appendChild(img)
}

import imgFile1 from './img/1.png';
insertImgElem(imgFile1);
import imgFile2 from './img/2.jpeg';
insertImgElem(imgFile2);

Image() 函数将会创建一个新的HTMLImageElement实例。

它的功能等价于 document.createElement('img')

webpack配置

npm i -D file-loader 
npm i -D url-loader
  1. webpack.dev.js
module: {
rules: [
{
test: /\.(png|jpeg|jpg|git)$/,
use: 'file-loader'
}
]
}
  1. webpack.prod.js
mode: 'production',
module: {
rules: [
{
test: /\.(png|jpeg|jpg|git)$/,
use: {
loader: 'url-loader',
options: {
// 小于 5kb 的图片 base64 格式产出
// 否侧 依然用 file-loader 的形式,产出 url 格式
limit: 5 * 1024,
// 打包到 img 目錄下
outputPath: '/img1',
// 設置圖片的cdn地址
// publicPath: 'https://cdn.abc.com'
}
}
}
]
},

效果

image.png

好处:图片小 用base64 减少一次http请求 减少耗时

处理样式文件

  • postcss-loader
  • less-loader
  • css-loader
    • 解析 css 文件中的 @importurl 语句,处理 css-modules ,并将结果作为一个js模块返回。
  • style-loader
    • 把 CSS 插入到 DOM 中。

npm i -D postcss-loader less-loader css-loader style-loader

配置 webpack.common.js

module: {
rules: [
{
test: /\.css$/,
// loader 的执行顺序是: 从后往前
use: ['style-loader', 'css-loader', 'postcss-loader']// 加了postcss
},
{
test: /\.less$/,
// 增加 ‘less-loader',注意顺序
use: ['style-loader', 'css-loader', 'less-loader']
}
]
},

postcss-loader中配置自动生成前缀postcss.config.js

module.exports = {
plugins: [require('autoprefixer')]
}

transform: rotate(-45deg) 自动添加前缀的效果 image.png

高级配置

配置多入口

虽然说现在的项目都是SPA(单页面应用),但项目有时候需要产出多个页面

plugins: [
// 多入口 - 生成 index.html
new HtmlWebpackPlugin({
template: path.join(srcPath, 'index.html'),
filename: 'index.html',
// chunks 表示该页面要引用哪些 chunk (即上面的 index 和 other),默认全部引用
chunks: ['index'] // 只引用 index.js
}),
// 多入口 - 生成 other.html
new HtmlWebpackPlugin({
template: path.join(srcPath, 'other.html'),
filename: 'other.html',
chunks: ['other'] // 只引用 other.js
})
]

image.png

image.png 打包输出

image.png

MiniCssExtractPlugin 抽离和压缩 CSS(重要)

image.png

抽离公共代码(重要)

  • 为什么需要抽离公共代码和第三方代码?

    • 当引入第三方库时,如果不独立打包,每次修改业务逻辑代码,都会重新打包第三方库,导致加载慢。
    • 两个文件引入了同一个模块,会打包两次
  • 配置为独立的chunks

image.png

webpack如何实现异步加载JS(懒加载)(重要)

默认支持的语法,不用配置

setTimeout(() => { 
import ('./danymic.js').then(res => {// 打包时会独立成一个chunk
console.log('res :>> ', res.default.message);
})
}, 1500)

处理React和Vue

  • 处理React(解析JSX语法)

    安装

    npm install --save-dev @babel/preset-react

    .babelrc

    注意: env 参数可能很快将被废弃

    {
    "presets": ["@babel/preset-react"]
    }

    配置完webpack中配置了'babel-loader',babel-loader会使用@babel/preset-react 去解析JSX语法

  • 处理vue(vue-loader)

    image.png

参考:https://www.babeljs.cn/docs/babel-preset-react

  • 处理Vue

module chunk bundle的区别

  1. module-各个源码文件,webpack中一切皆模块 (能被引入的文件都是模块,不管是什么类型,比如css  js 图片)

  2. chunk-多模块合并成的,如entry、import() 、splitChunk都可以生成chunk

  3. bundle-最终的输出文件

image.png

优化构建速度

image.png

聊聊webpack的时候,不要跟背书式回答问题,要通过分析问题的思路来回答问题

  • 通过问题引出解决问题的办法
  • 通过开发遇到的痛点和用户使用的痛点出发
    • 加快编译提高开发效率
      • 使用 HappyPack 多进程打包
    • 单页面应用的痛点,用户首屏加载会将所有的资源下载下来,缩小整体资源情况就是优化的切入点
      • 从缩小体积出发
        • 按需打包,例如多语言、时间 Memont.js 都可以配置只打包特定的语言和地区时间:IgnorePlugin
        • 压缩,通过打开 gzip 压缩,在 NGINX 配置实现
        • 通过 CDN 内容分发加速,不过不太适合小公司
        • 通过配置 Tree Shaking 剔除无用代码,因为 ES6 的模块化特性,可以通过静态分析方式找到无用代码,例如一些从未被使用的的变量和方法
      • 网络层面
        • 延迟 js 和 css 的加载,通过给 js script 设置 defer 和 async 属性,defer 是延迟
        • 尽量减少 js 和 css 的个数
        • 压缩 JS CSS:mini-css-extract-plugin,代码合并 splitChunks
  • 通过浏览器原理来解析问题出处
    • 浏览器进程:浏览器主进程、网络进程、渲染进程、GPU进程、插件进程
    • 浏览器访问 url 流程:
      • 浏览器主进程判断 url
    • DNS 解析域名,IP 交给网络进程,下载 HTML CSS JS
    • HTML 解析器,解析 HTML, 生成 DOM 树,JS 对象。CSS 解析器同理,解析 CSS ,生成 CSSOM 树
      • 在渲染 HTML 过程中遇到 JS CSS 链接的时候会暂停渲染去下载,因为下载下来的 JS 和 CSS 可能会影响到接下来的渲染结果
        • 在这里如果确定 JS 和 CSS 不需要提前下载,滞后到 body 后面,有助于提高首屏渲染速度,让用户更快看到页面
      • 渲染:
        • 在渲染进程中实现,里面有主线程、合成线程、
        • 主线程进行解析HTML、CSS, 生成 DOM CSSOM 树,根据结构和 position 分层 Layer,布局 Layout,生成绘制指令
        • 将绘制指令发送到合成线程中,合成线程执行指令,调用 GPU 进程绘制加速。
        • GPU 进程中光栅化
        • 渲染进程完成渲染后,将结果发送给浏览器主进程,浏览器主进程将结果显示到屏幕上,并更新浏览器的loading状态和页面
  1. 在浏览器输入 url 会发生什么。大体浏览器原理
  2. 渲染流程
  • 重排、回流
  1. V8 是如何执行 JS 代码的
  • 解释器:代码->(词法分析、语法分析)->AST->(词义分析)字节码->解释执行(如果是热代码,会转为机器码)
  • 编译器:代码->(词法分析、语法分析)->AST->(词义分析)中间代码->二进制文件->直接执行
  • V8 如何执行 JavaScript 代码
  1. 安全方面
  • 跨域: 前后端分离,防止盗版网页随意访问后端接口
    • 服务端配置:access-control-allow-origin (接入控制所允许的起源)
  • 网页登录信息通过 Cookie 来做验证,恶意脚本可以通过植入脚本到网页中,通过 document.cookie 来使用 Cookie,发起一些危险操作,例如转账
    • 解决,配置:http-only,不允许 js 获取 Cookie
  • XSS: 因为 HTML 解析的时候遇到 JS 代码会执行,所以 跨站脚本攻击 就是利用这个特性,用不同方式插入脚本到网页中执行
    • 评论区输入脚本代码:所以在react vue 中都会讲 insertHTML 的方法标为危险了
      • 解决:转义。npm 库也有实现了。例如将 < 转为 <,> 转为 >
    • DOM 型
  • CSRF: 跨站伪造请求攻击。主要是用吸引眼球的方式来伪造成一个链接,通过盗用用户的 Cookie 来发起请求,这也是广撒网式的钓鱼,例如转账
    • 所以现在有验证码,多重验证
    • eg: 美女弹窗、奖品领取、重磅新闻

总结下来(浏览器开放了两个特权)

  • 浏览器为了让用户方便,给引用第三方资源开放了权限,允许跨域。跨站脚本攻击,引入脚本,执行嵌入sql
  • 浏览器为了开放js权限。导致js可以获取到cookie,为跨站伪造请求创造了条件。document.cookie
  1. 模块化
  • 核心
    • 同步异步问题

模块化的发展也是因为互联网技术发展的一路衍生品。以前,js 都是直接写到html中,直接运行,此时的模块化都是全局污染的。后来,想出了命名空间,就是在根目录下命名一个 变量,再在变量下面定义变量。 nodejs 用的语法是 const a = require()

  • AMD
  • CMD
  • CommonJS
  • ESM
  • TreeShaking

优化 babel-loader(一般用于开发环境)

module: {
rules: [
// 优化: 1. 开启缓存 2. include exclude明确范围,写其中一个即可以
{
test: /\.js$/,
loader: ['babel-loader?cacheDirectory'], // 开启缓存
include: srcPath,
// exclude: /node_modules/
},
]
}

开启缓存: 用cacheDirectory,只要ES6代码没变,就不会重新编译,会缓存下来 第二次编译时,没改的部分使用缓存

IgnorePlugin(可用于生产环境)

避免一些模块引入。如果不用IgnorePlugin,可能会出现一些问题,比如打包的体积太大,打包慢

例如:moment会支持多语言,如何只引入中文模块?

// 忽略 moment 下的全部 /locale目录 
new webpack.IgnorePlugin(/\.\/locale/, /moment/)

按需引入

// 业务代码中动态引入语言包 
import 'moment/locale/zh-cn'

noParse(忽略大型的 library 可以提高构建性能)(可用于生产环境)

noParse避免重复打包 module: { noParse: [/react.min.js$/] }

IgnorePlugin和noParse区别:

  • IgnorePlugin直接不引入,代码中没有
  • noParse(类似vue.min.js已经模块化处理过)引入,但不打包

happyPack 多进程打包(可用于生产环境)

JS单线程,开启多进程打包 提高构建速度(特别是多核CPU)

image.png

image.png

ParallelUglifyPlugin优化压缩(可用于生产环境)

必须用于生产环境,压缩代码

  • webpack内置Uglify工具压缩JS
  • JS单线程,开启多线程压缩更快
  • 和happypack同理
// 使用 ParallelUglifyPlugin 并行压缩输出的 JS 代码
plugins: [
new ParallelUglifyPlugin({
// 传递给 UglifyJS 的参数
// (还是使用 UglifyJS 压缩,只不过帮助开启了多进程)
uglifyJS: {
output: {
beautify: false, // 最紧凑的输出
comments: false, // 删除所有的注释
},
compress: {
// 删除所有的 `console` 语句,可以兼容ie浏览器
drop_console: true,
// 内嵌定义了但是只用到一次的变量
collapse_vars: true,
// 提取出出现多次但是没有定义成变量去引用的静态值
reduce_vars: true,
}
}
})
]
  • 关于是否需要开启多进程
    • 项目较大的时候,开启会加快打包
    • 项目较小的时候,开启会变慢,因为多进程开销

自动刷新(不可用于生产环境)

一般不用自我配置,因为开发的时候一般会用devServer,会自动开启自动刷新

// 一般不用自我配置,因为开发的时候一般会用devServer,会自动开启自动刷新
// watch: true, // 开启监听,默认为 false
// // 开启监听后,webpack-dev-server 会自动开启刷新浏览器

// // 监听配置
// watchOptions: {
// ignored: /node_modules/, // 忽略哪些
// // 监听到变化发生后会等300ms再去执行动作,防止文件更新太快导致重新编译频率太高
// // 默认为 300ms
// aggregateTimeout: 300,
// // 判断文件是否发生变化是通过不停的去询问系统指定文件有没有变化实现的
// // 默认每隔1000毫秒询问一次
// poll: 1000
// }

HotModuleReplacementPlugin 热更新(不可用于生产环境)

区别

  • 自动刷新:整个网页全部刷新,速度慢,状态会丢失(比如填写表单时,表单内容会丢失,或者路由跳转了很多层,网页刷新后会跳到首页)
  • 热更新:新代码生效,网页不刷新,状态不丢失

开启热更新配置

image.png

配置哪些模块需要热更新

image.png

DllPlugin 拆分 bundles(不可用于生产环境)

webpack.dll.js

const path = require('path')
const DllPlugin = require('webpack/lib/DllPlugin')
const { srcPath, distPath } = require('./paths')

module.exports = {
mode: 'development',
// JS 执行入口文件
entry: {
// 把 React 相关模块的放到一个单独的动态链接库
react: ['react', 'react-dom']
},
output: {
// 输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称,
// 也就是 entry 中配置的 react 和 polyfill
filename: '[name].dll.js',
// 输出的文件都放到 dist 目录下
path: distPath,
// 存放动态链接库的全局变量名称,例如对应 react 来说就是 _dll_react
// 之所以在前面加上 _dll_ 是为了防止全局变量冲突
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'),
}),
],
}
  • DllReferencePlugin 使用dll文件 配置

image.png

优化产出代码

  • 体积更小
  • 合理分包,不重复加载
  • 速度更快,内存使用更少

使用生产环境

mode: 'production',打包生产环境代码

  • 自动开启代码压缩
  • Vue React等框架会自动删掉调试代码(如开发环境的warning)
  • 自动开启Tree-Shaking(摇晃树,把没有用的东西摇掉)

什么是Tree-Shaking?实现原理(与模块化有关)

  • js和css都可以treeShaking

  • ES6 Module才能让tree-shaking生效,commonjs不可以。因为ES6 Module是静态引入,Commonjs是动态引入

    • 而 ES6 Module 方案则从规范层面规避这一行为,它要求所有的导入导出语句只能出现在模块顶层, 且导入导出的模块名必须为字符串常量,这意味着下述代码在 ESM 方案下是非法的:

          if(process.env.NODE_ENV === 'development'){
      import bar from 'bar';
      export const foo = 'foo';
      }

tree-shaking 会把一些没有用到的函数删除

Webpack 原理系列九:Tree-Shaking 实现原理

url-loader 小图片使用base64编码

 module: {
rules: [
// 图片 - 考虑 base64 编码的情况
{
test: /\.(png|jpg|jpeg|gif)$/,
use: {
loader: 'url-loader',
options: {
// 小于 5kb 的图片用 base64 格式产出
// 否则,依然延用 file-loader 的形式,产出 url 格式
limit: 5 * 1024,

// 打包到 img 目录下
outputPath: '/img1/',

// 设置图片的 cdn 地址(也可以统一在外面的 output 中设置,那将作用于所有静态资源)
// publicPath: 'http://cdn.abc.com'
}
}
},
]
}

bundle加hash

image.png

提取公共代码

splitChunks

image.png

懒加载

使用CDN加速

image.png

webpack的scope hosting(作用域提升)

默认情况不开启scope hosting

  • 产生多个函数,每个函数都有一个作用域,对js代码执行和内存消耗非常不友好

开启后

  • 多个函数合并为一个函数,作用域减少
  • 代码体积更小
  • 创建函数作用域更少
  • 代码可读性更好

来自:深入浅出webpack

同时,考虑到 Scope Hoisting 依赖源码需采用 ES6 模块化语法,还需要配置 mainFields。 原因在 4-10 使用 TreeShaking 中提到过:因为大部分 Npm 中的第三方库采用了 CommonJS 语法,但部分库会同时提供 ES6 模块化的代码,为了充分发挥 Scope Hoisting 的作用,需要增加以下配置:

module.exports = {
resolve: {
// 针对 Npm 中的第三方模块优先采用 jsnext:main 中指向的 ES6 模块化语法的文件
mainFields: ['jsnext:main', 'browser', 'main']
},
};

image.png

webpack4升级 webpack5 以及周边插件后

Babel

Babel作用

ES6模块化,浏览器并不完全支持,需要通过Babel来编译成ES5

例如:将let const 编译为var;将class编译为function

环境搭建和基本配置

package.json

{
"devDependencies": {
"@babel/cli": "^7.7.5",
"@babel/core": "^7.7.5",
"@babel/plugin-transform-runtime": "^7.7.5",
"@babel/preset-env": "^7.7.5"
},
"dependencies": {
"@babel/polyfill": "^7.7.0",
"@babel/runtime": "^7.7.5"
}
}

.babelrc 基本配置

{
"presets": [
[
"@babel/preset-env"
]
],
"plugins": [
]
}

presets

  • @babel/preset-env是很多plugin的一个集合,可以转换ES6 7 8语法
    • 是一个babel插件的集合,预设置,代替我们写很多的plugins
  • @babel/preset-flow
  • @babel/preset-react(转换jsx语法)
  • @babel/preset-typescript(转换ts语法)

babel和babel-loader的区别

  • babel是编译ES6的核心工具
  • babel-loader是用babel封装后,将babel用于webpack打包流程。类似less和less-loader

babel-polyfill

什么是babel-polyfill?

对一些浏览器不支持的函数做补丁或者兼容

  • babel只负责解析语法,不处理API,也不管模块化(webpack处理模块化)
  • webpack把babel-polyfill引入进来,babel处理完交给webpack。polyfill解析Promise和includes语法,API就可以在浏览器正常运行

@babel/polyfill与core-js关系

@babel/polyfill可以看作是:core-jsregenerator-runtime

regenerator-runtimegenerator以及async/await的运行时依赖

单独使用@babel/polyfill会将core-js全量导入,造成项目打包体积过大。

从Babel v7.4.0[5]开始,@babel/polyfill被废弃了,可以直接引用core-jsregenerator-runtime替代

为了解决全量引入core-js造成打包体积过大的问题,我们需要配合使用@babel/preset-env

babel-polyfill如何按需引入

为什么要按需引入

babel-polyfill文件较大,如果只使用一部分功能,无需全部引入。(已废弃原因)

babel-runtime

babel-polyfill的问题

  • 会污染全局环境。如果做一个独立的系统,则没问题,如果做一个第三方库,就会可能产生与使用方命名冲突问题。
babel-runtime: 避免自行引入polyfill时导致的污染全局命名空间的问题

@babel/plugin-transform-runtime 的作用是将 helper 和 polyfill 都改为从一个统一的地方引入,并且引入的对象和全局变量是完全隔离的,这样解决了上面的两个问题。

参考

思考

前端为什么需要打包构建

代码层面

  • 打包体积会更小(tree-shaking,代码压缩,合并),加载速度更快
  • 编译高级语言语法(TS,ES6,less,scss,模块化)
  • 兼容性和错误提示(babel-polyfill、postcss、eslint)

打包流程、前端工程化方面

  • 統一、高效的开发环境
  • 统一的构建流程和产出标准
  • 集成公司的构建规范(提测、上线等)

webpack 中 loader 和 plugin 的区别?

webpack 的流程是,分析代码转换代码编译代码输出代码

  • loader 模块转换器。如 less -> css
    • css-loader
    • style-loader
    • postcss-loader
    • sass-loader
  • plugin 扩展插件,类似浏览器的插件,主要是扩展和增强功能。原理是基于 webpack 中的事件机制,监听广播事件,再做相应的处理。
    • HtmlWebpackPlugin
    • MiniCssExtractPlugin
    • TreeShakingPlugin:由于分析代码过程中可知道
    • HotModuleReplacementPlugin
    • DllPlugin: 将不会经常变动的代码打包成静态资源,提高打包速度,例如react、vue、jquery等固定版本的库

loader,它是一个转换器,将A文件进行编译成B文件,比如:将A.less转换为A.css,单纯的文件转换过程。

plugin是一个扩展器,它丰富了webpack本身,针对是loader结束后,webpack打包的整个过程, 它并不直接操作文件,而是基于事件机制工作,会监听 webpack 打包过程中的某些节点,执行广泛的任务

loader

  • 在打包前或期间调用
  • 调用顺序与书写顺序相反

常见的 loader

plugin

  • 事件触发调用,监听 webpack 广播的事件
  • 不同生命周期改变输出结果

常见的 plugin

babel 和 webpack 的区别

  • babel-js 新语法编译工具,不关心模块化
  • webpack 是打包构建工具,是多个 loader plugin 的集合

如何产出一个lib

babel-polyfill和babel-runtime的区别

  • babel-polyfill会污染全局
  • babel-runtime不会污染全局
  • 开发第三方lib要用babel-runtime,避免与使用方产生冲突

webpack如何使用懒加载

  • import()
  • 结合Vue React 异步组件
  • 结合Vue-router React-router异步加载路由

为何Proxy不能被Polyfill

因为(function callback是es5语法)

  • Class可以用function模拟
  • Promise可以用callback来模拟
  • 但Proxy的功能用Object.defineProperty无法模拟

Polyfill 是一块代码(通常是 Web 上的 JavaScript),用来为旧浏览器提供它没有原生支持的较新的功能。 proxy没有任何现成的语法可以模拟到,所以无法Polyfill

常见性能优化方法

构建速度产出代码两个方面分析

优化构建速度

  • 可用于生产环境的
    • 优化babel-loader
    • IgnorePlugin
    • noParse
    • happyPack多进程打包
    • ParallelUgifyPlugin优化压缩
  • 不能用于生产环境
    • 自动更新
    • 热更新
    • DllPlugin

特别强调:热更新万万不能用于生产环境,在代码中写的热更新范围,在生产打包时一定要删掉,否则生产环境会有问题!

优化产出代码

  • 小图片通过base64方式
  • bundle加hash
  • 懒加载
  • 提取公共代码
  • 使用cdn加速
  • IgnorePlugin
  • 使用Production模式
  • 开启Scope Hosting

react cli 中的 webpack 做了啥?基于他的基础上做了哪些配置和优化?

  • 通过 config-overrides.js 覆盖默认的 webpack,例如做了修改 antd design 的样式变量

参考