直接从项目开始学习vue+typescript+装饰器(一)

1.vue-property-decorator 

这个组件完全依赖于vue-class-component,它具备以下几个属性

@Prop

// @Prop 装饰器是用以接收来自父组件的数据
// 子组件
<template>
<h1>收到:{{msg}}</h1>
</template>
<script lang="ts">
import { Component, Vue,Prop,Model } from 'vue-property-decorator';
@Component({})
export default class LangSelect extends Vue {
@Prop() private msg!: string;
}
</script>
// 父组件
<template>
<div class="login-container">
<lang-select msg='父组件的消息'></lang-select>
</div>
</template>
<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator';
import LangSelect from '@/components/LangSelect/index.vue';
@Component({
components: {
LangSelect
}
})
export default class extends Vue {}
</script>

@Model

// @Model 装饰器是用以组件上实现双向绑定
// 子组件
<template>
<input type="checkbox" v-on:change="$emit('handleChange',$event.target.checked)" v-bind:checked='checked'>
</template>
<script lang="ts">
import { Component, Vue,Prop,Model } from 'vue-property-decorator';
import { AppModule } from '@/store/modules/app';@Component({})
export default class LangSelect extends Vue {
@Model('handleChange', { type: Boolean }) private checked!: boolean;
}
</script>// 父组件
<template>
<div class="login-container">
<el-button type="primary" @click="onAlterFoo">父组件改变foo</el-button>
<lang-select v-model="foo" class="set-language" size="16px"></lang-select>
</div>
</template>
<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator';
import LangSelect from '@/components/LangSelect/index.vue';
@Component({  name: 'Login',  components: {    LangSelect  }})
export default class extends Vue {  
private foo:boolean = true;  
private onAlterFoo() {
this.foo = !this.foo;
}}
</script>

@Watch

// @Watch 装饰器是用以监控数据是否改变
<template>
<div class="login-container">
<el-button type="primary" @click="onAlterFoo">父组件改变foo</el-button>
val:{{val}}——oldVal:{{oldVal}}
</div>
</template>
<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator';
import LangSelect from '@/components/LangSelect/index.vue';
@Component({
name: 'Login',
components: {
LangSelect
}})
export default class extends Vue {
private foo:boolean = true;
private val:boolean = true;
private oldVal:boolean = true;  
private onAlterFoo() {
this.foo = !this.foo;  
}
@Watch('foo')  
private handleChanged(val:boolean,oldVal:boolean):void{
this.val = val;
this.oldVal = oldVal;
}}
</script>

@Provide 与 @Inject

//@Provide 装饰器是用以注入数据,@Inject 装饰器是用以获取注入的数据。
// 子组件 @Inject
<template>
<h1>收到:{{bar}}</h1>
</template>
<script lang="ts">
import { Component, Vue, Inject } from 'vue-property-decorator';
@Component({})
export default class LangSelect extends Vue {
@Inject() private readonly bar!:string
}
</script>
// 父组件 @Provide
<template>
<div class="login-container">
@provide/@Inject —— 接收来自父组件的数据:{{foo}}
<lang-select></lang-select>
</div>
</template>
<script lang="ts">
import { Component, Vue, Provide } from 'vue-property-decorator'
import LangSelect from '@/components/LangSelect/index.vue';
@Component({
components: {
LangSelect
}
})
export default class extends Vue {
@Provide('bar') private foo = '啥消息';
}
</script>

@Emit

// @Emit 装饰器是用以子组件触发父组件的自定义事件
// 子组件
<template>
<el-dropdown trigger="click" class="international" @command="handleParent">
<div class="el-dropdown-link">
<slot>
<i ref="icon" class="el-icon-setting"></i>
</slot>
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :disabled="language==='zh'" command="zh">中文</el-dropdown-item>
<el-dropdown-item :disabled="language==='en'" command="en">English</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script lang="ts">
import { Component, Vue,Prop, Watch,Emit } from 'vue-property-decorator';import { AppModule } from '@/store/modules/app';
@Component({})
export default class LangSelect extends Vue {  
@Prop({default:"14px"}) size!:string  
get language() {    return AppModule.language;  
}  
@Emit() 
private handleParent(){}
}
</script>
// 父组件
<template>
<div class="login-container">
<lang-select @handle-parent='handleMe' class="set-language" size="16px"></lang-select>
</div>
</template>
<script lang="ts">import { Component, Vue, Watch } from 'vue-property-decorator';
import LangSelect from '@/components/LangSelect/index.vue';
@Component({  name: 'Login',  components: {    LangSelect  }})
export default class extends Vue {  
private handleMe(){    console.log('子组件触发')  }
}
</script>

@Component (完全继承于 vue-class-component)

// @Component 装饰器是用以声明子组件
// 子组件
<template>
<p>子组件</p>
</template>
<script lang="ts">
import { Component, Vue,Prop, Watch } from 'vue-property-decorator';
@Component
export default class LangSelect extends Vue {}
</script>
// 父组件
<template>
<div class="login-container">
<lang-select class="set-language" @langChanged="changeLanguage" size="16px">{{currentLang}}</lang-select>
</div>
</template>
<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator';
import LangSelect from '@/components/LangSelect/index.vue';
@Component({  name: 'Login',  components: {    LangSelect  }})
export default class extends Vue {}
</script>

Mixins (由vue-class-component 提供的名为mixins的辅助函数) 

// 声明 resize.ts
import { Component, Vue, Watch } from 'vue-property-decorator';
@Component({  name: 'ResizeMixin'})
export default class extends Vue {
get device() {
return '231323'
}  
get sidebar() {
return '是的'
}
}
// 使用 vue界面
<template>
<div class="login-container"> {{classObj}}  </div>
</template><script lang="ts">
import { mixins } from 'vue-class-component';
import ResizeMixin from '../../layout/mixin/resize';
export default class extends mixins(ResizeMixin) {
get classObj() {
console.log(this.sidebar,this.device)
return this.sidebar;
}
}
</script>

@Ref

// @ref 获取 DOM 元素
<template>
<div class="login-container">
<input type="text" v-model="value" ref='aButton' />
</div></template><script lang="ts">
import { Vue, Component, Ref } from 'vue-property-decorator';
@Componentexport default class YourComponent extends Vue {
@Ref('aButton') readonly name!: string;  
private value = 'AAA';  
private mounted() {    
console.log(this.name); // <input type="text">  
}
}
</script>

@ProvideReactive

参考地址

@PropSync

@InjectReactive

2.vuex-module-decorators 

// Vuex 允许我们将 store 分割成模块(module)
// 每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块
// index.ts
import Vue from 'vue'import Vuex from 'vuex';
import { IAppState } from './modules/app';
import { IUserState } from './modules/user';
Vue.use(Vuex)
export interface IRootState {
app: IAppState
user: IUserState
}
export default new Vuex.Store<IRootState>({})
// ./modules/app
import { VuexModule, Module, Mutation, Action, getModule } from 'vuex-module-decorators';
import Cookies from 'js-cookie';
import { getSidebarStatus, setSidebarStatus } from '@/utils/cookies';
const initLang = window.appConf.lang;
import store from '@/store';
export enum DeviceType {  
Mobile,  
Desktop,  
language
}
export interface IAppState {  
device: DeviceType  
language:string  
sidebar: {    
opened: boolean    
withoutAnimation: boolean  
}
}
@Module({ dynamic: true, store, name: 'app' })
class App extends VuexModule implements IAppState {  
public sidebar = {    
opened: getSidebarStatus() !== 'closed',    
withoutAnimation: false  
}  
public device = DeviceType.Desktop  
public language = Cookies.get('language') || initLang;  
@Mutation  
private TOGGLE_SIDEBAR(withoutAnimation: boolean) {    
this.sidebar.opened = !this.sidebar.opened    
this.sidebar.withoutAnimation = withoutAnimation    
if (this.sidebar.opened) {      
setSidebarStatus('opened')    
} else {      
setSidebarStatus('closed')    
}  
}  
@Mutation  
private CLOSE_SIDEBAR(withoutAnimation: boolean) {    
this.sidebar.opened = false    
this.sidebar.withoutAnimation = withoutAnimation    
setSidebarStatus('closed')  
}  
@Mutation  
private TOGGLE_DEVICE(device: DeviceType) {    
this.device = device  
}    
@Mutation  
SET_LANGUAGE(language: string) {    
this.language = language;    
Cookies.set('language', language)  
}  
@Action  
public ToggleSideBar(withoutAnimation: boolean) {    
this.TOGGLE_SIDEBAR(withoutAnimation)  
}  
@Action  
public CloseSideBar(withoutAnimation: boolean) {    
this.CLOSE_SIDEBAR(withoutAnimation)  
}  
@Action  
public ToggleDevice(device: DeviceType) {    
this.TOGGLE_DEVICE(device)  
}  //i18n-setting  
@Action({ commit: 'SET_LANGUAGE' })  
async SetLanguage(language: string) {    
return language;  
}
}
export const AppModule = getModule(App)
// ./modules/user
import { VuexModule, Module, Action, Mutation, getModule } from 'vuex-module-decorators';
import { login, logout, getUserInfo } from '@/api/users';
import { getToken, setToken, removeToken } from '@/utils/cookies';
import store from '@/store';
export interface IUserState {  token: string  name: string  avatar: string  introduction: string  roles: string[]}@Module({ dynamic: true, store, name: 'user' })
class User extends VuexModule implements IUserState {  
public token = getToken() || ''
public name = ''
public avatar = ''
public introduction = ''
public roles: string[] = []  
@Mutation  private SET_TOKEN(token: string) {    
this.token = token  
}  
@Mutation  
private SET_NAME(name: string) {    
this.name = name  
}  
@Mutation  
private SET_AVATAR(avatar: string) {    
this.avatar = avatar  
}  
@Mutation  
private SET_INTRODUCTION(introduction: string) {    
this.introduction = introduction  
}  
@Mutation  
private SET_ROLES(roles: string[]) {    
this.roles = roles  
}  
@Action  
public async Login(userInfo: { username: string, password: string }) {    
let { username, password } = userInfo    
username = username.trim()    
const { data } = await 
login({ username, password })    
setToken(data.accessToken)    
this.SET_TOKEN(data.accessToken)  
}  
@Action  public ResetToken() {    
removeToken()    
this.SET_TOKEN('')    
this.SET_ROLES([])  
}  
@Action  
public async GetUserInfo() {    
if (this.token === '') {      
throw Error('GetUserInfo: token is undefined!')    
}    
const { data } = await getUserInfo({ /* Your params here */ })    
if (!data) {      
throw Error('Verification failed, please Login again.')    
}    
const { roles, name, avatar, introduction } = data.user    
// roles must be a non-empty array    
if (!roles || roles.length <= 0) {      
throw Error('GetUserInfo: roles must be a non-null array!')    
}    
this.SET_ROLES(roles)    
this.SET_NAME(name)    
this.SET_AVATAR(avatar)    
this.SET_INTRODUCTION(introduction)  
}  
@Action  
public async LogOut() {    
if (this.token === '') {      
throw Error('LogOut: token is undefined!')    
}    
await logout()    
removeToken()    
this.SET_TOKEN('')    
this.SET_ROLES([])  
}
}
export const UserModule = getModule(User)

参考地址:https://www.npmjs.com/package/vue-property-decorator

参考地址:https://www.npmjs.com/package/vuex-module-decorators

https://juejin.im/post/5ecdd2055188254314374cc2

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论