Vue3.x 从零开始(六)—— Router + Vuex + TypeScript 实战演练(下)

在上一篇实战演练中,已经将项目搭建好,并介绍了 Router、Vuex 的基本用法

接下来会以一个 Todo List 项目来介绍实战中如何使用 Composition API

一、输入框与列表(按键别名 + computed 类型声明 )

首先是输入框,由于需要支持回车键提交,所以需要监听 keydown 事件

如果是传统的按键处理,需要在事件对象中根据 keyCode 来判断按键

Vue 提供了一些常用的按键修饰符,不用在事件处理函数中再做判断

比如这里就使用了 enter 修饰符,直接监听 enter 键的 keydown 事件


列表部分,需要判断当前列表是否为空,如果为空则展示空状态

这里使用了 v-if 和 v-else 来做条件判断,而其判断条件 showList 是一个计算属性 computed

在 TypeScript 的项目中,如果像 JS 项目一样添加计算属性,无法进行类型推断

需要加上类型声明:

// 在 setup() 中通过 computed() 注册的计算属性不需要声明类型

二、添加、删除条目(在 setup 中使用 vuex)

创建的条目需要保存到 store 中,首先需要定义条目类型

然后在 state 中新增 todoList 字段,用于保存列表

这里还添加了一个 todoListMap 字段,是 todoList 的字典项,后面查找条目的时候会用到

同时在 mutations 中新增添加和删除方法

Store 已经调整好了,接下来只要在组件中调用即可

可以像之前介绍的那样,使用 mapStatemapMutations 来导出对应的字段和方法

不过如果想在 setup 中使用 vuex,就需要用到 vuex 4 提供的 useStore 方法

import { useStore } from 'vuex';

export default {
  //...
  setup() {
    const store = useStore();
    console.log('store--->', store);
    return { ...store.state }
  }
}

从截图可以看到,useStore() 的返回值其实就是 $store

接下来的事情就简单了,手动导出需要用到的 state 和 mutations、actions 即可

这种方式导出 state 还行,但对于 mutation 和 action,需要一个一个手动创建函数并导出,就比较笨重

没关系,我们还有 mapMutations 和 mapActions 可以使用

但需要注意,不要在 setup 使用 mapState!

因为 mapState 导出 state 是一个函数(computed),这个函数内部使用了 this.$store

而 setup 中 this 是一个空值,所以在 setup 中使用 mapState 会报错

如果确实希望以 mapState 的形式在 setup 中导出 state,可以看一下 vuex-composition-helpers

import { useState, useActions } from 'vuex-composition-helpers';

export default {
    props: {
        articleId: String
    },
    setup(props) {
        const { fetch } = useActions(['fetch']);
        const { article, comments } = useState(['article', 'comments']);
        fetch(props.articleId); // dispatch the "fetch" action

        return {
            // both are computed compositions for to the store
            article,
            comments
        }
    }
}

三、查看条目详情(在 setup 中使用 router)

在条目详情页,可以在 url 上携带条目 id,然后通过 id 在 store 中找到对应的数据

这就需要调整路由配置文件 src/router/index.ts,配置 vue-router 中的动态路由

路由配置好了,接下来需要在列表上添加“查看详情”按钮的处理函数

如果这个函数写在 methods 里面,可以直接通过  this.$router.push() 来跳转页面

但是在 setup 中,就需要用到 vue-router 提供的 useRouter

import { useRouter } from 'vue-router';

export default {
  // ...
  setup() {
    const router = useRouter();
    const viewItem = (id: string) => {
      router.push(`/about/${id}`);
    };
    return { viewItem };
  }
}

然后在详情页,通过 useRoute(注意不是 userRouter )获取 params

四、完全使用 Composition API 开发组件

以上都是在 setup 中使用 Composition API,整个组件本身依然是使用 Options API 开发

想象一下,如果整个组件的 <script> 部分就是一个 setup 函数,会发生什么呢?

给 <script> 标签加上 setup 修饰符试试!

<script lang="ts" setup>
// ...
</script>

然后把整个 <script> 当做 setup 函数,改写上面的详情页

这样就能完全使用 Composition API 来开发组件了

和 setup 的区别在于,setup 最终需要 return 一个对象,而现在需要使用 export 来导出变量

如果需要使用 setup 函数的参数 ( props 和 context ),可以这么写:

<script lang="ts" setup="props, { emit }">

等效于

setup(props, { emit }) {
  // ...
}

最后再贴一份 script-setup 的组件示例:

<script lang="ts" setup="props, { emit }">
import {
  defineComponent,
  computed,
  onMounted,
  ref,
  toRefs,
} from 'vue';

const { modelValue, disabled } = toRefs(props);

/* data */
export const currentValue = ref('');
export const isComposing = ref(false);

/* methods */
export function handleInput() {
  if (isComposing.value) return;
  emit('update:modelValue', currentValue.value);
}

/* lifecycle */
onMounted(() => {
  currentValue.value = modelValue.value;
});

/* computed */
export const inputDisabled = computed(() => disabled.value);

export default defineComponent({
  name: 'test-input',
  props: {
    modelValue: [String, Number],
    disabled: Boolean,
  },
  emits: {
    'update:modelValue': null,
  },
});
</script>

在这份代码中,我还是按照组件选项的方式,将变量分类放到一起

其实更合理的方式是按功能分类,这样更利于抽取逻辑

五、小结

Vuex 和 vue-router 都提供了可以在 setup 中获取实例的方法

这也侧面体现了 Vue 3 的 setup 是一个独立的钩子函数

它不会依赖于 Vue 组件实例,如果需要用到函数外部的变量,都需要从外部获取

同时也提醒我们在开发 Vue 3 的插件的时候,一定要提供相应的函数让开发者能在 setup 中使用

(0)

相关推荐