为什么要进行工程化
我们在日常的开发中常常会用到第三方框架去开发,但是浏览器无法对我们开发的代码进行解析,所以我们需要使用一些工程化工具(这里我们用的是webpack5)将开发的内容打包(将文件整理、压缩、分类,抽离公共功能),最终使浏览器能够解析我们开发的代码。
打包引擎解析
找到入口 => 解析依赖 => 解析编译 => 分包 => 压缩优化 => 输出目标产物 => koa 渲染目标产物 => 输出页面

打包配置
配置采用 base + dev + prod 三层分离。webpack.base.js 放所有环境共用的基础配置,webpack.dev.js 放开发环境配置, webpack.prod.js 放生产环境配置,生产、开发环境配置通过 webpack-merge 合并webpack.base.js。
基础配置用处(webapck.base.js)
- 获取 app/pages 目录下所有的入口文件 (entry.xx.js)
- entry: 配置打包输入入口
- module: 模块解析配置
- .vue: vue-loader
- .js: babel-loader
- .(png|jpg|jepg|gif): url-loader
- .css: style-loader, css-loader
- .less: style-loader, css-loader, less-loader
- .(eot|svg|ttf|woff|woff2): file-loader
- output: 产物输出路径, 因为开发和生产环境不一致, 所以在各自环境中进行配置
- resolve: 配置模块解析的具体行为(定义 webpack 在打包时,如何找到并解析具体模块的路径)
- plugin: webpack 插件
- vue-loader: 解析 .vue 文件的必需插件
- webpack.ProvidePlugin: 将第三方库的内容暴露到 window.context 下
- webpack.DefinePlugin: 定义全局的常量
- html-webpack-plugin: 构建最终渲染的页面模板
- optimization: 配置打包输出优化(代码分割, 模块合并, 缓存, TreeShaing, 压缩优化策略)
把 js 文件打包成3种类型( 目的: 把改动和引用频率不一样的 js 区分出来, 以达到更好利用浏览器缓存的效果)- vendor: 第三方 lib 库, 基本不会改动, 除非依赖版本升级
- common: 业务组件代码的公共部分抽取出来,改动较
- entry.{page}: 不用页面 entry 里的业务组件代码的差异部分,会经常改动
生产环境配置用处(webpack.prod.js 主要为压缩大小与加快速度)
- .css抽离与压缩: .css => css-loader => mini-css-extract-plugin抽离为独立.css 文件 => css-minimizer-webpack-plugin压缩
- 多线程打包: 使用happyPack
- 构建前清理: 使用clean-webpack-plugin
- 提升压缩阶段性能和清除console.log:使用terser-webpack-plugin
开发环境配置用处(webpack.dev.js 主要为方便代码调试与热更新)
-
开发时的调试支持: 配置Source Map
-
开发时热更新(HMR):HMR需要
webpack-dev-middleware(监控文件变更并重新编译的服务)、webpack-hot-middleware(把更新的内容推送给浏览器,基于SSE)、HMR Client(就是每个入口页面,入口页面需要配置HMR连接路径)三块配合才能实现- 配置入口页面HMR连接路径(path: SSE连接地址)
js// webpack.dev.js Object.keys(baseConfig.entry).forEach(v => { // 第三方包不作为 hmr 入口 if (v !== 'vendor') { baseConfig.entry[v] = [ // 主入口文件 baseConfig.entry[v], // hmr 更新入口, 官方指定的 hmr 路径 `webpack-hot-middleware/client?path=http://${DEV_SERVER_CONFIG.HOST}:${DEV_SERVER_CONFIG.PORT}/${DEV_SERVER_CONFIG.HMR_PATH}&timeout=${DEV_SERVER_CONFIG.TIMEOUT}` ] } }- 启用
HotModuleReplacementPlugin
js// webpack.dev.js plugins: [ // HotModuleReplacementPlugin 用于实现热模块替换 (Hot Module Replacement 简称 HMR) // 模块热替换允许应用程序运行时替换模块 // 极大的提升开发效率,因为能让应用程序一直保持运行状态 new webpack.HotModuleReplacementPlugin({ multiStep: false // 不分步编译,每次变更一次性编译完成 }) ]- 启用DevServer
这里是使用express起了一个DevServer(端口: 9092, 负责webpack编译内容的分发和HMR推送),和koa业务服务器(端口: 8080, 负责页面路由和API)是有区别的
因为两个服务器配置端口不同会有跨域问题,所以配置了headers解决
devMiddleware编译的文件除了.tpl写磁盘文件中(因为koa的渲染引擎nunjucks需要从文件中读取tpl),其他全写内存中(读写速度更快)
js// dev.js const app = express(); const compiler = webpack(webpackConfig); const devMiddleware = require('webpack-dev-middleware'); const hotMiddleware = require('webpack-hot-middleware'); // 引用 devMiddleware 中间件 (监控文件改动) app.use(devMiddleware(compiler, { // 落地文件 writeToDisk: (filePath) => filePath.endsWith('.tpl'), // 资源路径 publicPath: webpackConfig.output.publicPath, // headers 配置 headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS', 'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization' } })); // 引用 hotMiddleware 中间件 (实现热更新通讯) app.use(hotMiddleware(compiler, { path: `/${DEV_SERVER_CONFIG.HMR_PATH}`, log: () => {} })); app.listen(9002);