前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版

webpack4 定制前端开发环境,从零搭一套构建流水线

首页2018-09-04 15:10:12Build
Webpackwebpack4前端工程化构建工具开发环境

接手一个新项目,npx create-react-app 一敲,脚手架把 webpack 藏在 react-scripts 里,日子过得挺舒服。直到某天要加一个 svg 的自定义处理,或者要把 CSS 拆成两份,eject 出来一看几百行配置,完全不知道从哪改起。

这篇是我照着掘金小册的思路,把「从空目录到能上线的构建流水线」完整走了一遍留下的笔记。每个 Step 都说清楚在解决什么问题、为什么要加这个包,最后给一份上线前的 checklist。基线是 webpack 4,webpack 5 有变化的地方我都单独标了出来。

在本篇文章中,我们将从浅入深,和大家一起学习以下知识:

  • webpack 的四块骨架(entry / loader / plugin / output)在整条流水线上分别站在哪
  • 六个 Step 从零搭出一套能跑的开发环境,每步解决一个具体问题
  • resolve 怎么配才能既写得爽又不拖慢构建
  • module.rules 的匹配条件、use 写法和 loader 的四类执行顺序
  • DefinePlugin、copy-webpack-plugin、extract-text-webpack-plugin 这三个高频插件
  • webpack-dev-server 的 proxy、mock 和 publicPath 怎么配
  • 开发和生产环境的差异怎么用 mode 加配置拆分来管
  • HMR 的接入方式和 module.hot 的四个 API
  • 图片压缩、DataURL、代码压缩、代码分离、按需加载这几项产物优化
  • 一份上线前的 checklist,以及 webpack 5 上哪些写法要换

原始笔记来源是掘金小册,我在此基础上补了讲解和现在的做法。这篇偏实战流程,如果你要的是「每个配置项分别是什么」的手册式内容,看姊妹篇 webpack 回顾篇 和 webpack4 配置详解。

# 一、webpack 概念和基础使用

动手之前先把整条流水线的形状看清楚。webpack 做的事情可以画成这样一条线:

  src/index.js  ──┐
  src/style.less  │
  src/logo.png    ├─► entry(从这里开始爬依赖)
  src/index.html  │
                  │
                  ▼
        ┌───────────────────────────────┐
        │ module.rules(loader 层)      │
        │  .jsx? → babel-loader          │
        │  .less → less → css → style    │
        │  .png  → file-loader           │
        │  作用:把非 JS 翻译成模块       │
        └───────────────┬───────────────┘
                        │
                        ▼
        ┌───────────────────────────────┐
        │ plugins(生命周期钩子层)       │
        │  HtmlWebpackPlugin 生成页面     │
        │  ExtractTextPlugin 抽 CSS       │
        │  DefinePlugin 注入常量          │
        │  作用:loader 管不了的都归它     │
        └───────────────┬───────────────┘
                        │
                        ▼
              output → dist/[name].[hash].js
                       dist/[name].css
                       dist/index.html
@前端进阶之旅: 代码已经复制到剪贴板

记住这个形状,后面每加一个包你都能说清楚它挂在哪一层。loader 只在「文件转模块」这一格里干活,其余的一律是 plugin 的事。

# 1.1 安装和使用

全局装是为了先跑通命令看看效果,真实项目里一律装到 devDependencies,用 npx webpack 或者写进 package.json 的 scripts 调用。原因很实在,全局版本和项目需要的版本对不上的时候,报错信息通常毫无提示性,团队里每个人的全局版本还都不一样。

webpack 4 开始 CLI 被拆成了独立的 webpack-cli 包,所以要两个一起装。webpack 5 也是同样的分法。

npm install webpack webpack-cli -g 

# 或者
yarn global add webpack webpack-cli

# 然后就可以全局执行命令了
webpack --help
@前端进阶之旅: 代码已经复制到剪贴板

# 1.2 webpack 的基本概念

webpack 说到底就是个打包工具,它会根据代码的内容解析模块依赖,把多个模块的代码打包到一起

这张图说的就是上面那条流水线:左边一堆散落的源文件,中间经过 webpack 处理,右边变成浏览器能直接跑的几个静态文件。

webpack 会把我们项目中使用到的多个代码模块(可以是不同文件类型),打包构建成项目运行仅需要的几个静态文件

入口

入口可以使用 entry 字段来进行配置,webpack 支持配置多个入口来进行构建

module.exports = {
  entry: './src/index.js' 
}

// 上述配置等同于
module.exports = {
  entry: {
    main: './src/index.js'
  }
}

// 或者配置多个入口
module.exports = {
  entry: {
    foo: './src/page-foo.js',
    bar: './src/page-bar.js', 
    // ...
  }
}

// 使用数组来对多个文件进行打包
module.exports = {
  entry: {
    main: [
      './src/foo.js',
      './src/bar.js'
    ]
  }
}

@前端进阶之旅: 代码已经复制到剪贴板

loader

可以把 loader 理解为是一个转换器,负责把某种文件格式的内容转换成 webpack 可以支持打包的模块

  • 当我们需要使用不同的 loader 来解析处理不同类型的文件时,我们可以在 module.rules 字段下来配置相关的规则,例如使用 Babel 来处理 .js 文件
module: {
  // ...
  rules: [
    {
      test: /\.jsx?/, // 匹配文件路径的正则表达式,通常我们都是匹配文件类型后缀
      include: [
        path.resolve(__dirname, 'src') // 指定哪些路径下的文件需要经过 loader 处理
      ],
      use: 'babel-loader', // 指定使用的 loader
    },
  ],
}
@前端进阶之旅: 代码已经复制到剪贴板

plugin

模块代码转换的工作由 loader 来处理,除此之外的其他任何工作都可以交由 plugin 来完成。通过添加我们需要的 plugin,可以满足更多构建中特殊的需求。例如,要使用压缩 JS 代码的 uglifyjs-webpack-plugin 插件,只需在配置中通过 plugins 字段添加新的 plugin 即可。

const UglifyPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
  plugins: [
    new UglifyPlugin()
  ],
}
@前端进阶之旅: 代码已经复制到剪贴板

plugin 理论上可以干涉 webpack 整个构建流程,可以在流程的每一个步骤中定制自己的构建需求

输出

构建结果的文件名、路径等都是可以配置的,使用 output 字段

module.exports = {
  // ...
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
}

// 或者多个入口生成不同文件
module.exports = {
  entry: {
    foo: './src/foo.js',
    bar: './src/bar.js',
  },
  output: {
    filename: '[name].js',
    path: __dirname + '/dist',
  },
}

// 路径中使用 hash,每次构建时会有一个不同 hash 值,避免发布新版本时线上使用浏览器缓存
module.exports = {
  // ...
  output: {
    filename: '[name].js',
    path: __dirname + '/dist/[hash]',
  },
}
@前端进阶之旅: 代码已经复制到剪贴板

我们一开始直接使用 webpack 构建时,默认创建的输出内容就是 ./dist/main.js

一个简单的 webpack 配置

我们把上述涉及的几部分配置内容合到一起,就可以创建一个简单的 webpack 配置了,webpack 运行时默认读取项目下的 webpack.config.js 文件作为配置。所以我们在项目中创建一个 webpack.config.js 文件

const path = require('path')
const UglifyPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
  entry: './src/index.js',

  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },

  module: {
    rules: [
      {
        test: /\.jsx?/,
        include: [
          path.resolve(__dirname, 'src')
        ],
        use: 'babel-loader',
      },
    ],
  },

  // 代码模块路径解析的配置
  resolve: {
    modules: [
      "node_modules",
      path.resolve(__dirname, 'src')
    ],

    extensions: [".wasm", ".mjs", ".js", ".json", ".jsx"],
  },

  plugins: [
    new UglifyPlugin(), 
    // 使用 uglifyjs-webpack-plugin 来压缩 JS 代码
    // 如果你留意了我们一开始直接使用 webpack 构建的结果,你会发现默认已经使用了 JS 代码压缩的插件
    // 这其实也是我们命令中的 --mode production 的效果,后续的小节会介绍 webpack 的 mode 参数
  ],
}
@前端进阶之旅: 代码已经复制到剪贴板

# 二、搭建基础的前端开发环境

这一节是全文的主干,六个 Step 走完,你会得到一套能写业务的环境。每个 Step 只解决一个问题,出问题的时候也好定位是哪一步的锅。

先约定一下目录结构,后面所有配置里的路径都基于它:

project/
├── src/
│   ├── index.js        入口
│   ├── index.html      页面模板
│   ├── style.less      样式
│   └── assets/         图片等静态资源
├── package.json
└── webpack.config.js
@前端进阶之旅: 代码已经复制到剪贴板

六个 Step 分别是:关联 HTML、构建 CSS、接上预处理器、处理图片、接入 Babel、启动静态服务。顺序不能随便调,后面几步依赖前面的产物。

# 2.1 Step 1:关联 HTML

webpack 默认从作为入口的 .js 文件进行构建(更多是基于 SPA 去考虑),但通常一个前端项目都是从一个页面(即 HTML)出发的,最简单的方法是,创建一个 HTML 文件,使用 script 标签直接引用构建好的 JS 文件,如:

<script src="./dist/bundle.js"></script>
@前端进阶之旅: 代码已经复制到剪贴板
fe
  • 一、webpack 概念和基础使用
    • 1.1 安装和使用
    • 1.2 webpack 的基本概念
  • 二、搭建基础的前端开发环境
    • 2.1 Step 1:关联 HTML
    • 2.2 Step 2:构建 CSS
    • 2.3 Step 3:接上 CSS 预处理器
    • 2.4 Step 4:处理图片文件
    • 2.5 Step 5:接入 Babel
    • 2.6 Step 6:启动静态服务
    • 2.7 阶段成果:完整示例代码
  • 三、webpack 如何解析代码模块路径
    • 3.1 常用的一些配置
  • 四、配置 loader
    • 4.1 loader 匹配规则
    • 4.2 规则条件配置
    • 4.3 使用 loader 配置
    • 4.4 loader 应用顺序
    • 4.5 完整代码
  • 五、使用 plugin
    • 5.1 DefinePlugin
    • 5.2 copy-webpack-plugin
    • 5.3 extract-text-webpack-plugin
  • 六、更好地使用 webpack-dev-server
    • 6.1 基础使用
    • 6.2 配置
  • 七、开发和生产环境的构建配置差异
    • 7.1 在配置文件中区分 mode
    • 7.2 运行时的环境变量
    • 7.3 常见的环境差异配置
    • 7.4 拆分配置
    • 7.5 完整代码
  • 八、模块热替换提高开发效率
    • 8.1 配置使用 HMR
    • 8.2 module.hot 常见的 API
  • 九、图片加载优化
    • 9.1 CSS Sprites
    • 9.2 图片压缩
    • 9.3 使用 DataURL
    • 9.4 代码压缩
  • 十、分离代码文件
    • 10.1 webpack 4.x 的 optimization
    • 10.2 webpack 3.x 的 CommonsChunkPlugin
  • 十一、进一步控制 JS 体积
    • 11.1 按需加载模块
    • 11.2 以上完整示例代码
  • 十二、上线前 checklist
  • 总结
  • 参考

← 浅谈PWA(Progressive Web App)Taro开发小程序体验 →