开源库vue-form-design首页资源体积从3.9M到1.5M, 如何做到的?

![企业微信截图_9a2f2c80-0069-48f4-8654-e1557f724fd5.png](p6-juejin.byteimg.com

背景

打开开源项目 vue-form-design 时, 白屏时间耗时很长, 一半多可能是 github 被限速的原因, 但是通过 F12 调试发现, 首页资源有 3.9M, 假设用户的下载速度 300KB,都要 13s 多(理论情况下)

所以为了提升下开源项目预览体验,获得更多 star, 得到 jym 的认可, 性能优化刻不容缓

来看下实际优化效果

优化前:

优化后:

首页资源从 3.9M 到 1.5M, 减少了 64%

说下开源项目 vue-form-designn 的相关技术栈

  • vue3 + vite4 + typescript
  • element-plus + jsoneditor + vue-codemirrir + wangEditor

分析

开始的时候, 我发现我写的没啥问题, 引用的包确实很大, 也确实必要, 不可能删除掉.

后面知道可以利用工具进行分析, 所以安装了rollup-plugin-visualizer, 能可视化地感知到产物的体积情况

  1. 安装
js 复制代码
npm install rollup-plugin-visualizer -D
  1. vite.config.js 中使用
js 复制代码
import { visualizer } from "rollup-plugin-visualizer";
export default defineConfig({
  plugins: [
    visualizer({
      open: true, //在默认用户代理中打开生成的文件
    }),
  ],
});

这样就能在 npm run build 的时候生成 stat.html 文件, 可视化感知产物体积

我们来看下优化前的产物结果

蓝色框就是首屏资源体积, 相当于首屏加载的时候会下载所有资源

优化

拆包

从上图看到, wangEditor + jsoneditor+ codemirror 这三个包占据了首屏资源的半壁江山, 如果把这些资源进行异步加载, 那体积就减少了差不多一半

首先看下在哪里引用的 wangEditor, 发现是 starfish-form 项目下的富文本表单组件

如果把该表单组件作为异步组件, 那等同于 wangEditor 异步加载

在这里我们需要了解一个知识点, vue3 如何实现异步组件的

js 复制代码
import { defineAsyncComponent } from "vue";

const AsyncComp1 = defineAsyncComponent(() => import("./components/MyComponent1.vue"));

实现如下

js 复制代码
// 富文本
const RichText = defineAsyncComponent(() => import("./components/RichText/index.vue"));
RichText.ControlType = "RichText"; // 必须与文件名匹配
RichText.nameCn = "富文本";
RichText.icon = "icon-textEdit";
RichText.formConfig = getFormConfig("RichText");

为什么后面要跟其他字段?

原来这些字段是绑定到组件内部的

如:

js 复制代码
defineComponent({
    ControlType: "RichText", // 必须与文件名匹配
    nameCn: "富文本",
    icon: "icon-textEdit",
    formConfig: getFormConfig("RichText"),
}

但是现在作为异步组件, 就访问不到组件内部的值, 只能在创建异步组件的地方进行绑定

这样参数能不要吗? 可以不要, 看个人的实现, 目的是渲染出组件列表

这样就把 wangEditor 抽离出去了

如何抽离 jsoneditor? 也是一样的步骤吗?

jsoneditor 是全局进行导入的, 然后组件内部直接使用

js 复制代码
import JSONEditor from "jsoneditor";

window.JSONEditor = JSONEditor;

这样就必须在组件内部单个导入, 这样又有一个问题? 单个导入的地方首屏确实会加载, 那怎么办?

html 复制代码
<el-tab-pane label="JSON配置" name="json">
  <div class="json">
    <div ref="jsonCenter"></div>
  </div>
</el-tab-pane>

和首页耦合到一起的

那我们把该模块抽离出去作为一个单独的组件, 同时该组件异步引入, 到这一步还不算完, 就算异步引入了, 但是首屏还是会渲染到该异步组件, 那我们就不让首屏渲染到, 就需要使用到 v-if 了, 惰性加载, 如果为 false, 就不渲染

改造后的代码如下

html 复制代码
<el-tab-pane label="JSON配置" name="json">
  <div class="json" v-if="activeName == 'json'">
    <jsonEnter ref="jsonCenter" />
  </div>
</el-tab-pane>
js 复制代码
defineComponent({
  components: {
    jsonEnter: defineAsyncComponent(() => import("./jsonEditor.vue")),
  }}

同理, vue-codemirrir 组件我们也可以使用上方相同的方法, 异步组件和惰性加载, 这样 vite 打包的时候会单独打包到一个文件中

这其中遇到了一个问题, 优化前是直接全局注册所以 starfish-form 项目没有安装 vue-codemirror,因为 vue-form-design 是 monorepo 项目, 如果要拆包所以两个项目 starfish-editor 和 starfish-form 都要安装 vue-codemirrir, 所以有个时间差,导致安装的版本不同

导致拆包后出现了两个 vue-codemirrir 文件

所以我们需要保证两个项目的相同包的版本一致性

组件按需加载

在项目中很多公共组件都是全局直接注册的, 会导致在首屏中加载不需要的公共组件

表单编辑器中

js 复制代码
app.component("CustomDialog", CustomDialog);
app.component("ConditionSelect", ConditionSelect);
app.component("HighConditionSelect", HighConditionSelect);
app.component("draggable", draggable);
app.component("Shape", Shape);
app.component("FormStyle", FormStyle);
app.component("StarfishEditor", Editor);

这些组件中有些是通过点击某个按钮后进行弹窗展示, 首屏是不会展示出来, 所以分析下哪些需要作为异步组件

动态表单渲染中, 所有表单也是直接注册的, 我们是不是可以都作为异步组件, 因为首屏肯定是不会渲染的

如:

js 复制代码
// 规则
const Rule = defineAsyncComponent(() => import("./components/Rule/index.vue"));
Rule.ControlType = "Rule"; // 必须与文件名匹配
Rule.rule = _.getJsonValidate();
utilFuns[Rule.ControlType] = Rule;

但是因为工作量大, 同时有些组件体积很小, 不是很有必要作为异步组件, 只抽离了其中比较大的组件

公共组件库按需导入

vue-form-design 使用的组件库是 element-plus, 是直接全局注册的

js 复制代码
app.use(ElementPlus, {
  locale: zhCn,
});

导致其中没有使用到的组件也一起导入了

我们使用如下方法就能按需且自动导入

js 复制代码
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";

{
plugins: [
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
}

实现了体积从1.3M 到 650Kb

还有如路由懒加载, 在开发初期就是这样做的, 如cdn加速, 没钱, 也懒得用网上现有的

总结

以上差不多都是把首屏不需要的资源进行拆包, 减少js体积, 不过效果也挺大的. 同时也认识到可视化分析代码体积的重要性和必要性, 希望对大家有些思考.

github 地址

预览

相关推荐
一个专注写代码的程序媛19 分钟前
vue组件间通信
前端·javascript·vue.js
一笑code29 分钟前
美团社招一面
前端·javascript·vue.js
懒懒是个程序员1 小时前
layui时间范围
前端·javascript·layui
NoneCoder1 小时前
HTML响应式网页设计与跨平台适配
前端·html
凯哥19701 小时前
在 Uni-app 做的后台中使用 Howler.js 实现强大的音频播放功能
前端
心宽体胖连壮实1 小时前
记录一次 MarchingSquaresJS 使用经历
vue.js
烛阴1 小时前
面试必考!一招教你区分JavaScript静态函数和普通函数,快收藏!
前端·javascript
GetcharZp1 小时前
xterm.js 终端神器到底有多强?用了才知道!
前端·后端·go
JiangJiang1 小时前
🚀 React 弹窗还能这样写?手撸一个高质量 Modal 玩起来!
前端·javascript·react.js
吃炸鸡的前端1 小时前
el-transfer穿梭框数据量过大的解决方案
前端·javascript