可视化表单设计器首页资源体积从3.9M到1.5M, 如何做到的?

news/2024/7/10 1:14:14 标签: javascript, 开源, vue

企业微信截图_9a2f2c80-0069-48f4-8654-e1557f724fd5.png

背景

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

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

来看下实际优化效果

优化前:

企业微信截图_1371442e-b395-406a-9c1a-7ba3939ded29.png

优化后:

企业微信截图_aa2c1dbd-906b-4955-bb4b-b7e329d0e901.png

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

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

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

分析

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

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

  1. 安装
npm install rollup-plugin-visualizer -D
  1. vite.config.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 如何实现异步组件的

import { defineAsyncComponent } from "vue";

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

实现如下

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

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

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

如:

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

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

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

这样就把 wangEditor 抽离出去了

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

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

import JSONEditor from "jsoneditor";

window.JSONEditor = JSONEditor;

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

企业微信截图_3e571d29-504d-4510-bde3-93777e541f98.png

<el-tab-pane label="JSON配置" name="json">
  <div class="json">
    <div ref="jsonCenter"></div>
  </div>
</el-tab-pane>

和首页耦合到一起的

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

改造后的代码如下

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

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

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

wecom-temp-c24aff3a8df81f3ac032ea1086406b50.png

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

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

wecom-temp-5f9e621e538ef780219fe019c28d3dd1.png

组件按需加载

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

表单编辑器中

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);

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

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

如:

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

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

公共组件库按需导入

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

app.use(ElementPlus, {
  locale: zhCn,
});

企业微信截图_042826fe-302f-4a3a-bc7d-f7e106a53303.png

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

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

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 地址

预览


http://www.niftyadmin.cn/n/5444797.html

相关文章

SpringBoot-04 | spring-boot-starter-logging原理原理

SpringBoot-04 | spring-boot-starter-logging原理原理 第一步&#xff1a;springboot加载factories文件第二步&#xff1a;构造监听器第三步&#xff1a;注册监听器到Spring中第四步&#xff1a;开始加载日志框架第五步&#xff1a;加载日志框架logback-spring.xml第六步&…

【Qt】使用Qt实现Web服务器(五):QtWebApp上传文件、详解请求数据处理过程

1、示例 1)演示 2)上传图片 3)显示图片 2、源码 示例源码Demo1->FileUploadController void FileUploadController::service(HttpRequest& request, HttpResponse& response)

conda 查看激活自己的新环境,labelImg的使用

查看环境目录 我们可以在基础环境中查看我们有几个环境 conda env list 激活新环境 我们激活pytorch环境pytorch conda activate pytorch 在新环境下安装 然后我们安装labelImg&#xff08;Python3.10以上会报错&#xff09; pip install labelImg 新环境下打开 labelImg …

数据库系统概论-练手题集合【期末复习|考研复习】

前言 总结整理不易&#xff0c;希望大家点赞收藏。 给大家整理了一下数据库系统概论中的练手题&#xff0c;以供大家期末复习和考研复习的时候使用。 数据库系统概论系列文章传送门&#xff1a; 第一章 绪论 第二/三章 关系数据库和标准语言SQL 第四/五章 数据库安全性和完整性…

Java毕业设计 基于springboot医院挂号系统 医院管理系统

Java毕业设计 基于springboot医院挂号系统 医院管理系统 springboot医院挂号系统 医院管理系统 功能介绍 用户&#xff1a;登录 首页 个人资料 修改密码 门诊管理 用户挂号 医生&#xff1a;登录 首页 个人资料 修改密码 门诊管理: 用户挂号 处方划价 项目划价 项目缴费 项目…

vuex - 21年的笔记 - 后续更新

vuex是什么 Vuex是实现组件全局状态&#xff08;数据&#xff09;管理的一种机制&#xff0c;方便的实现组件之间的数据的共享 使用vuex统一管理状态的好处 能够在vuex中集中管理共享的数据&#xff0c;易于开发和后期维护能够高效地实现组件之间的数据共享&#xff0c;提高…

国内区块链公司哪个好

目录 1. 蚂蚁金服(Ant Financial) 2. 腾讯(Tencent) 3. 阿里巴巴(Alibaba) 4. 海尔智家(Haier Smart Home

java中使用@Pointcut和使用自定义注解对切面增强的区别

1、定义方式不同&#xff1a; Pointcut&#xff1a;是Spring AOP框架提供的注解&#xff0c;用于定义切点表达式。例如&#xff1a;Pointcut(“execution(* com.example.service..(…))”)。 自定义注解&#xff1a;需要自己创建一个新的注解&#xff0c;并在切面类上使用该注…