還在遲疑是否上ts?先上車再說!vue3+ts開發初體驗

老實說我對ts寫業務一直不太感冒,總感受影響效率。但事有兩面,好比之前寫angular,被谷歌強迫一下,寫一段時間以後感受也不賴,有時還有一種莫名的優越感,ts+rxjs+decorator各類高大上,咱ng就是nb。像不像小媳婦兒嫁給第一次見面的小少爺,過着過着以爲還挺滋潤,成天跑出去炫耀~javascript

小夥伴們管我要vue3+ts項目好久了,我就納悶了,寫個vue3,爲啥非要用ts哪?他們說ts寫多有牌面呀,面試提及來都以爲高人一截。這說明一個卷的現狀,如今前端JD裏面愈來愈多提到ts要求,這讓應聘者以爲ts是個加分項,因此就不得不學會。html

前言

文本主要結合案例體驗一下vue3+ts開發的實際效果。到底適不適合你和你的項目,還得根據各位看官本身掌握程度和項目實際狀況綜合判斷。 本文主要涉及如下知識點:前端

  • ts能爲咱們帶來什麼
  • 可能的額外負擔
  • 整合ts+vue3的兩種姿式
  • ts編寫vue組件的兩種姿式
    • 傳統選項方式
    • setup方式
  • ts編寫vuex的兩種姿式
    • 傳統$store方式
    • setup方式

查看本文配套視頻教程vue

ts能爲咱們帶來什麼

如下結論來自官方團隊視頻教程java

  • 增長項目擴展性和維護性,尤爲適合開源項目
  • vue3對ts的支持比之前更好了
  • ts能夠增量引入,不須要梭哈

image-20210531151342630

可能的額外負擔

固然也有負面影響:react

  • 額外學習成本
  • 影響開發效率
  • 排期壓力
  • 並不是萬能藥丸
  • anyscript

image.png

整合vue3+ts

下面咱們就整合ts到vue3中,主要有如下兩種環境:面試

image.png

vue cli環境

新建立項目:vuex

vue create my-project
複製代碼

image-20210510114727426

已存在項目:typescript

vue add typescript
複製代碼

Vite環境

新建立項目:npm

npm init @vitejs/app
複製代碼

image-20210531151929571

已存在項目,本身手擼~

使用TS編寫Vue組件

編寫一個組件常見任務:

  • 註冊組件
  • data類型定義
  • props類型定義
  • methodscomputed類型支持
  • composition api中的類型支持

組件定義

使用<script lang="ts">defineComponent定義一個組件。

<template>
  <div>{{ counter }}</div>
</template>

<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  data() {
    return {
      counter: 0
    }
  },
});
</script>
複製代碼

工具支持:Volar

data類型定義

data中的對象類型利用類型斷言肯定數據類型。

類型定義

首先定義一個類型,types.d.ts:

export type Todo = {
  id: number;
  name: string;
  completed: boolean;
}
複製代碼

組件定義

定義一個組件Comp.vue,並引入這個類型:

<script lang="ts"> import type { Todo } from "../types"; export default defineComponent({ data() { return { // 利用類型斷言 items: [] as Todo[] } }, created() { // 此處會得到類型支持  this.items.push({ id: 1, name: 'vue3', completed: false }) } }); </script>
複製代碼

模板定義

<template>
  <!-- 此處會得到類型支持 -->
  <div v-for="item in items" :key="item.id" class="todo-item">
    {{ item.name }}
  </div>
</template>
複製代碼

props類型定義

props中的對象類型利用類型斷言和PropType<T>

類型定義

export type TitleInfo = {
  value: string;
  color: string;
}
複製代碼

屬性定義

<script lang="ts"> // 屬性類型須要PropType支持 import { PropType } from "vue"; import type { TitleInfo } from "../types" export default defineComponent({ props: { // 利用泛型類型約束對象類型 titleInfo: Object as PropType<TitleInfo>, }, }) </script>
複製代碼

模板中使用

<h1 :style="{ backgroundColor: titleInfo?.color }">{{ titleInfo?.value }}</h1>
複製代碼
<Comp :title-info="{ value: '待辦事項', color: '#41b883' }"></Comp>
複製代碼

computed中的類型

computed要着重標識函數返回類型。

computed: {
  doubleCounter(): number {
    return this.counter * 2
  }
},
複製代碼

methods中類型

標識函數形參和返回類型便可。

methods: {
  newTodo(todoName: string): Todo {
    return {
      id: this.items.length + 1,
      name: todoName,
      completed: false,
    };
  },
  addTodo(todo: Todo) {
    this.items.push(todo);
    this.todoName = ''
  },
},
複製代碼

Setup Script

setup script方式編寫代碼會更加簡潔。 下面範例代碼目標是不改變template結構,重構script部分,以composition api方式實現,咱們來看看有什麼變化:

數據定義

單值利用泛型方法ref<T>()定義

<script setup lang="ts"> import { defineProps, ref, computed } from "vue"; import type { Todo } from "../types" const items = ref<Todo[]>([]); items.value.push({ id: 1, name: "vue3", completed: false, }); </script>
複製代碼

屬性定義

利用defineProps()定義屬性,經常使用手法有兩種:

  • 泛型方式:defineProps<{ titleInfo: TitleInfo }>()
  • 參數方式:defineProps({ titleInfo: Object as PropType<TitleInfo> })

計算屬性

使用computed()定義,一般類型能夠推斷出來。

const counter = ref(0);
const doubleCounter = computed(() => counter.value * 2);
複製代碼

方法

就是普通函數,定義形參類型和返回值類型便可。

const todoName = ref("");

function newTodo(todoName: string): Todo {
  return {
    id: items.value.length + 1,
    name: todoName,
    completed: false,
  };
}
function addTodo(todo: Todo) {
  items.value.push(todo);
  todoName.value = "";
}
複製代碼

使用TS編寫Vuex

vuex總體對ts的支持比較蹩腳,這是之前架構問題引發的,咱們一塊兒來感覺一下:

建立Store

建立store實例,store/index.ts

import { createStore, Store } from "vuex";
import { State } from "./vuex";

const store = createStore({
  state: {
    counter: 0,
  },
});

export default store;
複製代碼

引入vue,main.ts

createApp(App).use(store).mount("#app");
複製代碼

使用,Comp.vue

import { mapState } from "vuex";

export default defineComponent({
  computed: {
    // 映射state counter
    ...mapState(['counter']),
    doubleCounter(): number {
      // $store已經有類型了
      return this.$store.state.counter * 2;
    },
  },
}
複製代碼

$store類型化

咱們但願this.$store是有明確類型的, 這須要爲組件選項添加一個明確類型的$store屬性,能夠爲ComponentCustomProperties擴展$store屬性,store/vuex.d.ts

import { ComponentCustomProperties } from "vue";
import { Store } from "vuex";

// declare your own store states
export interface State {
  counter: number;
}

declare module "@vue/runtime-core" {
  // provide typings for `this.$store`
  interface ComponentCustomProperties {
    $store: Store<State>;
  }
}
複製代碼

useStore()類型化

setup中使用useStore時要類型化,共須要三步:

  1. 定義 InjectionKey
  2. app安裝時提供InjectionKey
  3. 傳遞 InjectionKeyuseStore

定義一個InjectionKey,約束StoreState類型,store/index.ts

import { InjectionKey } from "vue";
import { State } from "./vuex";

// define injection key
export const key: InjectionKey<Store<State>> = Symbol();
複製代碼

main.ts中做爲參數2傳入vuex插件

import { key } from "./store";
// 做爲參數2傳入key
createApp(App).use(store, key).mount("#app");
複製代碼

使用時,store就能夠有明確類型了,CompSetup.vue

import { useStore } from 'vuex'
import { key } from '../store'

const store = useStore()
const counter = computed(() => store.state.counter);
複製代碼

簡化使用

封裝useStore,避免每次導入key,store/index.ts

import { useStore as baseUseStore } from "vuex";
export function useStore() {
  return baseUseStore(key);
}
複製代碼

使用變化,CompSetup.vue

import { useStore } from '../store'
const store = useStore()
複製代碼

模塊化

建立模塊文件,store/modules/todo.ts

import { Module } from "vuex";
import { State } from "../vuex";
import type { Todo } from "../../types";

const initialState = {
  items: [] as Todo[],
};

export type TodoState = typeof initialState;

export default {
  namespaced: true,
  state: initialState,
  mutations: {
    initTodo(state, payload: Todo[]) {
      state.items = payload;
    },
    addTodo(state, payload: Todo) {
      state.items.push(payload)
    }
  },
  actions: {
    initTodo({ commit }) {
      setTimeout(() => {
        commit("initTodo", [
          {
            id: 1,
            name: "vue3",
            completed: false,
          },
        ]);
      }, 1000);
    }
  },
} as Module<TodoState, State>;
複製代碼

引入子模塊,store/index.ts

import todo from "./modules/todo";
const store = createStore({
  modules: {
    todo,
  },
});
複製代碼

狀態中添加模塊信息,vuex.d.ts

import type { TodoState } from "./modules/todo";

export interface State {
  todo?: TodoState;
}
複製代碼

組件中使用,Comp.vue

export default {
  data() {
    return {
      // items: [] as Todo[],
    };
  },
  computed: {
    items(): Todo[] {
      return this.$store.state.todo!.items
    }
  },
  methods: {
    addTodo(todo: Todo) {
      // this.items.push(todo);
      this.$store.commit("todo/addTodo", todo);
      this.todoName = "";
    },
  },
}
複製代碼

setup中使用,CompSetup.vue

const items = computed(() => store.state.todo!.items)
store.dispatch('todo/initTodo')

function addTodo(todo: Todo) {
  // items.value.push(todo);
  store.commit('todo/addTodo', todo)
  todoName.value = "";
}
複製代碼

總結

vue3+ts體驗中規中矩,跟react相比還有差距,尤爲vuex這塊支持比較弱,僅能作到state類型支持,getters依然any,子模塊mutationsactions更是徹底抓瞎,這個是之前架構問題,估計之後會有vuex5來解決。

ts顯然仍是作開源庫和框架更好一點,業務編寫不是特別必要。

源碼

微信搜索並關注「村長學前端」,回覆「ts+vue3」得到文中完整代碼

查看本文配套視頻教程

感謝你們觀看,我是村長,一個熱愛分享的程序猿。若是以爲本文還不錯,記得點贊+收藏哦,說不定哪天就用得上!

相關文章
相關標籤/搜索