vue3 + setup sugar + TS 经验分享__前端__Vue.js
发布于 3 年前 作者 banyungong 1602 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

theme: smartblue

前言

目前setup sugar已经进行了定稿,而vue3 + setup sugar + TS的写法看起来很香,所以我大胆尝试了下,期间发现一些小问题,分享下我的经验,如有问题,欢迎斧正。

前期准备

  1. 使用vue-cli创建一个 vue3 + TS 的项目

    相关可去 https://cli.vuejs.org/zh/guide/ 查看

  2. vscode 禁用 Vetur,下载Volar

    相关下载以及说明可去 https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar 查看

注意:vue3 不支持 ie 全系(包括 ie11),因为 ie 并不支持 proxy api。若你想使用 vue3 相关语法,请等待 vue2.7。预期将在 2021 q3 或者 q4 发布 vue2.7,详情见:https://github.com/vuejs/rfcs/discussions/296

什么是 setup sugar

在单文件组件(SFC)中引入一个新的 <script> 类型 setup。它向模板公开了所有的顶层绑定。

未使用setup sugar

<template>
  <Foo :count="count" @click="inc" />
</template>
<script>
import Foo from './Foo.vue'
import { ref } from 'vue'

export default {
  setup() {
    const count = ref(1)
    const inc = () => { count.value++ }

    return {
      Foo,
      count,
      inc
    }
  }
}
</script>

使用了setup sugar

<template>
  <Foo :count="count" @click="inc" />
</template>
<script setup>
// 导入的组件也可直接可用于模板
import Foo from './Foo.vue'
import { ref } from 'vue'

// 书写组合式 api 就像在正常的 setup 中一般,但是不需要进行手动地进行 return
const count = ref(0)
const inc = () => { count.value++ }
</script>

你可以在 https://github.com/vuejs/rfcs/pull/222 以及https://github.com/vuejs/rfcs/pull/227 看到有关 setup sugarref sugar有关更多的讨论。个人不喜欢ref sugar,个人认为.value的写法比较好理解,结合Volar的帮助提示,已经没有多少的心智负担。

vue3 组合式写法是否会产生更多问题

目前网络上很多人反应使用组合式 api 反而显得代码更加乱了。那么从vue2options apivue3composition api写法,究竟会获得什么好处呢?

  1. 逻辑耦合度更高:在options api中如何一个功能我们需要用到 data+method+watch…等更多 api,一段代码无法合并在一起,我们在阅读一段逻辑需要进行反复上下移动进行观看。而composition api就解决了这个问题。
  2. 功能抽离:得益于函数式编程,一个功能逻辑我们可以封装到一个 hook 中,我们直接导入hook,运行方法,即可。

缺点:从options api切换到composition api最大的问题无异于最大的问题就是没有强制的代码分区,如果书写的人没有很好的代码习惯,那么后续的人将会看的十分难受。目前我是这么解决的:

  • 自我代码分区并且尽量抽离方法(写好注释),分区如下:
    1. 相关引入
    2. 响应式数据、props、emit 定义
    3. 生命周期以及 watch 书写
    4. 方法定义
    5. 方法、属性暴露
  • 组件抽离:将页面拆成两个文件夹,一个为 views,一个为 components。views 和 components 文件夹下有各自的文件。views 文件夹中为页面入口,掌管数据,而 components 则为页面中一些组件抽离。如果是公共组件,再抽离到 components 文件夹下其他位置。
  • hook 抽离:尽可能将逻辑抽离,并不一定要进行复用。

setup sugar 衍生出的新的 api

define 编译器宏(compiler macros )

define开头的 api 都为编译器宏(compiler macros )api,只能在 <script setup> 中使用。它们不需要被导入,并且在处理 <script setup> 时被编译掉。

注意:define类 api 必须直接在 setup 中外层进行使用,你无法将其放在方法中。

注意:define类 api 虽然不用导入,但是这一点和 TS 兼容不太好,如果不引入会提示 undefined,如果进行了引入,会有 warning 提示。

注意:define类 api虽然目前都可以使用 TS 类型声明,但是你无法导入一个 interface 或者 type 进行类型声明(直接在文件中声明是可以的),因为这样会报错。猜测这一点是和 define编译器宏有关,可能后期会被修复。

  1. defineProps:这个 api 很好理解,就是定义 props 相关信息。

    基础用法:

    defineProps({
     name: {
       type: String,
       required: false,
       default: 'Petter',
     },
     userInfo: Object,
     tags: Array,
    })
    

    使用 TS 类型声明:

    const props = defineProps<{ 
        foo: string 
        bar?: number
    }>()
    

    注:两个写法不可以一起进行使用,也就是一个 defineProps 不能既使用 TS 类型声明,也使用基础用法。

  2. withDefaults:这个方法并非属于编译器宏(compiler macros )api,但是这个 api 由defineProps衍生而出。在 TS 类型声明下无法进行设置默认值,这个 api 主要是为了解决这个场景。

    withDefaults(defineProps<{
      size?: number
      labels?: string[]
    }>(), {
      size: 3,
      labels: () => ['default label']
    })
    
  3. defineEmits:这个 api 也很好理解,就是定义 emits 相关信息。不过使用和以前有点不一样。

    基础使用:

    // 声明
    const emits = defineEmits(['change', 'delete'])
    // 使用
    emits('change')
    

    TS声明类型:

    // 声明
    const emit = defineEmits<{ (e: 'change', id: number): void (e: 'update', value: string): void }>()
    // 使用
    emits('change',1)
    
  4. defineExpose:在传统的 Vue 组件中,所有暴露在模板上的东西都隐含地暴露在组件实例上,也就是父组件可以通过ref 或者子链可以全量获取到子组件所有的属性、方法。大多数时候,这种全量暴露是过度的,而 vue3 必须进行手动暴露。

    const a = 1
    const b = ref(2)
    defineExpose({ a, b, })
    

    注意:目前发现defineExpose暴露出去的属性以及方法都是 uknown 类型,如果有修正类型的方法,欢迎评论区补充。

hook api

注:useContext API 被弃用,取而代之的是更加细分的 api。

  1. useAttrs:见名知意,这是用来获取 attrs 数据,但是这和 vue2 不同,里面包含了 class属性方法

    在 vue2 中封装组件透传属性、方法你可能这么写:

    <component v-bind='$attrs',v-on='$listeners'></component>
    

    vue3 里删除了$listeners,新写法:

    <template>
      <component v-bind='attrs'></component>
    </template>
    <srcipt setup lang='ts'>
        const attrs = useAttrs();
    <script>
    
  2. useCSSModule:CSS Modules 是一种 CSS 的模块化和组合系统。vue-loader 集成 CSS Modules,可以作为模拟 scoped CSS。此 api 本人用的比较少,不过多做介绍。

  3. useSlots: 顾名思义,获取插槽数据。

  4. useCssVars: 利用了 css 中的定义变量,此 api 暂时资料比较少。

  5. useTransitionState

  6. useSSRContext

setup sugar 目前存在的限制

修改选项配置需要单开一个 script

配置项的缺失,有时候我们需要更改组件选项,在setup中我们目前是无法做到的。我们需要在上方再引入一个 script,在上方写入对应的 export即可。

<script>
   export default {
       name: 'YourName',
       inheritAttrs: false,
       customOptions: {},
   } 
</script>
<script setup>
  // your code
</script>

注意:Vue 3 SFC 一般会自动从组件的文件名推断出组件的 name。在大多数情况下,不需要明确的 name 声明。唯一需要的情况是当你需要 <keep-alive> 包含或排除或直接检查组件的选项时,你需要这个名字。

与 TS 与 ESLint 不是完美融合

  1. @typescript-eslint/no-unused-vars规则不兼容,此规则含义为定义了,未进行使用。该规则其实影响不大,关闭即可。

  2. 与导入的类型声明不兼容,当你通过结构的方式去导入类型,setup sugar 会进行自动导出。这时候,你就会收到 TS 的一条报错:此为类型,但被当作值使用。解决办法:类型导出使用export default导出或者引入时使用import * as xx来进行引入。

    感谢评论区补充:import type { test } from "./test";如此书写,也是可以解决的。

    关于这一点,也许你会想着在上方写一个 script 进行导入,但是这是不行的。在上方 script 导入的东西,也会被自动导出。详情见:https://github.com/vuejs/vue-next/issues/3238

必须安装新的插件

集成开发环境需要为这个新的 <script setup> 模型提供专门的处理,以便提供模板表达式类型检查 / 道具验证等。安装Volar也是为了这个目的。

vue3 中如何实现国际化

  1. 下载最新的vue-i18n,当前版本"vue-i18n": "^9.1.7"
  2. src 下创建文件夹locales,创建好对应语言文案(这里最好使用 json)。
    import en from "./en.json";
    import zhHans from "./zh-cn.json";
    import zhHant from "./zh-hk.json";
    import { createI18n } from "vue-i18n";
    import { judgeLang } from "@/utils/url";
    
    const messages = {
        en: en,
        "zh-hans": zhHans,
        "zh-hant": zhHant,
    };
    
    const i18n = createI18n({
        locale: judgeLang(),
        messages,
    });
    
    export default i18n;
    
  3. main.ts中引入i18n
    import { createApp } from "vue";
    import App from "./App.vue";
    import router from "./router";
    import store from "./store";
    import i18n from "@/locales/index";
    
    createApp(App).use(store).use(router).use(i18n).mount("#app");
    
  4. 创建一个 i18n hook
    import { provide, inject } from "vue";
    import { useI18n } from "vue-i18n";
    
    export function useProvideI18n(): void {
        const { t } = useI18n();
        // 注入翻译函数
        provide("t", t);
    }
    
    export function useInjectI18n(): (text: string) => string {
       const t = inject("t") as (text: string) => string;
       return t;
    }
    
  5. app.vue,进行注入。随后,便可在各处进行接收使用。
  6. 下载 i18n Ally,可查看实时翻译(强烈推荐)。

vue-i18n 详情请见:https://kazupon.github.io/vue-i18n/zh/introduction.html i18n Ally 插件详情请见:https://github.com/lokalise/i18n-ally/blob/master/README.md

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: qiuliang 原文链接:https://juejin.im/post/6990682369992704007

回到顶部