“茴”字写法 —— Vue typescript 组件

孔乙己显出极高兴的样子,将两个指头的长指甲敲着柜台,点头说,“对呀对呀!……回字有四样写法,你知道么?”我愈不耐烦了,努着嘴走远。孔乙己刚用指甲蘸了酒,想在柜上写字,见
我绝不热心,便又叹一口气,显出极可惜的样子。

《“茴”字写法》系列文章主要总结常见的代码操做,给出多种实现方式,就像茴香豆的“茴”字有多种写法同样。
本文连接:https://www.taskhub.work/arti... javascript

规则千万条,简洁第一条vue


正文开始 java

Vue 组件有不少中写法,在3.0以后会更好的支持typescript,ts用过都知道,真香,不管是代码提示仍是代码重构都很是方便,本人以前写过一个UI库大概4~5万行规模,全用ts,期间发现不少不合理的地方,对UI库进行重构,只花了2天时间。下面经过对比不一样写法。ios

各个Vue 组件UI库实现方式:

UI 库 实现方式
muse-ui 彻底不写 <template> 只使用 render 函数
iview 使用 .vue 文件,样式单独写
element 使用 .vue 文件,样式单独写
vant 使用 .vue 文件,样式单独写
ant-design-vue 使用 .jsx 文件,样式单独写
vux 使用带 <style> 的 .vue 文件,但在使用时必须用 vux-loader
cube-ui 使用带 <style> 的 .vue 文件,但有一些配置

在实际开发中用不用 *.vue 这样的单文件组件来开发呢?
网上有不少网友吐槽Vue的单文件组件模式写法,认为Vue的模板语法很鸡肋,各类不方便,不如所有jsx。决定这个问题的关键是解耦,包括功能解耦、模块解耦、甚至框架解耦。*.vue文件组织方式虽然多少和Vue相关,可是实际操做时发现要解耦重构也不是很大的问题,因此还OK。git

三种组件写法对比

Object API 29 lines

import Vue, { PropOptions } from 'vue'

interface User {
  firstName: string
  lastName: number
}

export default Vue.extend({
  name: 'YourComponent',

  props: {
    user: {
      type: Object,
      required: true
    } as PropOptions<User>
  },

  data () {
    return {
      message: 'This is a message'
    }
  },

  computed: {
    fullName (): string {
      return `${this.user.firstName} ${this.user.lastName}`
    }
  }
})

Class API 17 lines

import { Vue, Component, Prop } from 'vue-property-decorator'

interface User {
  firstName: string
  lastName: number
}

@Component
export default class YourComponent extends Vue {
  @Prop({ type: Object, required: true }) readonly user!: User

  message: string = 'This is a message'

  get fullName (): string {
    return `${this.user.firstName} ${this.user.lastName}`
  }
}

Function API 25 lines

import Vue from 'vue'
import { computed, value } from 'vue-function-api'

interface User {
  firstName: string
  lastName: number
}

interface YourProps {
  user?: User
}

export default Vue.extend({
  name: 'YourComponent',

  setup ({ user }: YourProps) {
    const fullName = computed(() => `${user.firstName} ${user.lastName}`)
    const message = value('This is a message')

    return {
      fullName,
      message
    }
  }
})
写法 优势 缺点
Object API Vue 官方写法,方便Vue直接处理组件 1. 代码长、缩进多,组件复杂时难以理清逻辑,很差进行分割
2. 混入较多Vue的概念,新手学习成本高
Class API 相关概念能够用class的思路理解,能够更好地描述Vue的混入、data、computed,生命周期钩子等概念。Vue 3.0 将原生支持class写法 用到了修饰器语法特性,目前还在实验阶段(typescript可使用helper函数解决兼容问题,问题不大)
Function API 无状态,更好的单元测试、并行化 函数式写法很容易写出回调地狱,致使代码可读性、可维护性差,目前纯粹function api 写法较少见

完成一样一件事,ts的class写法简洁得多,在工程较大时减小1/3左右的代码,可维护性大大提升。github

typescript class 写法常见问题

  1. route 钩子无效问题

使用class写法会发现部分Vue的钩子函数没法使用问题,能够经过注册钩子函数解决,以下:vue-router

import Component from 'vue-class-component'

// Register the router hooks with their names
Component.registerHooks([
  'beforeRouteEnter',
  'beforeRouteLeave',
  'beforeRouteUpdate' // for vue-router 2.2+
])
  1. 与Vuex配合使用问题

使用vuex-class 解决,如state映射vuex

import { Vue, Component } from 'vue-property-decorator';
import { User } from '@/api/account';
import { State } from 'vuex-class';


@Component
export default class TestPage extends Vue {

  @State(state => state.user, { namespace: 'account' })
  user!: User;

}
  1. Vue 混入功能

代码常常须要各类错误,包括用户输入错误、安全检测、后台错误、网络故障等。若是所有错误处理代码放进组件中,代码臃肿,阅读性差,能够将常见的错误处理逻辑提取出来,经过混入的方式插入组件中。class写法推荐使用vue-property-decorator 的 Mixins,参考附录typescript

附录

附上模板代码,解决大多数Vue typescript 组件问题
Vue class 组件axios

import { Vue, Component, Prop, Watch, Model, Mixins } from 'vue-property-decorator';
import { State, Getter, Action, Mutation } from 'vuex-class';
import axios, { AxiosError } from 'axios';

interface Person {
  userId: string;
  nickname: string;
}

@Component
class CommonHandler extends Vue {
  onNetworkError(e: AxiosError) {
    console.log('on error');
  }
}

@Component
export default class Test extends Mixins(CommonHandler) {
  @Prop(Number) readonly propA: number | undefined

  @Prop({ default: 'default value' }) readonly propB!: string

  @Prop([String, Boolean]) readonly propC: string | boolean | undefined

  @Model('change', { type: Boolean }) readonly checked!: boolean
  
  message: string = 'hello world';

  get propBLen(): number {
    return this.propB.length;
  }

  @Watch('child')
  onChildChanged(val: string, oldVal: string) {}

  @Watch('person', { immediate: true, deep: true })
  onPersonChanged1(val: Person, oldVal: Person) {}

  @Watch('person')
  onPersonChanged2(val: Person, oldVal: Person) {}
 
  change() {
    console.log('on change');
  }

  created() {
    // 调用混入中的错误处理函数,简化代码
    axios.get('hello').catch(this.onNetworkError);
  }

  mounted() { console.log('mounted'); }
}

Vue 官方写法

import axios from 'axios';

const CommonHandler = {
  methods: {
    onNetworkError(e) {
      console.log('on error');
    }
  },
}

export default {
  mixins: [CommonHandler],
  
  props: {
    propA: {
      type: Number
    },
    propB: {
      default: 'default value'
    },
    propC: {
      type: [String, Boolean]
    }
  },

  model: {
    prop: 'checked',
    event: 'change'
  },

  data() {
    return {
      message: 'hello world',
    }
  },

  computed: {
    propBLen() {
      return this.propB.length;
    },
  },

  watch: {
    child: [
      {
        handler: 'onChildChanged',
        immediate: false,
        deep: false
      }
    ],
    person: [
      {
        handler: 'onPersonChanged1',
        immediate: true,
        deep: true
      },
      {
        handler: 'onPersonChanged2',
        immediate: false,
        deep: false
      }
    ]
  },

  methods: {
    change() {
      console.log('on change');
    },

    onChildChanged(val, oldVal) {},

    onPersonChanged1(val, oldVal) {},

    onPersonChanged2(val, oldVal) {}
  },

  // vue lifecycle hooks  
  created() {
    // 调用混入中的错误处理函数,简化代码
    axios.get('hello').catch(this.onNetworkError);
  },

  mounted() { console.log('mounted'); }
}

52行对比84行,一样功能减小38%的代码

最后打个广告~

TaskHub 是咱们团队开发的一个 Markdown 加密网盘,支持常见的任务管理功能还有Markdown 文件编辑,全部功能与平台解耦,只使用Markdown的特性实现,更好的保障用户的数据安全,实乃团队协做之利器。欢迎你们使用~

相关文章
相关标签/搜索