学习总结 前端工程化总结

Elpis项目工程化实践分析

目录

多页面构建机制

入口文件识别

Elpis项目采用了自动化的多页面构建机制,通过约定式的入口文件命名规则实现:

javascript 复制代码
// webpack.base.js 中的实现
const glob = require("glob");
const path = require("path");

// 动态构造 pageEntries 和 HtmlWebpackPluginList
const pageEntries = {};
const HtmlWebpackPluginList = [];
// 获取 app/pages 目录下所有入口文件(entry.xx.js)
const entryList = path.resolve(process.cwd(), './app/pages/**/entry.*.js');
glob.sync(entryList).forEach((file) => {
  const entryName = path.basename(file, '.js');
  // 构造entry
  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],
    })
  )
});

核心工作原理

  1. 约定式命名 :所有页面入口必须以entry.页面名.js形式命名
  2. 自动扫描 :使用glob库扫描app/pages目录下所有符合规则的入口文件
  3. 动态配置 :根据扫描结果自动构建webpack的entry配置和HtmlWebpackPlugin插件配置
  4. 模板生成 :为每个页面生成独立的.tpl模板文件,注入对应的js资源

页面入口结构

每个页面入口文件通过标准化的引导方式创建Vue应用:

javascript 复制代码
// app/pages/page1/entry.page1.js
import Page1 from './page1.vue';
import boot from '../boot';
boot(Page1);

统一的引导文件boot.js处理常见的初始化逻辑:

javascript 复制代码
// boot.js主要功能
export default (pageComponent, { routes, libs } = {}) => {
  const app = createApp(pageComponent);
  // 使用element-plus
  app.use(ElementPlus);
  // 使用pinia
  app.use(pinia);
  // 使用第三方库和路由
  // ...
  app.mount("#root");
}

分包策略

代码分割方案

Elpis项目使用了精细的代码分割策略,将代码分为三类:

javascript 复制代码
// webpack.base.js中的分包配置
optimization: {
  splitChunks: {
    chunks: 'all', // 对同步和异步模块都进行分割
    maxAsyncRequests: 10, // 每次异步加载的最大并行请求数
    maxInitialRequests: 10, // 入口点的最大并行请求数
    cacheGroups: {
      vendor: { // 第三方依赖库
        test: /[\\/]node_modules[\\/]/, // 打包 node_modules 下的模块
        name: 'vendor', // 模块名称
        priority: 20, // 优先级,数字越大,优先级越高
        enforce: true, // 强制执行
        reuseExistingChunk: true, // 复用已有的公共 chunk
      },
      common: { // 业务代码公共部分
        name: 'common', // 模块名称
        minChunks: 2, // 被两处引用即被归为公共模块
        minSize: 1, // 最小分割文件大小(1 byte)
        priority: 10, // 优先级,数字越大,优先级越高
        reuseExistingChunk: true, // 复用已有的公共 chunk
      }
    }
  },
  // 将 webpack 运行时生成的代码打包到 runtime.js
  runtimeChunk: true,
}

分包类型详解

  1. vendor包

    • 包含所有第三方库代码(node_modules)
    • 变动频率最低,适合长期缓存
    • 优先级最高,确保第三方库代码不会被其他规则分割
  2. common包

    • 包含业务代码中的公共部分
    • 条件是被至少两个不同页面引用
    • 变动频率适中,适合中期缓存
  3. 页面特定代码

    • 每个页面特有的业务逻辑
    • 变动频率最高,适合短期缓存
  4. runtime包

    • 包含webpack的运行时代码
    • 独立抽取,避免影响其他包的缓存效果

缓存优化机制

开发环境和生产环境使用不同的文件名策略:

javascript 复制代码
// 开发环境(webpack.dev.js)
output: {
  filename: 'js/[name]_[chunkhash:8].bundle.js',
  //...
}

// 生产环境(webpack.prod.js)
output: {
  filename: 'js/[name]_[chunkhash:8].bundle.js',
  //...
}

使用chunkhash确保文件内容变化时哈希值才会变化,有效利用浏览器缓存。

热更新原理

开发环境配置

Elpis项目中的热更新(HMR)主要通过以下方式实现:

javascript 复制代码
// webpack.dev.js 中的热更新配置
// 开发阶段的 entry 配置需要加入 hmr
Object.keys(baseConfig.entry).forEach(v => {
  // 第三方包不作为 hmr 的入口
  if (v !== 'vendor') {
    baseConfig.entry[v] = [
      // 主入口文件
      baseConfig.entry[v],
      `webpack-hot-middleware/client?path=http://${DEV_SERVER_CONFIG.HOST}:${DEV_SERVER_CONFIG.PORT}/${DEV_SERVER_CONFIG.HMR_PATH}&timeout=${DEV_SERVER_CONFIG.TIMEOUT}&reload=true`,
    ];
  }
});

// 开发阶段插件
plugins: [
  // 热更新插件 用于实现热模块替换
  // 模块热替换允许在应用程序运行时替换模块
  // 极大的提升开发效率,因为能让应用程序一直保持运行状态
  new webpack.HotModuleReplacementPlugin({
    multiStep: true,
  }),
],

HMR工作流程

  1. 客户端注入

    • 为每个页面入口添加webpack-hot-middleware/client客户端脚本
    • 客户端与服务器建立WebSocket连接,监听变更
  2. 服务器监听

    • 使用webpack-dev-middlewarewebpack-hot-middleware在服务器端监控文件变化
    • 当文件变化时,重新编译并通知客户端
  3. 模块替换

    • 客户端接收到变更通知后,请求更新的模块代码
    • 通过HMR运行时API安全地替换旧模块,而不需刷新整个页面
    • Vue单文件组件天然支持HMR,组件自动更新
  4. 多步骤更新

    • 使用multiStep: true选项实现更细粒度的热更新
    • 减少单次更新的体积,提高更新效率

构建优化

开发环境优化

javascript 复制代码
// 开发环境特定配置
mode: 'development',
// source-map 开发工具,呈现代码的映射关系,便于在开发过程中调试代码
devtool: 'eval-cheap-module-source-map',

生产环境优化

javascript 复制代码
// 生产环境配置
mode: 'production',
// 生产环境不需要 source map 或使用适合生产的轻量级 source map
devtool: false,

// 压缩和优化
optimization: {
  minimize: true,
  minimizer: [
    new TerserWebpackPlugin({
        cache: true, // 启用缓存来加速构建过程
        parallel: true, // 利用多核 CPU 加速构建过程
        terserOptions: {
          compress: {
            drop_console: true, // 删除所有的 console.*
          },
        },
      }),
  ],
}

加载器优化

javascript 复制代码
// 只对业务代码进行babel转换,加快构建速度
{
  test: /\.js$/,
  include: [
    // 只对业务代码进行 babel ,加快 webpack 打包速度
    path.resolve(process.cwd(), 'app/pages'),
  ],
  use: {
    loader: 'babel-loader',
  }
}

工程化价值

多页面应用的优势

  1. 独立部署:各页面可以独立开发、测试和部署
  2. 按需加载:用户只需加载当前访问页面的资源
  3. 资源隔离:页面间资源互不影响,降低耦合度
  4. 性能优化:首屏加载更快,用户体验更好

分包策略的价值

  1. 缓存效率:不同变更频率的代码分开打包,优化缓存策略
  2. 并行加载:多个小包可以并行下载,提高资源加载速度
  3. 资源复用:公共模块只需加载一次,减少重复下载
  4. 按需加载:结合动态导入实现真正的按需加载

热更新的价值

  1. 开发效率:无需手动刷新,保持应用状态
  2. 即时反馈:修改代码后立即看到效果
  3. 状态保持:保留应用运行状态,便于调试
  4. 提升体验:减少等待时间,提高开发体验

工程化实践总结

  1. 规范化:通过约定式开发降低协作成本
  2. 自动化:通过工具链自动完成重复工作
  3. 模块化:清晰的模块划分提高代码可维护性
  4. 优化化:针对不同环境的优化策略提高性能

By:抖音"哲玄前端"《大前端全栈实践》

相关推荐
南屿欣风6 分钟前
解决 Gin Web 应用中 Air 热部署无效的问题
前端·gin
猿大师办公助手9 分钟前
Web网页内嵌福昕OFD版式办公套件实现在线预览编辑PDF、OFD文档
前端·pdf·word
幼儿园技术家1 小时前
什么是RESTful 或 GraphQL?
前端
echola_mendes2 小时前
LangChain 结构化输出:用 Pydantic + PydanticOutputParser 驯服 LLM 的“自由发挥”
服务器·前端·数据库·ai·langchain
拉不动的猪2 小时前
刷刷题46(常见的三种js继承类型及其优缺点)
前端·javascript·面试
关注我:程序猿之塞伯坦2 小时前
JavaScript 性能优化实战:突破瓶颈,打造极致 Web 体验
开发语言·前端·javascript
兰德里的折磨5502 小时前
对于后端已经实现逻辑了,而前端还没有设置显示的改造
前端·vue.js·elementui
hikktn2 小时前
【开源宝藏】30天学会CSS - DAY9 第九课 牛顿摆动量守恒动画
前端·css·开源
申朝先生3 小时前
面试的时候问到了HTML5的新特性有哪些
前端·信息可视化·html5
在下千玦3 小时前
#前端js发异步请求的几种方式
开发语言·前端·javascript