概述:最近在學習 vue & typeScript,發現有有一些寫法和方法須要去梳理和總結。主要是參考 參考文章 vue-property-decorator、vue-class-component,因此選擇一些關鍵點用於沉澱和思考。html
publish:2019-09-15vue
可與 React & Redux in TypeScript - 靜態類型指南 對比着看react
vue-cli 中包含着 typescript 選項,只須要選擇便可git
vue create repo
# 手動配置的時候須要選擇 TypeScript
Check the features needed for your project:
◉ Babel
◉ TypeScript
◯ Progressive Web App (PWA) Support
◯ Router
◉ Vuex
❯◉ CSS Pre-processors
◉ Linter / Formatter
◯ Unit Testing
◯ E2E Testing
複製代碼
vuex-module-decorators、vuex-class 的引入見github
vuex-module-decorators 官方文檔vuex
vue-class ReadMe。vue-cli
用 Typescript 來開發 Vue,主要是採用 vue-property-decorator,來賦予組件 Class 各類能力,具體有typescript
@Component
主要是用來實現 class-style 形式的 Vue 組件npm
import { Component, Vue } from 'vue-property-decorator';
@Component
export default class HelloWorld extends Vue {
public render() {
return (<h5>Hello world</h5>);
}
}
複製代碼
與以前的 .vue
不一樣的是,利用 render
來渲染組件。此時的 class Hello world
中實現的組件的狀態與邏輯bash
data
組件狀態,在 vue 中很關鍵的 data
就是做爲 class
的屬性來實現的
import { Component, Vue } from 'vue-property-decorator';
@Component
export default class HelloWorld extends Vue {
private message: string = 'world';
public render() {
const { message }: HelloWorld = this;
return (<h5>Hello {message}</h5>);
}
}
複製代碼
使用 privat message
做爲組件的狀態;
computed
組件的計算屬性,在 class-style 形式的 vue 組件中,常採用 get()
操做符來實現
@Component
export default class HelloWorld extends Vue {
private message: string = 'world';
get subMessage(): string {
return `boy ${this.message}`;
}
public render() {
const { message, subMessage }: HelloWorld = this;
return (
<div>
<h5>Hello {message}</h5>
<span>{subMessage}</span>
</div>
);
}
}
複製代碼
methods
與 data
相似,也是做爲 class 的屬性來實現的,經常使用於實現頁面中的操做事件,例如點擊、選擇等
@Component
export default class HelloWorld extends Vue {
private message: string = 'world';
get subMessage(): string {
return `boy ${this.message}`;
}
public setMessage(msg: string): void {
this.message = msg;
}
public render() {
const { message, subMessage, setMessage }: HelloWorld = this;
return (
<div> <h5>Hello {message}</h5> <span>{subMessage}</span> <button onClick={() => setMessage('person')}> Click </button> </div>
);
}
}
複製代碼
@Prop
做爲單向數據流實現的關鍵一環,也是組件之間傳遞數據的關鍵
import { Component, Prop, Vue } from 'vue-property-decorator';
@Component
export default class HelloWorld extends Vue {
@Prop({ default: 'Click' }) public readonly porpA!: string;
public render() {
const { porpA }: HelloWorld = this;
return (
<div>
<h5>{porpA}</h5>
</div>
);
}
}
複製代碼
能夠在 @Prop
的參數中配置相關 options
,而且 vue-property-decorator 還提供了 @PropSync
,它將 props
與 computed
複合使用,具體用法
// 父組件 parent
<Child
name="hello world"
@update:name="handleUpdate"
/>
// 子組件 child
@Component
export default class Child extends Vue {
@PropSync('name', { type: String })
syncedName!: string
public setSyncedName() {
this.syncedName = 'delete';
}
}
複製代碼
等同於以前單文件組件模式下的
export default {
props: {
name: {
type: String
}
},
computed: {
syncedName: {
get() {
return this.name
},
set(value) {
this.$emit('update:name', value)
}
}
}
}
複製代碼
@Emit
等同於 this.$emit
,經常使用於觸發自定義事件
// tsx
@Emit()
addToCount(n: number) {
this.count += n
}
// vue
methods: {
addToCount(n) {
this.count += n
this.$emit('add-to-count', n)
}
}
複製代碼
@Watch
指的是偵聽屬性,也就是當偵聽的 data
或者 props
變化的時候,會觸發對應變化
import { Component, Watch, Vue } from 'vue-property-decorator';
@Component
export default class HelloWorld extends Vue {
private message: string = 'world';
@Watch('message', { immediate: true, deep: true })
public onMsgChange(val: string, oldVal: string) {
console.log('onMsgChange', val, oldVal);
}
public render() {
const { message }: HelloWorld = this;
return (
<div> <h5>Hello {message}</h5> <input type='text' v-model={this.message}/> </div> ); } } 複製代碼
@Watch
的第一個參數表明監聽的 path
,第二個參數則是表明
{ immediate: true, deep: true } //是否當即觸發、深度偵聽
複製代碼
@Provide
與 @inject
這兩個經常使用在組件開發上,簡單的來講就是在父組件中經過 provider 來提供變量,而後在子組件中經過 inject 來注入變量,在 class-style 中
import { Component, Inject, Provide, Vue } from 'vue-property-decorator'
const symbol = Symbol('baz')
@Component
export class MyComponent extends Vue {
@Inject() readonly foo!: string
@Provide() foo = 'foo'
}
複製代碼
注意這種共享的
data
是非響應式的
@ProvideReactive
以及 @InjectReactive
與上面的用法差很少,可是共享的值,若是在父組件中發生了變化,那麼在子組件中會捕捉到。
@ref
經常使用於直接訪問組件
import { Vue, Component, Ref } from 'vue-property-decorator'
import AnotherComponent from '@/path/to/another-component.vue'
@Component
export default class YourComponent extends Vue {
@Ref() readonly anotherComponent!: AnotherComponent
@Ref('aButton') readonly button!: HTMLButtonElement
}
# 等同於
export default {
computed() {
anotherComponent: {
cache: false,
get() {
return this.$refs.anotherComponent as AnotherComponent
}
},
button: {
cache: false,
get() {
return this.$refs.aButton as HTMLButtonElement
}
}
}
}
複製代碼
@Model
經常使用於實現 v-model
這樣的指令,在以前的 vue 組件中
export default {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: {
type: Boolean
}
}
}
複製代碼
在 class-style 中,只須要用 @Model
來實現
import { Vue, Component, Model } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
@Model('change', { type: Boolean }) readonly checked!: boolean
}
複製代碼
以前在使用 Vuex 的時候,主要是依賴 state
、getters
、mutations
以及 actions
,而且能夠將它們模塊化
state
狀態
import { Module, VuexModule } from 'vuex-module-decorators'
@Module
export default class Vehicle extends VuexModule {
wheels = 2
}
// 來替代以前的
export default {
state: {
wheels: 2
}
}
複製代碼
Getter
與 class-style 中的 computed
相似,也是藉助 getters
來實現的
import { Module, VuexModule } from 'vuex-module-decorators'
@Module
export default class Vehicle extends VuexModule {
wheels = 2
get axles() {
return this.wheels / 2
}
}
// 來代替以前的
export default {
state: {
wheels: 2
},
getters: {
axles: (state) => state.wheels / 2
}
}
複製代碼
Mutation
用來修改 state
,與 Vuex in js 同樣,只能用來實現同步操做
import { Module, VuexModule, Mutation } from 'vuex-module-decorators'
@Module
export default class Vehicle extends VuexModule {
wheels = 2
@Mutation
puncture(n: number) {
this.wheels = this.wheels - n
}
}
// 來替代以前的
export default {
state: {
wheels: 2
},
mutations: {
puncture: (state, payload) => {
state.wheels = state.wheels - payload
}
}
}
複製代碼
actions
用來實現異步操做
const request = require('request')
export default {
state: {
wheels: 2
},
mutations: {
addWheel: (state, payload) => {
state.wheels = state.wheels + payload
}
},
actions: {
fetchNewWheels: async (context, payload) => {
const wheels = await request.get(payload)
context.commit('addWheel', wheels)
}
}
}
// 來替代以前的
const request = require('request')
export default {
state: {
wheels: 2
},
mutations: {
addWheel: (state, payload) => {
state.wheels = state.wheels + payload
}
},
actions: {
fetchNewWheels: async (context, payload) => {
const wheels = await request.get(payload)
context.commit('addWheel', wheels)
}
}
}
複製代碼
以上,咱們簡單實現了一個 Vuex 的 module,那麼如何使用呢?將上述代碼梳理一下,並配置到 Vuex 中,具體
import { Module, VuexModule } from 'vuex-module-decorators'
@Module({ name: 'Vehicle', namespaced: true, stateFactory: true })
export default class Vehicle extends VuexModule {
public wheels = 2;
get axles() {
return this.wheels / 2;
}
@Mutation
public puncture(n: number): void {
this.wheels = this.wheels - n;
}
}
複製代碼
請注意 Module
的配置,咱們須要將須要 namespaced: true
,而且爲該空間命名,而且引入到項目中
import Vue from 'vue'
import Vuex from 'vuex'
import Vehicle from './Vehicle'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
Vehicle,
},
})
複製代碼
以後就是使用 vuex-class
,來獲取該命名空間下的狀態以及方法
import { Component, Vue } from 'vue-property-decorator';
import {
Getter,
Mutation,
namespace,
} from 'vuex-class';
const Vehicle = namespace('Vehicle');
@Component
export default class HelloWorld extends Vue {
// 引入 Vechicle 下的 Getters
@Vehicle.Getter('axles') public axles: number | undefined;
// 引入 Vechicle 下的 puncture
@Vehicle.Mutation('puncture')
public mutationPuncture!: (n: number) => void;
private message: string = 'world';
public render() {
const { message, axles, mutationPuncture }: HelloWorld = this;
return (
<div onClick={ () => mutationPuncture(1) }>
<h5 ref='quickEntry'>Hello {message} { axles }</h5>
</div>
);
}
}
複製代碼
利用 namespace
能夠很方便的獲取到 Vuex 的 Vehicle 模塊,再配合 Getter
、Mutation
就能夠完成引入,其餘的引入方法與之相似,就不贅述了。
整體來講整個體驗的過程很像以前寫 Mobx & React in Typescript 的感受,很相像。在實際項目中使用的話,會有 ts 帶來的一些便利,可是也感受總有些牽強,其餘後續 3.0,看看可否與 ts 產生奇妙的化學反應吧。