学习
实践
活动
专区
工具
TVP
写文章
专栏首页前端皮小蛋「项目实战」优化项目构建时间

「项目实战」优化项目构建时间

背景

前几天晚上下班的时候, 路过隔壁项目组, 听他们在聊项目构建的事:

现在线上打包时间太长了, 修个 bug 1 分钟, 发布一下半小时, 贼难受。

他们项目比较庞大, 线上构建时间特别长, 基本都在15分钟以上

和他们简单聊了会, 回去瞅了一下自己项目的构建时间:

其实也挺长的, 于是抽空优化了一下, 效果还是比较明显的。

优化后:

在正文部分,我将分享的内容主要是:

  • 一些提升 webpack 打包性能的配置
  • 优化大型项目构建时间的一些思考

希望对大家有所启发。

正文

我们项目不是很大, 是一个中型的国际化项目, 一百来个页面。

之前本地构建时间挺长的,初次启动要三次分钟, 后面我配置了 Vite, 本地启动时间降低到了 20s 左右,感兴趣的可以移步我这篇文章:

[项目实战] Webpack to Vite, 为开发提速!

看了一下,线上构建时间五六分钟,不痛不痒,但是应该也有优化空间,于是准备优化一下。

1. 发现问题

既然要优化构建时间, 第一步当然是先发现问题, 找出比较耗时的阶段,再加以优化。

这里我用到了SMP 插件。

SMP 插件用法非常简单, 这里也简单提一下:

// webpack.config.js
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const smp = new SpeedMeasurePlugin();

module.exports = smp.wrap({
  // ...
});

利用 SMP 插件得出各个阶段的打包耗时:

发现问题

发现两个比较明显的问题:

  1. IgnorePlugin 耗时接近 20 秒。
  2. less-loader 部分执行了2次,浪费了一分多钟。
  3. ts-loader 耗时一分半, 也挺长的。

2. 解决问题

1. IgnorePlugin

查看了一下配置, 发现配置里的 IgnorePlugin 并没有达到预期的效果, 删掉。

2. less-loader

查看配置后发现, 在处理less的部分,确实多处理了一遍。

less 文件的处理,可以直接看官方文档,文档地址:

https://webpack.docschina.org/loaders/less-loader/

我的配置:

{
  test: /\.less$/,
  use: [
    'style-loader',
    'css-loader',
    {
      loader: 'less-loader',
      options: {
        javascriptEnabled: true,
        sourceMap: true,
        modifyVars: {
          // inject our own global vars
          // https://github.com/ant-design/ant-design/issues/16464#issuecomment-491656849
          hack: `true;@import '${require.resolve('./src/vars.less')}';`,
          ...themeVariables,
        },
        limit: 10000,
        name: '[name].[hash:7].[ext]',
        outputPath: 'styles/',
      },
    },
  ],
},
{
  test: /\.css$/,
  use: ['style-loader', 'css-loader'],
},

3. ts-loader

对于ts-loader部分的优化, 可以参考:

https://webpack.js.org/guides/build-performance/#typescript-loader

文档上也有比较清晰的描述:

文档建议, 我们开启transpileOnly选项,关闭类型检查。

如果要类型检查, 可以使用 ForkTsCheckerWebpackPlugin,这个插件会在另外一个进程中做相关的检查。

这个插件,我们在优化构建时内存溢出的问题上, 也做了探索, 感兴趣的可以移步我这篇文章:

项目构建内存溢出了?看看 Node 内存限制

现在我们也开启这个选项。

开启之后, 本地构建的时候, 本地报了个警告:

这个错误, 十分的眼熟, 是之前我们讲过的 import type 的问题:

你不知道的 「 import type 」

修复一下:

问题解决。

重新构建, 得到如下结果:

优化之后之后, 我们发现:

  • IgnorePlugin、HtmlWebpackPlugin 时间大幅缩短。
  • less-loader 等恢复了正常,只执行了一次。
  • ts-loader 时间大幅缩短,由1分30秒缩短为40秒。

本地效果明显,需要去线上构建验证。

3. 确认有效

在线上执行之后, 得到如下结果:

然后去检查了一下页面,也都是正常的。

完美!

回头看,不难发现,其实也没改多少东西, 就收获了不错的效果。

针对中小型项目来说, 改改配置往往就能达到我们的要求, 但是如果是面对大型项目呢?

比如那种数十个模块, 几百个页面的项目。

回到开头那个问题:修个 bug 1 分钟, 发布一下半小时

简单的修改配置, 都无法把时间降下来, 这时候该怎么办呢?

优化大型项目构建时间的一些思考

拆分子应用

假设我们有一个项目,大模块就有将近30个:

每个大模块里面又有几十个页面,这种系统构建时间会比较久, 需要做优化。

而且到了项目后期,问题会越来越明显, 比如:

  • 代码越来越臃肿
  • 业务模块本身无关联
  • 构建速度越来越慢
  • 无法独立部署

面对这种情况,一种可行的做法是:拆分子应用

拆分之后的架构:

每个子项目都有单独的入口, 是可以独立部署的项目。

子项目打成单独umd包:

在主项目启动的时候, 再去加载这些子项目:

加载完成之后, 需要处理路由以及store, 示例代码:

// base
export const bootstrap = () => {
  // ...
  ReactDOM.render((
    <Provider store={store}>
      <Router history={history}>
        <App defaultInitialData={_initialData} />
      </Router>
    </Provider>
  ), document.getElementById('root'));
  return Promise.resolve();
};

// main
const loadSubApp = (htmlEntry: string) => {
  return importHTML(`${htmlEntry}?${Date.now()}`)
    .then((res: any) => res.execScripts())
    .then((exportedValues: any) => {
      console.log(`importHTML: ${htmlEntry} loaded, exports:`, exportedValues);
      const { store, router } = exportedValues || {} as any;
      router && addCustomRouter(router);
      store && addCustomStore(store);
    })
    .catch(e => {
      console.error('importHTML: ${htmlEntry} load error:', e);
    });
};

const load = () => {
  if (__ENV__ !== 'dev') {
    const paths: string[] = [];
    subAppConfig.subApps.forEach(item => {
      if (item.project === localStorage.getItem('ops_project')) {
        paths.push(...item.paths);
      }
    });
    Promise.all(paths.map(path => loadSubApp(path)))
      .catch(e => console.log(e))
      .finally(setAllLoaded);
  } else {
    setAllLoaded();
  }
};

const init = () => {
  console.log('init: Start to bootstrap the main APP');
  addCustomStore(rootStore);
  bootstrap().then(() => {
    load();
  });
};

init();

代码共享

  • common
    • component
    • utils
    • typings
    • ..
  • externals
    • react全家桶
    • moment
    • antd
    • ..

样式隔离

给样式添加以子项目为名的 namespace :

开发调试

以 ops 项目为例。

让开发调试 ops-common 包像本地文件一样方便:

  1. 让项目来编译 common 包
  1. wepback alias
  1. TS alias

独立部署

在同一个project上为每个子项目申请独立module

拆分子应用的优缺点

优点:

  • 每个子应用都可以独立发布, 子模块和主模块解耦。
  • 子项目是可以单独编译的,主项目只需要做引入即可, 以此减少主模块的构建时间

缺点:

  • 额外的复杂性和维护成本

结论

一般来说,对于中小型项目,做好打包配置的优化, 能够解决一部分问题。

大型项目的构建时间优化, 可以考虑拆分子应用的模式。

只不过这种模式需要考虑一些维护的问题,比如如何维护版本 tag、如何快速回滚等。

这些需要结合你们项目的实际情况再做决定。

今天的内容就这么多,希望对大家有所启发。

最后

祝大家五一快乐~~

如果觉得文章内容有帮助, 可以关注下我哦, 掌握最新动态。

也可以加我微信 「 scaukk 」, 一起探讨。

文章分享自微信公众号:
前端皮小蛋

本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!

作者:南山皮小蛋
原始发表时间:2021-05-01
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • SpringCloud实战:项目准备,构建大型实战项目

    本章中,我们将开始一个大型实战项目——博客网站。通过“以战代练”的方式来学习如何构建Spring Cloud微服务架构,让读者走出理论的丛林,在实践中玩转微服务...

    愿天堂没有BUG
  • 项目实战:如何构建知识图谱

    作者丨徐阿衡 学校丨卡耐基梅隆大学硕士 研究方向丨QA系统 实践了下怎么建一个简单的知识图谱,两个版本,一个从 0 开始(start from scratch)...

    小莹莹
  • 项目总结 | 对 "时间" 构建的特征工程

    写文章的目的在于之前面试的时候,提到某一个时间序列项目的特征工程处理。我说的大多数都是一些数据清洗、数据去除异常点、针对数据特性做出的特别的特征工程的操作,然后...

    机器学习炼丹术
  • 项目构建

    SConstruct文件 https://blog.csdn.net/lusic01/article/details/69643093

    sofu456
  • 项目开发实战_go项目实战

    TodoMVC 是一个非常经典的案例,功能非常丰富,并且针对多种不同技术分别都开发了此项目,比如React、AngularJS、JQuery等等。

    全栈程序员站长
  • 使用 craco 对 cra 项目进行构建优化

    修改 CRA 项目的配置使用 create-react-app 创建的项目默认是无法修改其内部的 webpack 配置的,不像 vue-cli 那样可以通过一个...

    用户1476962
  • kubernetes下jenkins实战maven项目编译构建

    在kubernetes环境部署的jenkins集群,执行任务时会新建pod,任务完成后pod被销毁,架构如下:

    程序员欣宸
  • react项目实战教程(react项目实战)

    React基础 React组件 React-Router——Reac路由的学习 React高阶组件及CRA的定制 React中使用Antd组件

    全栈程序员站长
  • mysql数据库优化(四)-项目实战

    在flask项目中,防止随着时间的流逝,数据库数据越来越多,导致接口访问数据库速度变慢。所以自己填充数据进行测试及 mysql优化

    用户1558882
  • [Bazel]构建Golang项目

    Bazel 支持很多内置的规则,语言相关规则有 Shell、Objective-C、C++ 和 Java,比如 sh_binary、cc_binary、cc_i...

    别打名名
  • Java 项目热部署,节省构建时间的正确姿势

    简而言之,不管你修改了类还是资源,只需要重新 Build 一下相关的类,改动就直接反映到你的应用程序了。

    JavaFish
  • 【项目实战】记一次项目实战

    上面这个是需要做渗透测试的地址,高度打码望谅解,还是开始最常见的手段我们从信息收集开始吧,打开前端地址探测一波 云悉查一下

    天钧
  • WPF项目实战WeMail - 项目准备

    http://mpvideo.qpic.cn/0bf22maayaaamqapbe2ogzqvbu6dbtjqadaa.f10002.mp4?dis_k=336...

    DotNet致知
  • flutter实战项目之博客项目

    https://www.bilibili.com/video/bv1uy4y1t7LX

    nuts
  • 第十三章:SpringBoot实战SpringDataJPA构建项目总结

    恒宇少年
  • php项目优化

    如果a目录被黑,这样会被限制到一定范围的目录中,目录最后一定要加上/,比如/tmp/,而不是/tmp

    陈不成i
  • Flutter项目优化

    应用的性能表现不尽如人意,主要体现在启动慢、页面加载慢,页面滑动操作不流畅、卡顿等,非常影响用户使用体验。需要对应用做了优化工作,包括安装包瘦身、加快启动速度、...

    愤怒的小鸟

扫码关注腾讯云开发者

领取腾讯云代金券