百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

我是如何把vue项目启动时间从70s优化到7秒的

bigegpt 2024-11-06 16:36 18 浏览

可怕的启动时间

公司的产品是一个比较大的后台管理系统,而且使用的是webpack3的vue模板项目,单次项目启动时间达到了70s左右

启动个项目都够吃一碗豆腐脑了,可是没有豆腐脑怎么办,那就优化启动时间吧!

考虑到升级webpack版本的风险还是比较大的,出了一点问题都得找我,想想还是先别冒险,稳妥为主,所以我选择了通过插件来优化构建时间。

通过查阅资料,提升webpack的构建时间有以下几个方向:

  • 多进程处理文件,同一时间处理多个文件
  • 预编译资源模块,比如把长时间不变的库提取出来做预编译,构建的时候直接取编译结果就好
  • 缓存,未修改的模块直接拿到处理结果,不必编译
  • 减少构建搜索和处理的文件数量

针对以上几种优化方向,给出以下几种优化方案。

多进程构建

happypack

happypack 的作用就是将文件解析任务分解成多个子进程并发执行。

子进程处理完任务后再将结果发送给主进程。所以可以大大提升 Webpack 的项目构件速度。

查看happypack的github,发现作者已经不再维护该插件,并且作者推荐使用webpack官方的多进程插件thread-loader,所以我放弃了happypacy,选择了thread-loader。

thread-loader

thread-loader是官方维护的多进程loader,功能类似于happypack,也是通过开启子任务来并行解析文件,从而提高构建速度。

把这个loader放在其他loader前面。不过该loader是有限制的。示例:

  • loader无法发出文件。
  • loader不能使用自定义加载器API。
  • loader无法访问网页包选项。

每个worker都是一个单独的node.js进程,其开销约为600毫秒。还有进程间通信的开销。在小型项目中使用thread-loader可能并不能优化项目的构建速度,反而会拖慢构建速度,所以使用该loader时需要明确项目构建构成中真正耗时的过程。

我的项目中我主要是用该loader用来解析vue和js文件,作用于vue-loaderbabel-loader,如下代码:

const threadLoader = {
  loader: 'thread-loader',
  options: {
    workers: require('os').cpus().length - 1,
  }
}

module.exports = {
  module:{
  rules: [
      {
        test: /\.vue$/,
        use: [
          threadLoader, // vue-loader前使用该loader
          {
            loader: 'vue-loader',
            options: vueLoaderConfig
          }
        ],
      },
      {
        test: /\.js$/,
        use: [
          threadLoader, // babel-loader前使用该loader
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true
            }
          }
        ]
      }
    ]
  }
}

复制代码

配置了thread-loader后,重新构建试试,如下图所示,大概缩短了10秒的构建时间,还不错。

利用缓存提升二次构建的速度

虽然使用了多进程构建项目使构建时间缩短了10秒,但是一分钟的构建时间依然让人无法接受,这种挤牙膏似的优化方式多少让人有点不爽,有没有比较爽的方法来进一步缩短构建时间呢?

答案是有的,使用缓存。

缓存,不难理解就是第一次构建的时候将构建的结果缓存起来,当第二构建时,查看对应缓存是否修改,如果没有修改,直接使用缓存,由此,我们可以想象,当项目的变化不大时,大部分缓存都是可复用的,拿构建的速度岂不是会有质的飞跃。

cache-loader

说到缓存,当然百度一查,最先出现的就是cache-loader,github搜索下官方文档,得到如下结果:

该loader会缓存其他loader的处理结果,把该loader放到其他loader的前面,同时该loader保存和读取缓存文件也会有开销,所以建议在开销较大的loader前使用该loader。

文档很简单,考虑到项目中的vue-loaderbabel-loadercss-loader会有比较大的开销,所以为这些loader加上缓存,那么接下来就把cache-loader加到项目中吧:

const cacheLoader = {
  loader: 'cache-loader'
}

const threadLoader = {
  loader: 'thread-loader',
  options: {
    workers: require('os').cpus().length - 1,
  }
}

module.exports = {
  module:{
  rules: [
      {
        test: /\.vue$/,
        use: [
          cacheLoader,
          threadLoader, // vue-loader前使用该loader
          {
            loader: 'vue-loader',
            options: vueLoaderConfig
          }
        ],
      },
      {
        test: /\.js$/,
        use: [
          cacheLoader,
          threadLoader, // babel-loader前使用该loader
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true
            }
          }
        ]
      }
    ]
  }
}

复制代码

util.js文件中,该文件主要是生成css相关的webpack配置,找到generateLoaders函数,修改如下:

  const cacheLoader = {
    loader: 'cache-loader'
  }
  
  function generateLoaders(loader, loaderOptions) {
  // 在css-loader前增加cache-loader
    const loaders = options.usePostCSS ? [cacheLoader, cssLoader, postcssLoader] : [cacheLoader, cssLoader]

    if (loader) {
      loaders.push({
        loader: loader + '-loader',
        options: Object.assign({}, loaderOptions, {
          sourceMap: options.sourceMap
        })
      })
    }

    // Extract CSS when that option is specified
    // (which is the case during production build)
    if (options.extract) {
      return ExtractTextPlugin.extract({
        use: loaders,
        fallback: 'vue-style-loader',
        // 添加这句配置解决element-ui的图标路径问题
        publicPath: '../../'
      })
    } else {
      return ['vue-style-loader'].concat(loaders)
    }
  }
复制代码

如上配置完成后,再次启动项目,可以发现,现在的启动时间没什么变化,然后我们二次启动项目,可以发现现在的启动时间来到了30s左右,前面我们已经说过了,cache-loader缓存只有在二次启动的时候才会生效。

虽然项目启动时间优化了一半还多,但是我们的欲望是无限大的,30秒的时间离我们的预期还是有点差距的,继续优化!

hard-source-webpack-plugin

HardSourceWebpackPlugin是一个webpack插件,为模块提供中间缓存步骤。为了查看结果,您需要使用此插件运行webpack两次:第一次构建将花费正常的时间。第二次建设将大大加快。

话不多说,直接配置到项目中:

const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
module.exports = {
    //...
    plugins: [
        new HardSourceWebpackPlugin()
    ]
}
复制代码

二次构建时,我们会发现构建时间来到了个位数,只有短短的7秒钟。

在二次构建中,我发现了一个现象,构建的进度会从10% 一下跳到 80%,甚至是一瞬间就完成了中间构建过程。这正验证了该插件为模块提供中间缓存的说法。

为模块提供中间缓存,我的理解是cache-loader缓存的是对应loader的处理结果 ,而这个插件甚至可以缓存整个项目全部的处理结果,直接引用最终输出的缓存文件,从而大大提高构建速度。

其他优化方法

babel-loader开启缓存

babel-loader自带缓存功能,开启cacheDirectory配置项即可,官网的说法是,开启缓存会提高大约两倍的转换时间。

module.exports = {
  module: {
  rules: [
      {
        test: /\.js$/,
        use: [
          ...
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true // 开启缓存
            }
          }
        ]
      }
    ]
  }
}

复制代码

uglifyjs-webpack-plugin开启多进程压缩

uglifyjs-webpack-plugin或是其他的代码压缩工具都提供了多进程压缩代码的功能,开启可加速代码压缩。

总结

至此,我们完成了项目构建时间从70s到7s的优化过程,文中主要使用:

工具

作用

优化效果

thread-loader

多进程解析文件

70s -> 60s

cache-loader

缓存部分高开销的loader

60s -> 30s

hard-source-webpack-plugin

缓存模块中间过程

30s -> 7s

最后

如果你觉得此文对你有一丁点帮助,点个赞。或者可以加入我的开发交流群:1025263163相互学习,我们会有专业的技术答疑解惑

如果你觉得这篇文章对你有点用的话,麻烦请给我们的开源项目点点star: https://gitee.com/ZhongBangKeJi/CRMEB不胜感激 !

相关推荐

悠悠万事,吃饭为大(悠悠万事吃饭为大,什么意思)

新媒体编辑:杜岷赵蕾初审:程秀娟审核:汤小俊审签:周星...

高铁扒门事件升级版!婚宴上‘冲喜’老人团:我们抢的是社会资源

凌晨两点改方案时,突然收到婚庆团队发来的视频——胶东某酒店宴会厅,三个穿大红棉袄的中年妇女跟敢死队似的往前冲,眼瞅着就要扑到新娘的高额钻石项链上。要不是门口小伙及时阻拦,这婚礼造型团队熬了三个月的方案...

微服务架构实战:商家管理后台与sso设计,SSO客户端设计

SSO客户端设计下面通过模块merchant-security对SSO客户端安全认证部分的实现进行封装,以便各个接入SSO的客户端应用进行引用。安全认证的项目管理配置SSO客户端安全认证的项目管理使...

还在为 Spring Boot 配置类加载机制困惑?一文为你彻底解惑

在当今微服务架构盛行、项目复杂度不断攀升的开发环境下,SpringBoot作为Java后端开发的主流框架,无疑是我们手中的得力武器。然而,当我们在享受其自动配置带来的便捷时,是否曾被配置类加载...

Seata源码—6.Seata AT模式的数据源代理二

大纲1.Seata的Resource资源接口源码2.Seata数据源连接池代理的实现源码3.Client向Server发起注册RM的源码4.Client向Server注册RM时的交互源码5.数据源连接...

30分钟了解K8S(30分钟了解微积分)

微服务演进方向o面向分布式设计(Distribution):容器、微服务、API驱动的开发;o面向配置设计(Configuration):一个镜像,多个环境配置;o面向韧性设计(Resista...

SpringBoot条件化配置(@Conditional)全面解析与实战指南

一、条件化配置基础概念1.1什么是条件化配置条件化配置是Spring框架提供的一种基于特定条件来决定是否注册Bean或加载配置的机制。在SpringBoot中,这一机制通过@Conditional...

一招解决所有依赖冲突(克服依赖)

背景介绍最近遇到了这样一个问题,我们有一个jar包common-tool,作为基础工具包,被各个项目在引用。突然某一天发现日志很多报错。一看是NoSuchMethodError,意思是Dis...

你读过Mybatis的源码?说说它用到了几种设计模式

学习设计模式时,很多人都有类似的困扰——明明概念背得滚瓜烂熟,一到写代码就完全想不起来怎么用。就像学了一堆游泳技巧,却从没下过水实践,很难真正掌握。其实理解一个知识点,就像看立体模型,单角度观察总...

golang对接阿里云私有Bucket上传图片、授权访问图片

1、为什么要设置私有bucket公共读写:互联网上任何用户都可以对该Bucket内的文件进行访问,并且向该Bucket写入数据。这有可能造成您数据的外泄以及费用激增,若被人恶意写入违法信息还可...

spring中的资源的加载(spring加载原理)

最近在网上看到有人问@ContextConfiguration("classpath:/bean.xml")中除了classpath这种还有其他的写法么,看他的意思是想从本地文件...

Android资源使用(android资源文件)

Android资源管理机制在Android的开发中,需要使用到各式各样的资源,这些资源往往是一些静态资源,比如位图,颜色,布局定义,用户界面使用到的字符串,动画等。这些资源统统放在项目的res/独立子...

如何深度理解mybatis?(如何深度理解康乐服务质量管理的5个维度)

深度自定义mybatis回顾mybatis的操作的核心步骤编写核心类SqlSessionFacotryBuild进行解析配置文件深度分析解析SqlSessionFacotryBuild干的核心工作编写...

@Autowired与@Resource原理知识点详解

springIOCAOP的不多做赘述了,说下IOC:SpringIOC解决的是对象管理和对象依赖的问题,IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系...

java的redis连接工具篇(java redis client)

在Java里,有不少用于连接Redis的工具,下面为你介绍一些主流的工具及其特点:JedisJedis是Redis官方推荐的Java连接工具,它提供了全面的Redis命令支持,且...