缘起
我们团队负责的超算云服务控制台前端工程(console-ui)从提交第一行代码(2021-03-18)至今已 2 年多时间,两年多的时间内随着需求的增加,前端工程也越来越庞大,陈旧的基础设施暴露出了如下问题:
1、Node.js 版本仍然使用 2021 年发布的 14.x,而最新的 Node 大版本已更新到20.x,仍然使用旧版本导致一些构建工具版本不能随意更新,也同时留下一定的安全隐患。
2、项目目前仍使用两年前发行的 Webpack4.46.0 版本作为项目构建工具,随着项目逐渐庞大,Webpack 在开发时编译项目时间通常达到 2 分钟以上,严重影响开发效率。
3、前端生态日新月异,守着老旧的生态,不便于团队成长。
借本次升级 console-ui 核心 vue版本的契机,考虑同时升级到新的构建工具。Vite 从 2021 年发布以来,在构建速度及开发体验上一直口碑不错,经过两年多的迭代,也趋于稳定,完全可以应用于产线了。且根据官方的介绍 Vite 4.3 开始的版本比之前的版本在开发及构建速度上又有了成倍的提升:
发行日志见链接:https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md
开始折腾
1、升级 Node.js
目前 console-ui 构建还是用的Node.js 14.x, 而现在 Node.js 的 LTS 版本是18.x,按照 Node.js 的版本发行规律,预计目前最新的 20.x 版本在今年的 10 月份将成为下一个 LTS 版本,且 Node.js 的向下兼容性较强,所以目前咱们可以拥抱 Node.js 20.x 相关生态。所以本次升级就直接选用 Node.js 20.x 了。
开发机是使用nvm
管理 Node.js 多版本的。使用如下命令安装并使用Node.js 20:
nvm install 20.3.0
nvm use 20.3.0
2、升级构建工具
在升级过程如下:
创建初始工程
使用官方开始页面提供的命令初始化一个初始工程:
pnpm create vite console-ui --template vue
3、console-ui 已有功能最简验证
3.1、支持多主题
console-ui
的设定是可以打包不同主题的页面,构建时根据 env 变量打包不同主题的版本,然后采用不同主题文件。经验证vite也支持,vite.config.js 核心配置代码如下:
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/themes/${env.VITE_APP_DIST_MODE}.scss";`
}
}
}
由于不同的主题有些不同的资源文件(如favicon.ico、主题相关的图片等)需要引用,在这里我们在public 目录下新建2个目录用于管理不同主题资源文件的管理,然后配置不同的publicDir:
publicDir: `./public/${distMode}`,
3.2、验证多页面支持
console-ui
提供针对普通用户的 UI 界面,同时也提供管理员使用的维护界面,这两套界面在原工程是使用多页面机制实现的。经验证,vite 也能很好的支持,核心配置代码如下:
plugins: [
createHtmlPlugin({
minify: true,
pages: [
{
entry: '/src/main.js',
filename: 'index.html',
template: 'index.html',
injectOptions: {
data: {
title: '超算云服务控制台'
}
}
},
{
entry: 'src/pages/admin/index.js',
filename: 'admin.html',
template: 'admin.html',
injectOptions: {
data: {
title: '超算运服务控制台-系统维护'
}
}
}
]
})
]
4、开始整合
整合时会涉及一些资源文件引用的改造,一般改动会比较多,为不影响主分支开发,建议单独开个分支处理。整合过程比较繁琐,就不一一罗列了,主要涉及如下事项:
- 替换 ENV 变量:vue-cli 工程的环境变量都是以Vue_APP 开头的变量,可以全局替换为VITE_APP开头,而且 Webpack 工程一般使用
process.env.XXX
访问ENV变量,而 Vite 是使用import.meta.env.XXX
访问 ENV 变量,为了少改动,可以使用如下方式继续使用process.env.XXX
方式访问:
define: {
'process.env': { ...env }
}
- 调整资源引用问题:Webpack 项目多数使用require('xxx')的方式在 JS 里引入资源,确实特别的方便,但是这一套在 Vite支持不是很好,即便有插件支持(如vite-plugin-commonjs)但依然没有 Webpack 的 require 机制强大。一些不需要参与打包的资源可以放在
public
目录,然后使用URL路径访问,参与打包的文件,可改为import XXX from 'xxxx.png'
的方式引入
整体改造过程提交记录截图如下:
优化
整合后发现打包的文件比原 Webpack 打包的 dist 目录更大,前端工程层面的优化,主要以从以下几点着手处理:
- 压缩:可采用
vite-plugin-compression
插件对打包后的文件进行了压缩处理 - 静态资源上CDN:优化过程主要使用
rollup-plugin-visualizer
对打包后的文件进行分析,对一些占用打包空间较大的文件使用CDN引入的方式进行优化 - 按需引入:如
lodash
、moment.js
等,尽量按需引入,减少非必须的语言包等,能很大程度的减小打包体积。
附上本次改造核心的完整 vite.config.js
文件内容:
import path from 'path'
import vitePluginCommonjs from 'vite-plugin-commonjs'
import { createHtmlPlugin } from 'vite-plugin-html'
import { visualizer } from 'rollup-plugin-visualizer'
import viteCompression from 'vite-plugin-compression'
import externalGlobals from 'rollup-plugin-external-globals'
// import eslintPlugin from 'vite-plugin-eslint'
import { defineConfig, loadEnv } from 'vite'
import legacy from '@vitejs/plugin-legacy'
import vue2 from '@vitejs/plugin-vue2'
export default ({ mode }) => {
const env = loadEnv(mode, process.cwd())
const distMode = env.VITE_APP_DIST_MODE || 'paratera'
const cdn = {
scripts: mode.includes('development') ? [] : [
'https://statics.paratera.com/console/libs/vue.2.7.14.min.js',
// 'https://cdn.jsdelivr.net/npm/vue-router@3.6.5/dist/vue-router.min.js',
// 'https://cdn.jsdelivr.net/npm/vuex@3.6.2/dist/vuex.min.js',
'https://statics.paratera.com/console/libs/element-ui.2.15.13.min.js',
'https://statics.paratera.com/console/libs/exceljs.4.3.0.min.js'
]
}
// https://vitejs.dev/config/
const viteConfig = {
base: '/',
publicDir: `./public/${distMode}`,
define: {
'process.env': { ...env }
},
plugins: [
vue2(),
// eslintPlugin(),
createHtmlPlugin({
minify: true,
pages: [
{
entry: '/src/main.js',
filename: 'index.html',
template: 'index.html',
injectOptions: {
data: {
title: '超算云服务控制台',
cdn
},
tags: [
{
injectTo: 'body-prepend',
tag: 'div',
attrs: {
id: 'tag1'
}
}
]
}
},
{
entry: 'src/pages/admin/index.js',
filename: 'admin.html',
template: 'admin.html',
injectOptions: {
data: {
title: '超算运服务控制台-系统维护',
cdn
// injectScript: `<script src="./inject.js"></script>`,
}
}
}
]
}),
vitePluginCommonjs(),
legacy({
targets: ['ie >= 11'],
additionalLegacyPolyfills: ['regenerator-runtime/runtime']
}),
viteCompression({
verbose: true, // 是否在控制台输出压缩结果
disable: false, // 是否禁用,相当于开关在这里
threshold: 10240, // 体积大于 threshold 才会被压缩,单位 b,1b=8B, 1B=1024KB 那我们这里相当于 9kb多吧,就会压缩
algorithm: 'gzip', // 压缩算法,可选 [ 'gzip' , 'brotliCompress' ,'deflate' , 'deflateRaw']
ext: '.gz' // 文件后缀
})
],
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: [
{
find: /@\/.+/,
replacement: (val) => {
return val.replace(/^@/, path.resolve(__dirname, './src/'))
}
},
{
// this is required for the SCSS modules
find: /^~(.*)$/,
replacement: '$1'
}
]
},
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/themes/${distMode}.scss";`
}
}
},
server: {
port: 8686
},
build: {
// target: 'es2015',
// cssTarget: 'chrome80',
// brotliSize: false,
// chunkSizeWarningLimit: 2000,
// commonjsOptions: {
// // 改为 ture 后就会转化 require 语法
// transformMixedEsModules: true
// },
rollupOptions: {
input: {
index: path.resolve(process.cwd(), 'index.html'),
admin: path.resolve(process.cwd(), 'admin.html')
},
external: [
// 'vue',
'element-ui',
'exceljs'
],
plugins: [
externalGlobals({
vue: 'Vue',
// 'vue-router': 'VueRouter',
// vuex: 'Vuex',
'element-ui': 'ELEMENT',
'exceljs': 'ExcelJS'
})
],
output: {
chunkFileNames: 'assets/js/chunks/[name].[hash].js',
assetFileNames: (chunkInfo) => {
// 用后缀名称进行区别处理
// 处理其他资源文件名 e.g. css png 等
let subDir = 'images'
const extName = path.extname(chunkInfo.name)
if (['.css'].includes(extName)) {
subDir = 'css'
}
if (['.woff', '.ttf'].includes(extName)) {
subDir = 'fonts'
}
return `assets/${subDir}/[name].[hash].[ext]`
},
// 入口文件名
entryFileNames: 'assets/js/[name].[hash].js'
}
}
}
}
const lifecycle = process.env.npm_lifecycle_event
if (lifecycle.includes(':report')) {
viteConfig.plugins.push(visualizer({ open: true, brotliSize: true, gzipSize: true, filename: './dist/report.html' }))
}
return defineConfig(viteConfig)
}
升级前后 package.json 比对:
成效
对比项 | 优化前(Webpack) | 优化后(Vite) |
---|---|---|
安装依赖 | 66.60s | 17.2 |
开发启动 | 110228ms | 2536ms |
打包部署 | 123.77s | 1m 57s |
对比截图
安装依赖:
- Webpack ↓
- Vite ↓
开发启动:
- Webpack ↓
- Vite ↓
打包截图:
- Webpack ↓
- Vite ↓
评论 (0)