写点什么

XIAOJUSURVEY 的 Vue3 升级之路

作者:XIAOJUSURVEY
  • 2024-07-24
    浙江
  • 本文字数:3500 字

    阅读完需:约 11 分钟

XIAOJUSURVEY的Vue3升级之路

前言

XIAOJUSURVEY 是由公司内部沉淀、重新设计并开源的系统,因此也继承了内部使用的 Vue2 框架。但是随着 vue2 逐渐停止维护,vue3 的生态建设已经日趋完善与稳定,升级 Vue3 版本已势在必行。


文内项目 Github:XIAOJUSURVEY


作者:nilnoop

背景

经梳理,工程中需要升级的配套库包含 vue、vue-router、vuex、Element-UI、VueDraggable、wangeditor、Vue-Cli 等,涉及到的工作量巨大。为了控制变更范围、保持功能稳定,先做必须的升级,比如虽然 Vite 提供了内置的 TS 支持可开箱即用,本次升级仍保留原来的 JS 写法,后续再逐步改造成 TS。

接下来就逐个看一下升级过程。

vue

不知不觉间 vue 已经走过了第十个年头,在前端技术日新月异的今天,已经是一个不再年轻的框架。但是 vue3 仍然为社区带来了新的活力。重构的响应式系统、更好的代码组织方式(composition api)、更优秀的 diff 性能、解耦的视图层渲染,甚至更优雅的代码组织方式也让更多人了解并熟知了 mongo repo 的代码组织范式,并引起了社区的极大关注。

由于 Vue3 的一大亮点是升级了 Composition API,支持更好的逻辑复用,更有利于大型项目的维护。但是考虑到升级顺利,原组件仍保持 vue2 的 Option API 写法。但是部分 breaking change 还是需要手动升级的,包括但不限于:

1、ModelValue

v-model 的 props 名称变成了 ModelValue,这一点在子组件接收 props 的时候需要修改的 props 名字。当然也可以使用 defineModel 宏来简写。但是要注意版本需要为 3.4+

// vue2中v-model的语法相当于下面的结果<ChildComponent :value="pageTitle" @input="pageTitle = $event" />
// vue3中v-model的语法相当于下面的结果<ChildComponent :modelValue="pageTitle" @update:modelValue="pageTitle = $event"/>
复制代码

2、插槽 slot

// vue2写法-使用插槽<div slot="body"></div>// vue3写法-使用插槽<template #body></template>
复制代码

注意如果在 vue 组件中使用了渲染函数,那么插槽的渲染方式也发生了变化,改成了函数调用。

// vue2写法this.$scopedSlots.header
// vue3写法this.$slots.header()
复制代码

3、后缀

资源 import 的方式,必须要显示指定.vue 后缀

// vue2写法,可以省略文件后缀import HomePage from './HomePage'
// vue3写法,必须携带.vue后缀import HomePage from './HomePage.vue'
复制代码

这是因为 vue2 中,vue-loader 对资源后缀做了兼容。在 vite 中,所有 import 声明被设计为必须强制携带后缀,并后在 vue-cli 的后续计划中也会取消对省略后缀写法的支持。详见:https://github.com/vitejs/vite/issues/178#issuecomment-630138450

4、jsx 写法

vue 文件中使用 jsx 写法,必须在 script 标签上显式指定 lang="jsx"

// vue2写法,可以不声明lang="jsx"<script>/* jsx代码 */</script>
//vue3写法,必须声明lang="jsx"<script lang="jsx">/* jsx代码 */</script>
复制代码

想要了解更多可以参考官网的迁移指南:Vue 3 迁移指南

Vue Router

vue2 中的 rouer,需要先初始化,然后在初始化 Vue 实例的时候传入。

// router.jsimport VueRouter from 'vue-router';export const router = new Router({  mode: 'history',  base: '/management',  routes,})
// main.js
new Vue({ router, ...})
复制代码

vue3 中的 router 除了创建方式不同外,挂载的方法也改成了通过.use 方法进行链式挂载。

// router.jsimport { createRouter, createWebHistory } from 'vue-router'
export const router = createRouter({ history: createWebHistory('/management'), routes,})
// main.jsimport { createApp } from 'vue'import { router } from './router'
createApp(App).use(router).mount('#app')
复制代码

同时<router-link />标签上不再支持添加 class 与 tag,需要在 slot 元素上添加。

Pinia

pinia 作为官方推荐的全局状态管理库,和 vue3 的 composition 写法更搭,用起来也比 vuex 更灵活,不必一板一眼地写 action 与 commit。但是既然有了 composition api,我们完全可以通过简单的方式实现一个状态管理:

import { reactive } from 'vue'
const state = reactive({ count: 0})
export const useStore = () => { const setCount = (value) => { state.count = value } return [state.count, setCount]}
复制代码

那么为什么还需要使用 pinia 呢?原因有以下几点:

  1. 开发工具支持。开发过程中可以通过浏览器插件 vue.js devtools 查看并追踪 pinia 的状态、事件,也可以方便地进行时间回溯调试。如果没有开发工具的支持,就只能四处 console.log + debug 了, 尤其是在大型项目来说,简直折磨。

  2. 热更新。热更新时不会丢失当前的 state。前端 er,你也不想走完长长的业务流程因为热更新丢失状态从头点一遍吧?

  3. SSR 支持。不会造成跨请求状态污染。

不得不说 vue 的官方确实很有作为,官方推荐就是最佳实践。所以 XIAOJUSURVEY 也升级了 pinia 作为状态管理库。

Vite

vite 基于浏览器的原生 es module 带来的动态加载和 esbuild 的模块预编译带来的快速启动,对于开发体验带来的提升是巨大的。而且和 vue 是官配,对 vue 提供第一优先级支持,一经推出就席卷了社区,甚至成为众多非 vue 项目(angular、svelte、solidjs 等)的官方推荐。因此对本项目来说,vite 是开发脚手架的不二之选。

另外说一句 esbuild 是真滴快,而且非常简单易用。各种功能只通过参数指定就可以,没有复杂的打包配置,内置了 ts 支持,如果大家有一些库需要打包,可以尝试一下,对比下 rollup 各种复杂的插件配置以及各版本之间的兼容问题,esbuild 非常值得尝试。

诚然 vite 在当前阶段还有一些小问题,比如:

  1. 开发环境与生产环境产物不一致。开发期间使用 esbuild 编译模块、生产环境使用 rollup 打包,有可能出现两个环境产物不一致的情况,而且一旦遇到很难排查。

  2. 由于启动时不对资源进行打包,首屏会根据资源的依赖加载资源,可能造成请求过多、浏览器卡顿、甚至卡死、崩溃的的问题。但是 xiaojuSurvey 还远远没有达到这种体量。

MPA 支持

由于项目具有 B 端管理页和 C 端投放页两个 html 入口,因此需要使用 mpa 模式。vite 本身支持 mpa,只需要在 vite.config.ts 中将 appType 设置为 mpa 即可。但是使用体验并不美好,比如说文件路径不美观。

vite 中文件的路径就是浏览器访问的路径,可能很长。以 XIAOJUSURVEY 中的 B 端入口文件为例,它的路径是/mangement/index.html,那么浏览器访问的路径就是 localhost:8080/management/index.html。因此我们需要一个插件,将一个短的、便于记忆的路径映射到长长的 html 路径上。

在 vite 的插件市场找来找去,选中了功能最契合需求的 vite-plugin-virtual-mpa 插件,它具有以下特性:

  1. 路径映射,集成了 ·connect-history-api-fallback 来实现从路径到 html 文件的映射。这也是我们寻找插件的刚需。

  2. 从插件名字可以看出来,它可以生成虚拟的(virtual)html 入口文件。例如 xiaoju-survey 中有 B 端 C 端两个入口,它们可以共享一个 html 模板,在访问 C 端链接的时候读取在内存中生成“虚拟”html 文件,并可以通过 ejs 注入 rem 代码,提供了非常高的灵活性。

  3. 通过文件夹自动扫描页面,避免 html 太多时的繁琐配置,等等。

Vue Draggable

由于 vue3 一些升级的 breaking change,拖拽库的 api 也有一些变化。

vue2 使用库 vuedraggable 是的写法如下:

<draggable v-model="myArray">   <div v-for="element in myArray" :key="element.id">{{element.name}}</div></draggable>
复制代码

vue3 对应使用 vuedraggable 的 next 版本,数据对象与索引需要通过作用域插槽来获取,写法如下:

<draggable v-model="myArray" itemKey="id">  <template #item="{element, index}">    <div>{{element.name}}</div>   </template></draggable>
复制代码

ElementPlus

这部分比较简单,可以直接使用使用官方的迁移工具,见文档:https://element-plus.org/zh-CN/guide/migration.html

剩下可能有其他的小问题等小问题还需要人工回归验证,再逐个修复, 比如自定义主题变量不生效。相对于其他库,Element-UI 的整体迁移过程还是很顺滑的。


升级感受

在升级框架之前,vue2 和 vue3 的项目都开发过了一些。也遇到了一些预料之外的问题,拜繁荣的社区所赐,都顺利解决了。升级后的同事们的第一个感受是开发启动快,在后续的局部重构与优化中,项目的结构与代码逻辑也基于 composition api 变得更清晰。

同时,我们惊喜地发现,社区参与建设的朋友也多了起来。看来历史的车轮滚滚向前,想要项目保持活力,还是要保持技术栈的迭代不能停。


全部变更可以参见 pr:https://github.com/didi/xiaoju-survey/pull/98


关于我们

感谢看到最后,我们是一个多元、包容的社区,我们已有非常多的小伙伴在共建,欢迎你的加入。

Github:XIAOJUSURVEY


社区交流群

Star

开源不易,请star一下 ❤️❤️❤️,你的支持是我们最大的动力。


发布于: 刚刚阅读数: 5
用户头像

XIAOJUSURVEY

关注

@滴滴 github.com/didi/xiaoju-survey 2024-05-20 加入

「快速」打造「专属」问卷系统, 让调研「更轻松」

评论

发布
暂无评论
XIAOJUSURVEY的Vue3升级之路_前端_XIAOJUSURVEY_InfoQ写作社区