前言
学习声明:本文知识体系来源于
哲玄前端(抖音ID:44622831736)大前端全栈实践课程,结合个人学习实践进行整理。
什么是工程化?
在工作中我们使用打包工具的进行配置,它帮我们做了什么?它是如何监听文件的?环境不同时它是如何处理的?等等...
所以让我们思考一下知道如何配置这个工具就是工程化了吗?我想答案不全是。
我认为理解前端工程化不仅仅是利用众多的打包工具进行项目配置,而是要理解它的设计思想,只有理解了思想我们才能在众多的打包工具(比如:Webpack、Vite、Rollup...)中更好的利用它。
我认为前端工程化是将前端开发流程标准化、自动化以提高效率和维护性的一种思想。
工程化如何进行?业务文件-->解析引擎-->打包产物
举个例子
- 开发人员编写好项目业务组件
- 解析引擎首先会从入口文件(通过
entry配置)开始一层一层读取依赖业务文件 - 解析引擎通过模块加载器配置(
module)一系列的loader(例如:vue-loader、babel-loader、css-loader、url-loader...)配合插件加载器(plugin),输出浏览器能够识别的前端三件套(.html, .css, .js)文件 - 将输出的文件进行依赖分析,然后进行模块分包(
splitChunks)等一系列优化 - 区分不同环境,进入不同优化流程的处理,比如
生产环境压缩js、css、处理图 片资源 等等然后输出产物到文件系统,开发环境除了入口文件输出到文件系 统,其他的资源注 入到内存中,提高开发效率。 - 最后通过
output配置,输出产物到文件系统
上面的流程这就是一个项目工程化的体现
多页面打包如何处理?
从上面的图示中可以知晓,我们的项目有多个入口文件,这就意味着我们需要配置多入口文件打包。我们知道多页面打包意味着需要多个入口,然后我们需要通过处理后为每个入口生成独立的文件,同时注入每个文件需要的资源,然后通过SSR的方式给到前端访问。
多入口文件
多页面入口核心配置
let htmlWebpackPluginList = [];
// 约定所有的入口文件都是 entry.xxxx.js形式
const entryPath = path.resolve(process.cwd(), "./app/pages/**/entry.*.js");
glob.sync(entryPath).forEach(file => {
// path.basename: 获取文件名并删除后缀
const entryName = path.basename(file, ".js");
pageEntries[entryName] = file;
htmlWebpackPluginList.push(
// html-webpack-plugin 辅助注入打包后的 bundle 文件到 tpl 中
new HtmlWebpackPlugin({
// 产物(最终模板)输出路径
filename: path.resolve(process.cwd(), "./app/public/dist/", `${entryName}.tpl`),
// 指定要使用的模板文件
template: path.resolve(process.cwd(), "./app/view/entry.tpl"),
// 要将产物要注入到哪些代码块
chunks: [entryName]
})
);
});
打包产物
产物输出了多个页面的入口,这些入口文件引入了各自需要的资源,服务端通过SSR的方式返回给前端需要的具体页面。
不同环境项目如何进行工程化?
生产环境:追求加载速度快,代码安全
对于生产我们需要考虑以下问题:
- 产物的体积:通过压缩代码体积,删除无用代码等等减小代码体积,图片资源可以通过CDN等等处理。
- 分包处理:产物通过配置
optimization --> splitChunks进行分包,将第三方包、公共的包达成单独的文件进行共用,合理利用缓存体速。 - 保证代码的安全: 可以使用代码混淆,删除注释等等。
- 保证代码的兼容性。
开发环境:追求开发体验,调试方便
开发环境需要考虑的问题
- 更方便的调试、排查问题。可以利用source-map、或者不使用代码混淆来处理这方面的问题
- 开发体验感更好,即热更新的处理。
模块如何分包?遵循什么原则?
模块分包作为优化的一大重要手段,可以通过webpack 的 optimization splitChunks 进行分包,在这里遵循下面的原则把 js 分为三类模块包。可根据具体得项目进行其他一些个性化配置
- vender: 第三方 lib 库, 基本不会改动,除非依赖的版本升级
- common: 业务组件代码引入的公共模块,很少改动
- entry.{page}: 不同页面 entry 里的业务组件代码的差异部分,经常改动
目的:把改动较大和引用频率不多的js区分出来,以便更好的利用浏览器缓存,提高性能能
具体配置
splitChunks: {
// 对同步异步模块都进行分割
chunks: "all",
// 每次异步加载的最大并行次数
maxAsyncRequests: 10,
// 入口点的最大并行请求数
maxInitialRequests: 10,
cacheGroups: {
// 第三方包配置
vender: {
// 匹配第三方包,正则兼容不同平台的兼用性
test: /[\\/]node_modules[\\/]/,
// 模块包的名称
name: "vender",
// 缓存组的优先级, 数字越大,优先级越高
priority: 20,
// 是否强制执行
enforce: true,
// 是否共享缓存,即如果其他模块已经分出来这个包,那么它将被重用,不会再次分出来一样的包
reuseExistingChunk: true
},
// 业务组件公共配置
common: {
name: 'common',
// 模块包中引用次数,如果引用次数大于等于 minChunks,则被分割到公共包中
minChunks: 2,
// 模块包最小分割分拣大小,单位字节
minSize: 1,
priority: 10,
reuseExistingChunk: true
}
}
},
通过上述的配置最终的产物效果如下。
- vender文件是我们在具体的文件中引用的node_nodules中的包打成的单独文件。
- common文件是在page1.vue、page2.vue文件中共同引用的文件,在配置中我们设置minChukns:2,当包被引用2次即2次以上时就会打出来单独的文件。
- entry.{page} 文件是把各自资源注入到这个文件的具体页面文件。
热更新(Hot Module Replacement)如何实现?
在这里我们实现热更新并非通过配置webpack 的 devServer: { hot: true }实现,而是使用express结合webpack-dev-middleware、webpack-hot-middleware 实现一个开发服务器图示实现热更新的设计
webpack-dev-middleware: 监听业务文件的变化。
webpack-hot-middleware:通过在代码中注入一段 HMR客户端 代码来通知并监听浏览器的更新。
注入的HMR代码
具体关键代码实现
/* webpack.dev.js */
// devServer配置
const DEV_SERVER_CONFIG = {
HOST: "127.0.0.1",
PORT: "9002",
HMR_PATH: "__webpack_hmr",
TIMEOUT: 20000
};
// 配置开发环境支持热更新(HMR)
Object.keys(baseConfig.entry).forEach(key => {
// 解构
const { HOST, PORT, HMR_PATH, TIMEOUT } = DEV_SERVER_CONFIG;
// 第三方包不需要作为 HMR 入口
if (key !== "vendor") {
baseConfig.entry[key] = [
baseConfig?.entry[key],
// 在打包产物中注入HMR代码,webpack官方指定的 HMR 路径
`webpack-hot-middleware/client?path=HTTp://${HOST}:${PORT}/${HMR_PATH}?timeout=${TIMEOUT}&reload=true`
]
}
});
/* dev.js */
// 引入 webpack-dev-middleware,监听文件改动
// 它可以把 webpack 处理过的文件发送给 server
app.use(devMiddleware(compiler, {
// 将模板文件写入磁盘
writeToDisk: (filePath) => filePath.endsWith(".tpl"),
// 资源路径,需要与 webpack.dev.js 设置的 output.publicPath 一致
publicPath: webpackConfig.output.publicPath,
// 设置允许跨域请求
headers: {
"Access-Control-Allow-Origin": "*",
// 设置请求方法
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
// 跨域请求中允许的 HTTP 请求信息
"Access-ConTroll-Allow-Headers": "Content-type, Authorization, X-Requested-With"
},
// 彩色打印
stats: {
colors: true
}
}));
// 引入 webpack-hot-middleware, 实热更新通讯
app.use(hotMiddleware(compiler, {
log: () => { },
path: `/${DEV_SERVER_CONFIG.HMR_PATH}`
}));
总结
前端工程化是前端开发的重要的一环,它的核心是通过模块化、自动化和优化来提高开发效率和用户体验,它扮演着基础设施的重要角色。当然,工程化不只是只有这一块,持续集成/持续部署(CI/CD)也是工程化的重要一环。
学习声明:本文知识体系来源于
哲玄前端(抖音ID:44622831736)大前端全栈实践课程,结合个人学习实践进行整理。