so why?

因为闲着也是贤者。考虑到 Vue 2 已经 EoL 很久了,而这个项目又想要做点新功能,所以想着把依赖项升一升。

之所以尝试 svelte,一个是因为其不像 vue 使用的是虚拟 dom,理论上有些性能优势;二个是其对 typescript 比较友好,整套工具链用起来体验还行。而且之前也尝试过使用 svelte kit 做了一个小网站项目,用着还算顺手。于是这次这次就想着要不直接上 svelte 得了。

还有一个原因是,之前尝试从 vue 2 升级到 vue 3,中间的过程踏入了不少的坑。想着既然升 vue 3 是坑,换 svelte 也是坑,那不如直接换 svelte 得了。于是就有了这次的升级。

升级步骤

0. 从 vue-cli 升级到 vite

vue 2 用的是 vue-cli 这一套打包工具,而到 vue 3 之后,就转向了 vite。恰巧,svelte 也推荐使用 vite,那我们的第一步就是把 vue-cli 升级到 vite。

Vue school 提供了比较详细的教程,供参考。简单来说就是:
1. 删掉老的依赖项,添加 vite 依赖项
2. 移除 bable,修改 .eslint.rc
3. 添加 vite 相关配置
4. 移动 index.html 到根目录,删掉对应的 <%= %>,新增 script 引用。
5. 替换环境变量,process.env.XXX -> import.meta.env.XXX

需要注意的是,我们这里是切换到 svelte,所以里面的那些安装什么 vue 的依赖可以全部换成对应的 svelte 的依赖。

如果你不知道怎么改,那也可以直接新建一个 svelte 的工程,然后把里面的文件复制到老工程目录下,对着最后修改下对应的 package.json 即可。

1. 安装 svelte

直接 npm install –save svelte 即可。如果你在上一步已经改好了所有的依赖,那当我没说。

其他需要的依赖包括:
– @sveltejs/vite-plugin-svelte: vite 打包插件
– @tsconfig/svelte: typescript 相关配置
– svelte-check: 语法检查

2. 开始迁移

需要事先说明的是,我之前使用的是 vue-class-component + vue-property-decorator 这两个扩展组件,所以vue的语法可能跟原生的语法相差较大。

修改入口点

main.ts:

-import Vue from "vue";
-import App from "./App.vue";
-new Vue({
-  render: h => h(App)
-}).$mount("#app");
+import { mount } from 'svelte'
+import App from './App.svelte'
+const app = mount(App, {
+  target: document.getElementById('app')!,
+})
+export default app

因为我们切换了框架,所以这一步基本上是重写了整个入口点文件。可以看出两边的语法还是有诸多不同的。

修改对应文件

将对应的 App.vue 重命名为 App.svelte,然后就可以开始真正的迁移之旅了。

模板语法变更

在 Svelte 中,HTML内容不需要被 template 标签所包裹,所以直接删除就可以了。

-<template>
    <div></div>
-</template>

对于常见的 if 条件判断,Svelte 这里的语法会比较奇怪:

-<div v-if="cond"></div>
+{#if cond}
+   <div></div>
+{/if}

同理,foreach 也是:

-<div v-for="(item, index) in nav">{{item}}</div>
+{#each nav in item, index}
+   <div>{item}</div>
+{/each}

在 svelte 中,文本变量是通过{}插入的,而不是 vue 中的{{}}。

如果要设置对应的 property,那么可以这么设置:

-<a :href="link"></a>
+<a href={link}></a>

对于特定 class 的设置:

-<div class="A" :class="{ 'hidden': hidden }"></div>
+<div class={["A", hidden ?? 'hidden']}></div>

双向绑定的语法略有不同,Svelte 并没有 v-model 这种通用的的东西:

-<input type="text" v-model="value" />
+<input type="text" bind:value={value} />
-<input type="checkbox" v-model="agree" />
+<input type="checkbox" bind:checked={agree} />

事件语法也不一样:

-<button @click="clickHandler"></button>
+<button onclick={clickHandler}></button>

脚本语法变更

在之前的 vue property decorator 写法里面,需要新建一个类并继承对应的基类。但是在 svelte 中不需要创建类了,所以写法会有一些区别:

-import { Component, Prop, Vue } from "vue-property-decorator";
-@Component({})
-export default class UserRegister extends Vue {
-   foo = false;
+let foo = false;

-   state = 0;
+let state = $state(0);

-   bar() {
-       this.state = 1;
-   }
+function bar() {
+   state = 1;
+}

同时,对于自定义组件,Svelte 只需要直接 import 就可以了,不需要额外的声明:

+import Foo from "@/component/foo.svelte";
-import Foo from "@/component/foo.vue";
-@Component({
-  components: {
-    Foo: Foo
-  }
-})

对于 Property 的传递,两边有着极大的不同:

-@Prop()
-prop1!: string;
+let { prop1 } = $props();

同时,事件触发传递也有区别:

+let { prop1, event1 } = $props();

-fireEvent() {
-   this.$emit("event1");
+function fireEvent() {
+   event1();
 }

对于内置的 mounted 和 destroyed 生命周期事件,svelte 也提供了对应的方法:

+import { onMount, onDestroy } from "svelte";

-mounted() {
+onMount(() => {
    // do something
-}
+});

-destroyed() {
+onDestroy(() => {
    // do something
-}
+});

对于计算属性,svelte 可以使用 derived 和 derived.by 替代:

-get double() {
-   return val * 2;
-}
+let double = $derived(val * 2);
+let triple = $derived.by(() => {
+   return val * 3;
+});

你问我 set 怎么办?我也不知道,用下面的 effect 代替吧。

Svelte 中的 effect 方法会监听里面的所有状态变量的变更,可以替代对应的 Watch:

-@Watch("foo")
-onFooChange() {
-   this.bar = this.foo;
-}

+$effect(() => {
+   bar = foo;
+});

对于组件的引用,可以使用 bind:this

-<canvas ref="canvas"></canvas>
+<canvas bind:this={canvas}></canvas>

+let canvas: HTMLCanvasElement;

-const ctx = (this.$refs.canvas as HTMLCanvasElement).getContext("2d");
+const ctx = canvas.getContext("2d");

组件迁移

之前肯定安装了一些 vue 的对应组件,这时候你就应该去找找对应的替代品了。比如说 vue-router,你可能需要对应的 svelte5-router

总结

这次其实主要还是一些语法的变更,变更的幅度并不算很大。和 vue 2 升 vue 3 相比,工作量没有差太多。但是,升级完之后,确实感觉好像文件变小+速度变快了,当然也有可能只是我的错觉。

这次的迁移参考了 kowalczyk 博客的文章,它的结论跟我的差不多,迁移都是挺容易的。

我这次其实并没有使用 svelte5-router,因为它不能使用基于 HashTag 的路由,没法满足我的要求。我这里就自己实现了一个非常简单的路由,基于 if 做的,非常暴力,但是它能用。

分类: 编程

0 条评论

发表回复

Avatar placeholder

您的邮箱地址不会被公开。 必填项已用 * 标注