关于前端:如何提升项目的本地构建效率

9次阅读

共计 4205 个字符,预计需要花费 11 分钟才能阅读完成。

前言

最近写 H5 的我的项目比拟多,该我的项目从年龄上看着还算比拟年老😂,整个架构应该是间接应用 vue-cli 基于 vue2 生成的,那底层打包工具天然也就是 webpack,咱们晓得 webpack 有个通病,那就是随着我的项目的一直增大每次构建的工夫也会随之越来越长。比方咱们这个我的项目的单次冷启动就达到了惊人的 1 分 20 秒左右,每次跑完电脑风扇转的飞起,几乎忍不了!(可能是电脑太老了)

上面一起看看如何将我的项目的冷启动时长从 1 分 20 秒左右优化到十几秒左右吧~

是什么让构建效率这么慢?

页面数量

因为咱们这个我的项目是个 SPA 我的项目,路由是通过 vue-auto-routing 来主动生成的。为了更直观的看到外面有多少个页面,于是我把 routes 打印进去了。

竟然有 258 个之多!页面这么多,webpack 打包构建的速度天然就会慢。

很好奇的一点这么多页面都是线上在跑的?

工夫都用在哪?

为了对我的项目做一些有针对性的优化,咱们须要理解整个编译过程中耗时散布,晓得了各模块的耗时数据咱们能力隔靴搔痒。

这里能够应用 speed-measure-webpack-plugin 插件来进行剖析。

speed-measure-webpack-plugin不仅能够剖析总的打包工夫,还能剖析各阶段 loader 的耗时。

// 应用
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const plugins = [
  // ...
  new SpeedMeasurePlugin(),]

从上图来看,编译过程中的大部分工夫都是用在 vue 文件的编译解决 loader 上。说白了还是文件太多了导致编译耗时比拟长。

如何优化?

通用计划

开启缓存

webpack 中几种缓存形式:

  • cache-loader
  • hard-source-webpack-plugin
  • babel-loader 的 cacheDirectory 标记

咱们这个我的项目应用的 vue-cli 版本是4.1.0,它曾经内置了 cache-loader 和 **babel-loader 的 cacheDirectory 标记** 两种缓存

咱们能够二次启动看看

二次启动破费了大略 43 秒,晋升还是蛮大的,次要起因是 “ 冷启动 ” 时曾经将 babel-loader、vue-loader 进行了缓存:

另外一种缓存可自行测试 hard-source-webpack-plugin,次要缓存这种计划只在二次启动能力有显著的性能晋升,与我首次冷启动就要 ** 快 ** 的预期不符。这种计划这里就不再试了

开启多线程

因为 js 单线程的特点,当有多个工作同时存在,它们也只能排队串行执行。

所以有没有能够应用相似 web Worker 的技术实现多线程编译解决,将局部工作合成到多个子过程中去并行处理,子过程解决实现后把后果发送到主过程中,从而缩小总的构建工夫。

可选计划:

  • thread-loader(官网推出)
  • parallel-webpack
  • HappyPack

从下面能够发现,编译过程,大部分工夫都是在解决 vue 文件,所以能够针对 vue-loader 应用thread-loader

{
  test: /\.vue$/,
    include: path.resolve('src'),
      use: [
        {
          loader: 'thread-loader',
          options: {workers: 2,},
        },
      ],
},

留神:仅在耗时的操作中应用 thread-loader,否则应用 thread-loader 会后可能会导致我的项目构建工夫变得更长,因为每个 worker 都是一个独立的 node 过程,创立 worker 的过程也是耗时的,尽量不要得失相当。

此时的编译工夫为 41 秒左右,晋升如同并不是特地显著,可能在大型项目中才会施展出更大的作用。

当然还有很多计划能够一一尝试,但我感觉达到的成果应该都不会超过上面这个针对性计划。

针对性计划

该计划其实就是放大咱们的构建指标,整个我的项目尽管有很多页面,从下面路由来看多达 258 个,但咱们平时在开发过程中其实只关注咱们以后须要批改的页面,所以有没有可能在开发过程中,我只构建我须要用的页面,对于那些不须要的页面不参加构建,这样的话必定可能大幅晋升咱们的本地构建工夫。

这里还须要思考的是,怎么对原有构建代码的侵入性做到最小?

思路

  • 新增构建脚本,原有 npm run dev 放弃不变
  • 解决须要启动的页面,生成对应的路由routes.dev.js
  • 把原有 routes 提取成文件routes.pro.js
  • 再通过 NormalModuleReplacementPlugin 插件在编译过程进行文件替换
  • 最初再进行构建

构建脚本

新增 start 命令

// package.json
"start": "node ./build/cli.js start",

次要构建代码如下

// cli.js
const shell = require('shelljs')
const path = require('path')
const fs = require('fs')
const action = process.argv[2]
const arg = process.argv.slice(3)
let appName = arg[0]  // 指的是你要启动的我的项目(文件夹名)// const startPath = arg.join('/')
console.log('🚀🚀------start------🚀🚀')
;(() => {if (!appName) {
    // 未输出项目名称则开启交互命令行
    openInquirer()
    return
  }
  // 启动
  if (action === 'start') {start()
  }
})()

function start() {// console.log('启动我的项目')
  process.env.action = 'signle'
  runTask(appName)
}
// 启动我的项目
async function runTask(appName) {const cmds = []
  console.log(`🚢【启动我的项目】${appName}`)
  generateRoute(appName) // 生成须要启动的路由

  const runProPath = path.resolve(__dirname, `../src/pages/${appName}`)

  // if (process.platform === 'win32') {//   cmds.push(`set runProPath=${runProPath}`)
  // } else {//   cmds.push(`export runProPath=${runProPath}`)
  // }
  // 检测我的项目是否存在
  const res = await getProject(runProPath)
  if (res.errno < 0) {
    // 抛出异样
    throw new Error('没有找到可启动的我的项目😭')
  } else {cmds.push(`npx vue-cli-service serve --open --colors --mode dev`)
  }

  const cmd = cmds.join('&&')
  // return
  const {code} = shell.exec(cmd)
  return code
}

解决须要启动的页面

因为这个我的项目是用 vue-auto-routing 来主动生成路由的,所以这里我仍然还是用它外部的一个库来主动生成

const {generateRoutes} = require('vue-route-generator')

// 解决须要启动的路由
function generateRoute() {console.log('--', path.resolve(__dirname, `../src/pages/${appName}/`))
  const code = generateRoutes({pages: path.resolve(__dirname, `../src/pages/${appName}/`),
    importPrefix: `@/pages/${appName}/`,
  })

  fs.writeFileSync(path.resolve(__dirname, `../src/routes.dev.js`), code)
}

替换须要启动的路由

依据用户输出的须要启动的文件夹名,咱们为这个文件夹内的所有文件主动成了路由文件routes.dev.js,当初须要做的是通过 webpack 进行替换。

new webpack.NormalModuleReplacementPlugin(
    /src\/routes.pro.js/,
    './routes.dev.js',
  ),

应用

次要的工作实现,当初能够来启动试一试

比方:启动某 ** 我的项目

# 启动命令 npm start + 项目名称(文件夹名)npm start campusArea

当初的启动工夫大略在 15 秒左右,这与你以后文件夹下的文件数量无关,文件越少启动越快!二次启动工夫大略在 10 秒左右,小我的项目首次启动时长大略都在 10 秒内

首次冷启动时长大略节俭了 1min,写代码的工夫又变多了😂

优化

可能大家都习惯了 npm run devnpm start,会遗记启动页面的参数?

不要急,这一点也思考进去了,有个十分弱小的库 inquirer 能够为咱们开启交互式命令行。

// 未输出项目名称则开启交互命令行
function openInquirer() {
  // 获取所有可启动目录
  const projectList = fs.readdirSync(path.resolve(__dirname, '../src/pages'))
  // console.log('projectList', projectList)

  const promptList = [
    {
      type: 'list',
      message: '🚗请抉择启动的目录:',
      name: 'pro',
      choices: [...projectList],
    },
  ]
  inquirer.prompt(promptList).then((answers) => {console.log(answers)
    appName = answers.pro
    start()})
}

当你间接 npm start 的时候,能够让你抉择你想要启动的目录:

完结。

原文首发地址点这里,欢送大家关注公众号 「前端南玖」,如果你想进前端交换群一起学习,请点这里

我是南玖,咱们下期见!!!

正文完
 0