Vue Class Component文檔翻譯

英文文檔地址: https://class-component.vuejs.org/

本文包括了文檔中的 總覽, 安裝, 指南, TypeScript指南

總覽

Vue Class Component 是一個(gè)可以讓你使用Class風(fēng)格語法編寫Vue組件的庫. 例如, 下面是一個(gè)通過Vue Class Component編寫的簡單的計(jì)數(shù)器組件的例子:

<template>
  <div>
    <button v-on:click="decrement">-</button>
    {{ count }}
    <button v-on:click="increment">+</button>
  </div>
</template>

<script>
import Vue from 'vue'
import Component from 'vue-class-component'

// 使用Class風(fēng)格定義組件
@Component
export default class Counter extends Vue {
  // Class的屬性將是組件的data
  count = 0

  // Methods will be component methods
  increment() {
    this.count++
  }

  decrement() {
    this.count--
  }
}
</script>

正如例子展現(xiàn)的, 你可以使用通過@Component裝飾器標(biāo)注Class, 來用直觀和標(biāo)準(zhǔn)的Class語法定義組件的data和方法. 你可以簡單地使用Class風(fēng)格的組件代替組件定義, 因?yàn)樗葍r(jià)于普通的使用對象定義的組件.

通過使用Class風(fēng)格定義的組件, 你不但要改變語法, 還要利用一些ECMAScript語法特性, 比如Class繼承和裝飾器. Vue Class Component 也提供了一個(gè)mixins 幫助 來繼承mixin, 以及一個(gè) createDecorator 方法來簡單地創(chuàng)建你自己的修飾器.

你或許也需要使用 Vue Property Decorator 提供的 @Prop@Watch 裝飾器.

安裝

Vue CLI 安裝

你可以通過使用 Vue CLI 簡單的安裝 Vue Class Component. 運(yùn)行下面命令來創(chuàng)建一個(gè)新項(xiàng)目:

$ vue create hello-world

你將被告知是否使用預(yù)設(shè)配置. 請選擇 "Manually select features":

image.png

選中 TypeScript 功能來使用 Vue Class Component. 你可以添加其他你需要的功能:

image.png

輸入 y 來回答 Use class-style component syntax?:

image.png

你可以按照自己的偏愛回答剩下的問題. 在結(jié)束安裝進(jìn)程后, Vue CLI 會(huì)創(chuàng)建一個(gè)新的安裝了Vue Class Component 的項(xiàng)目目錄.

手動(dòng)安裝

如果你要手動(dòng)安裝, 娜美你需要使用npm以及配置你自己的打包工具.

npm

使用 npm 安裝 Vue Class Component. 請確定你以及安裝了Vue核心庫, Vue Class Component依賴于它:

$ npm install --save vue vue-class-component

使用 yarn 安裝:

$ yarn add --save vue vue-class-component

Build Setup

使用Vue Class Component 你需要在你的項(xiàng)目中配置 TypeScript 或者 Babel, 因?yàn)樗蕾囉?ECMAScript stage 1 decorators 編譯, 從而能在瀏覽器中運(yùn)行.

::: 注意
它不支持 stage 2 decorators, 由于 TypeScript 編譯器只支持舊版本裝飾器.
:::

TypeScript

在項(xiàng)目根目錄創(chuàng)建 tsconfig.json, 然后配置 experimentalDecorators 選項(xiàng), 它可以編譯裝飾器語法:

{
  "compilerOptions": {
    "target": "es5",
    "module": "es2015",
    "moduleResolution": "node",
    "strict": true,
    "experimentalDecorators": true
  }
}

Babel

安裝 @babel/plugin-proposal-decorators@babel/plugin-proposal-class-properties:

$ npm install --save-dev @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties

在文件根目錄下配置 .babelrc 文件:

{
  "plugins": [
    ["@babel/proposal-decorators", { "legacy": true }],
    ["@babel/proposal-class-properties", { "loose": true }]
  ]
}

注意 legacyloose 選項(xiàng)需要 Vue Class Component 只支持 stage 1 (legacy) decorator spec.

CDN

unpkg.com 提供基于npm的 CDN 鏈接. 你可以選擇對應(yīng)版本的 Vue Class Component 來代替 @latest 版本 (例如 https://unpkg.com/vue-class-component@7.2.2/dist/vue-class-component.js 就是使用版本為 7.2.2的Vue Class Component).

<!-- UMD build -->
<script src="https://unpkg.com/vue-class-component@latest/dist/vue-class-component.js"></script>

<!-- UMD minified build -->
<script src="https://unpkg.com/vue-class-component@latest/dist/vue-class-component.min.js"></script>

<!-- ES Module build -->
<script src="https://unpkg.com/vue-class-component@latest/dist/vue-class-component.esm.browser.js"></script>

<!-- ES Module minified build -->
<script src="https://unpkg.com/vue-class-component@latest/dist/vue-class-component.esm.browser.min.js"></script>

不同的打包方式

Vue Class Component 提供不同的打包方式, 用來在不同的環(huán)境下使用

  • For development
    • vue-class-component.js (UMD)
    • vue-class-component.common.js (CommonJS)
    • vue-class-component.esm.js (ES Module for bundlers)
    • vue-class-component.esm.browser.js (ES Module for browsers)
  • For production (minified)
    • vue-class-component.min.js (UMD)
    • vue-class-component.esm.browser.min.js (ES Module for browsers)

Class 組件

@Component 裝飾器可以讓你能創(chuàng)建一個(gè)基于Class的Vue組件:

import Vue from 'vue'
import Component from 'vue-class-component'

// HelloWorld class will be a Vue component
@Component
export default class HelloWorld extends Vue {}

Data

使用Class屬性來初始化 data:

<template>
  <div>{{ message }}</div>
</template>

<script>
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class HelloWorld extends Vue {
  // 定義 component 的 data
  message = 'Hello World!'
}
</script>

The above component renders 上面的組件會(huì)在<div>中的組件data message中渲染Hello World!

注意如果初始化的值是 undefined, Class屬性將不是響應(yīng)式的, 意思就是當(dāng)其發(fā)生修改后, 將不會(huì)被偵測到:

import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class HelloWorld extends Vue {
  // `message` 將不是響應(yīng)式數(shù)據(jù)
  message = undefined
}

為了防止這種情況, 你需要使用 null 來賦值, 或者使用 data 鉤子來代替:

import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class HelloWorld extends Vue {
  // `message` 將是響應(yīng)式
  message = null

  data() {
    return {
      // `hello` 將是響應(yīng)式的, 因?yàn)樵赿ata鉤子里
      hello: undefined
    }
  }
}

Methods

組件 methods 將直接定義在Class方法屬性中:

<template>
  <button v-on:click="hello">Click</button>
</template>

<script>
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class HelloWorld extends Vue {
  // 定義一個(gè)組件方法
  hello() {
    console.log('Hello World!')
  }
}
</script>

Computed Properties

計(jì)算屬性可以通過Class屬性的 getter / setter 定義:

<template>
  <input v-model="name">
</template>

<script>
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class HelloWorld extends Vue {
  firstName = 'John'
  lastName = 'Doe'

  // 定義計(jì)算屬性的 getter
  get name() {
    return this.firstName + ' ' + this.lastName
  }

  // 定義計(jì)算屬性的 setter
  set name(value) {
    const splitted = value.split(' ')
    this.firstName = splitted[0]
    this.lastName = splitted[1] || ''
  }
}
</script>

Hooks

data, render 以及所有Vue生命周期鉤子可以直接在Class屬性方法中直接定義, 但是你不能在實(shí)例本身上調(diào)用他們. 當(dāng)定義自定義方法時(shí), 你應(yīng)該回避這些保留名

import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class HelloWorld extends Vue {
  // 定義生命周七鉤子函數(shù)
  mounted() {
    console.log('mounted')
  }

  // 定義渲染函數(shù)
  render() {
    return <div>Hello World!</div>
  }
}

其他選項(xiàng)

對于其他的選項(xiàng), 使用修飾器來配置它們:

<template>
  <OtherComponent />
</template>

<script>
import Vue from 'vue'
import Component from 'vue-class-component'
import OtherComponent from './OtherComponent.vue'

@Component({
  // 查看 Vue.js 文檔, 來了解所有的選項(xiàng):
  // https://vuejs.org/v2/api/#Options-Data
  components: {
    OtherComponent
  }
})
export default class HelloWorld extends Vue {}
</script>

額外的鉤子

如果你使用Vue的插件, 比如Vue Router, 你或許需要Class組件來解決它們提供的鉤子. 在這個(gè)例子中, Component.registerHooks允許你注冊這些鉤子:

// class-component-hooks.js
import Component from 'vue-class-component'

// 使用路由鉤子函數(shù)的名字注冊
Component.registerHooks([
  'beforeRouteEnter',
  'beforeRouteLeave',
  'beforeRouteUpdate'
])

在注冊完這些鉤子后, 就可以在Class組件中把它們當(dāng)作Class屬性方法來使用:

import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class HelloWorld extends Vue {
  beforeRouteEnter(to, from, next) {
    console.log('beforeRouteEnter')
    next()
  }

  beforeRouteUpdate(to, from, next) {
    console.log('beforeRouteUpdate')
    next()
  }

  beforeRouteLeave(to, from, next) {
    console.log('beforeRouteLeave')
    next()
  }
}

推薦在單獨(dú)的文件中寫注冊鉤子的代碼, 因?yàn)槟阈枰谄渌M件定義前注冊它們. 你可以在文件頂部使用 import 引入:

// main.js

// 確定在引入其他組件前注冊
import './class-component-hooks'

import Vue from 'vue'
import App from './App'

new Vue({
  el: '#app',
  render: h => h(App)
})

自定義裝飾器

你可以通過創(chuàng)建你自己的裝飾器來擴(kuò)展功能性的庫. Vue Class Component 提供 createDecorator 幫助創(chuàng)建自定義裝飾器. createDecorator 接受一個(gè)回調(diào)函數(shù)作為第一個(gè)參數(shù), 回調(diào)將接受下面的參數(shù):

  • options: Vue 組件選項(xiàng). 改變這個(gè)對象將影響所提供的組件.
  • key: 裝飾器所需要的屬性或方法的鍵.
  • parameterIndex: 如果自定義裝飾器用于參數(shù),則裝飾參數(shù)的索引.

下面例子是創(chuàng)建一個(gè) Log 裝飾器, 當(dāng)修飾器方法被調(diào)用時(shí), 打印log信息, 包括方法名和參數(shù):

// decorators.js
import { createDecorator } from 'vue-class-component'

// 定義 Log 裝飾器.
export const Log = createDecorator((options, key) => {
  // 保存原始方法.
  const originalMethod = options.methods[key]

  // 覆蓋方法.
  options.methods[key] = function wrapperMethod(...args) {
    // 打印一個(gè) log.
    console.log(`Invoked: ${key}(`, ...args, ')')

    // 調(diào)用原始方法
    originalMethod.apply(this, args)
  }
})

作為方法修飾器使用它:

import Vue from 'vue'
import Component from 'vue-class-component'
import { Log } from './decorators'

@Component
class MyComp extends Vue {
  // 當(dāng)`hello`方法被調(diào)用, 打印一個(gè)log
  @Log
  hello(value) {
    // ...
  }
}

在上面代碼中, 當(dāng) hello 被調(diào)用, 并傳入 42, 將會(huì)打印處下面的log:

Invoked: hello( 42 )

擴(kuò)展 和 Mixins

擴(kuò)展

你可以擴(kuò)展一個(gè)存在的Class組件, 類似于原生的Class繼承. 想象你有下面的名為Super的Class組件:

// super.js
import Vue from 'vue'
import Component from 'vue-class-component'

// 定義一個(gè)叫super的Class組件
@Component
export default class Super extends Vue {
  superValue = 'Hello'
}

你可以擴(kuò)展他, 通過使用原生的Class繼承語法:

import Super from './super'
import Component from 'vue-class-component'

// 擴(kuò)展  名為Super的Class組件
@Component
export default class HelloWorld extends Super {
  created() {
    console.log(this.superValue) // -> Hello
  }
}

注意 名為Super的Class組件必須是一個(gè)Class組件. 換句話說, 它需要繼承作為原本的Vue構(gòu)造器以及被 @Component 裝飾器裝飾.

Mixins

Vue Class Component 提供 mixins 助手函數(shù)來在Class風(fēng)格中使用 mixins. 通過使用mixins助手, TypeScript 可以推斷mixin類型以及在組件類型中繼承它們.

例子中定義了名為HelloWorld 的 mixins :

// mixins.js
import Vue from 'vue'
import Component from 'vue-class-component'

// 你可以定義和組件風(fēng)格一樣的 mixins .
@Component
export class Hello extends Vue {
  hello = 'Hello'
}

@Component
export class World extends Vue {
  world = 'World'
}

在Class組件中使用它們:

import Component, { mixins } from 'vue-class-component'
import { Hello, World } from './mixins'

// 使用 `mixins` 助手函數(shù)代替 `Vue`.
// `mixins` 可以接收任何數(shù)量的參數(shù).
@Component
export class HelloWorld extends mixins(Hello, World) {
  created () {
    console.log(this.hello + ' ' + this.world + '!') // -> Hello World!
  }
}

和名為Super的Class組件一樣, 所有的mixins 必須被定義為一個(gè) Class 組件.

Class 組件的注意事項(xiàng)

Vue Class Component 收集Class屬性作為Vue實(shí)例的data, 通過在引擎下實(shí)例化原始的構(gòu)造器. 當(dāng)我們能像原生Class方法一樣定義實(shí)例data時(shí), 我們需要了解它時(shí)如何工作的.

在屬性中初始化this的值

如果你在類的屬性中定義一個(gè)箭頭函數(shù), 箭頭函數(shù)中訪問 this 時(shí), 將無法獲取實(shí)例. 這是因?yàn)楫?dāng)初始化Class屬性時(shí), this僅僅時(shí)Vue實(shí)例的代理:

import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class MyComp extends Vue {
  foo = 123

  // 不要這么做
  bar = () => {
    // 不能這樣更新屬性
    // 事實(shí)上`this` 不是Vue實(shí)例.
    this.foo = 456
  }
}

在下面的例子中, 你可以簡單的定義一個(gè)方法代替一個(gè)Class屬性, 因?yàn)閂ue將自動(dòng)綁定實(shí)例:

import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class MyComp extends Vue {
  foo = 123

  bar() {
    // 正確的更新屬性
    this.foo = 456
  }
}

通常使用生命周期函數(shù)代替 constructor

由于原始構(gòu)造函數(shù)被調(diào)用來收集初始組件數(shù)據(jù), 建議不要自己聲明 constructor:

import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class Posts extends Vue {
  posts = []

  // 不要這么做
  constructor() {
    fetch('/posts.json')
      .then(res => res.json())
      .then(posts => {
        this.posts = posts
      })
  }
}

上面的代碼打算在組件初始化的時(shí)候用fetch來獲取post列表, 但是fetch將會(huì)被調(diào)用兩次, 因?yàn)閂ue Class Component的運(yùn)作

所以推薦寫在生命周期函數(shù)里, 比如用created 代替 constructor:

import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class Posts extends Vue {
  posts = []

  // 這樣做才對哦
  created() {
    fetch('/posts.json')
      .then(res => res.json())
      .then(posts => {
        this.posts = posts
      })
  }
}

Props 定義

Vue Class Component沒有提供用于 props 定義的專用API, 當(dāng)然, 你可以使用Vue.extend API來做到這個(gè):

<template>
  <div>{{ message }}</div>
</template>

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'

// 使用Vue的經(jīng)典方法定義props
const GreetingProps = Vue.extend({
  props: {
    name: String
  }
})

// 通過擴(kuò)展GreetingProps來定義 props
@Component
export default class Greeting extends GreetingProps {
  get message(): string {
    // this.name will be typed
    return 'Hello, ' + this.name
  }
}
</script>

Vue.extend 推算定義的 prop 的類型, 可以通過擴(kuò)展它來在你的Class組件中使用.

如果你有一個(gè)名為super的Class組件或者mixins來擴(kuò)展, 使用 mixins 助手來關(guān)聯(lián)定義的props:

<template>
  <div>{{ message }}</div>
</template>

<script lang="ts">
import Vue from 'vue'
import Component, { mixins } from 'vue-class-component'
import Super from './super'

// 使用Vue的經(jīng)典方法定義props
const GreetingProps = Vue.extend({
  props: {
    name: String
  }
})

// 使用 `mixins` 助手來關(guān)聯(lián)定義props 和一個(gè) mixin.
@Component
export default class Greeting extends mixins(GreetingProps, Super) {
  get message(): string {
    // this.name will be typed
    return 'Hello, ' + this.name
  }
}
</script>

Property Type Declaration

有時(shí)候, 你需要在Class組件外定義組件屬性和方法. 例如, Vuex, Vue的狀態(tài)管理庫,提供 mapGettersmapActions 助手來映射一個(gè)存儲到組件屬性和方法. 這些助手函數(shù)需要在組件對象的選項(xiàng)中使用.

甚至在這個(gè)例子中, 你可以將組件選項(xiàng)作為@Component 裝飾器的參數(shù). 然而, 在運(yùn)行時(shí)方式工作時(shí),它不能自動(dòng)的在類型級別上定義屬性和方法

你需要在Class組件里手動(dòng)的定義它們的類型:

import Vue from 'vue'
import Component from 'vue-class-component'
import { mapGetters, mapActions } from 'vuex'

// post 接口
import { Post } from './post'

@Component({
  computed: mapGetters([
    'posts'
  ]),

  methods: mapActions([
    'fetchPosts'
  ])
})
export default class Posts extends Vue {
  // 在類型級別上定義 getters 和 actions的映射.
  // 你或許需要添加 `!` 在屬性的名字后
  // 來避免編譯錯(cuò)誤 (definite assignment assertion).

  // 給映射的posts getter 賦予類型
  posts!: Post[]

  // 給映射的 fetchPosts action 賦予類型
  fetchPosts!: () => Promise<void>

  mounted() {
    // 使用映射的getter 和 action.
    this.fetchPosts().then(() => {
      console.log(this.posts)
    })
  }
}

$refs 類型擴(kuò)展

$refs組件的類型聲明為處理所有可能的ref類型的最廣泛的類型. 雖然從理論上來它是收集的,但在大多數(shù)情況下,每個(gè)ref在實(shí)踐中僅具有特定的元素或組件.

通過在Class組件里重寫$refs類型, 你可以指定一個(gè)特定的 ref 類型:

<template>
  <input ref="input">
</template>

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class InputFocus extends Vue {
  // 解釋 refs 的類型.
  // 符號 `!` (definite assignment assertion)
  // 用來擺脫編譯錯(cuò)誤.
  $refs!: {
    input: HTMLInputElement
  }

  mounted() {
    // 使用 `input` ref 不用拋出類型
    this.$refs.input.focus()
  }
}
</script>

你可以訪問 input 類型而不用拋出類型, 因?yàn)樵谏厦媸纠? 在Class組件上指定了$refs.input 的類型.

注意, 需要一個(gè)類型注解(使用符號 :) 而不是賦值(=).

Hooks Auto-complete

Vue Class Component 提供內(nèi)建的鉤子類型, 這將自動(dòng)應(yīng)用在Class組件中定義的 data, render 和 其他生命周期鉤子上, 對于 TypeScript. 使用它, 你需要引入位于vue-class-component/hooks的鉤子類型.

// main.ts
import 'vue-class-component/hooks'
import Vue from 'vue'
import App from './App.vue'

new Vue({
  render: h => h(App)
}).$mount('#app')

如果你想讓它可以應(yīng)用在自定義鉤子上, 你可以自己手動(dòng)添加:

import Vue from 'vue'
import { Route, RawLocation } from 'vue-router'

declare module 'vue/types/vue' {
  // 增強(qiáng)組件實(shí)例的類型
  interface Vue {
    beforeRouteEnter?(
      to: Route,
      from: Route,
      next: (to?: RawLocation | false | ((vm: Vue) => void)) => void
    ): void

    beforeRouteLeave?(
      to: Route,
      from: Route,
      next: (to?: RawLocation | false | ((vm: Vue) => void)) => void
    ): void

    beforeRouteUpdate?(
      to: Route,
      from: Route,
      next: (to?: RawLocation | false | ((vm: Vue) => void)) => void
    ): void
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容