英文文档地址: https://class-component.vuejs.org/
本文包括了文档中的 总览, 安装, 指南, TypeScript指南
总览
Vue Class Component 是一个可以让你使用Class风格语法编写Vue组件的库. 例如, 下面是一个通过Vue Class Component编写的简单的计数器组件的例子:
{{ count }}
正如例子展现的, 你可以使用通过@Component
装饰器标注Class, 来用直观和标准的Class语法定义组件的data和方法. 你可以简单地使用Class风格的组件代替组件定义, 因为它等价于普通的使用对象定义的组件.
通过使用Class风格定义的组件, 你不但要改变语法, 还要利用一些ECMAScript语法特性, 比如Class继承和装饰器. Vue Class Component 也提供了一个mixins
帮助 来继承mixin, 以及一个 createDecorator
方法来简单地创建你自己的修饰器.
你或许也需要使用 Vue Property Decorator 提供的 @Prop
和 @Watch
装饰器.
安装
Vue CLI 安装
你可以通过使用 Vue CLI 简单的安装 Vue Class Component. 运行下面命令来创建一个新项目:
$ vue create hello-world
你将被告知是否使用预设配置. 请选择 "Manually select features":
选中 TypeScript 功能来使用 Vue Class Component. 你可以添加其他你需要的功能:
输入 y
来回答 Use class-style component syntax?
:
你可以按照自己的偏爱回答剩下的问题. 在结束安装进程后, Vue CLI 会创建一个新的安装了Vue Class Component 的项目目录.
手动安装
如果你要手动安装, 娜美你需要使用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 你需要在你的项目中配置 TypeScript 或者 Babel, 因为它依赖于 ECMAScript stage 1 decorators 编译, 从而能在浏览器中运行.
::: 注意
它不支持 stage 2 decorators, 由于 TypeScript 编译器只支持旧版本装饰器.
:::
TypeScript
在项目根目录创建 tsconfig.json
, 然后配置 experimentalDecorators
选项, 它可以编译装饰器语法:
{
"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 }]
]
}
注意 legacy
和 loose
选项需要 Vue Class Component 只支持 stage 1 (legacy) decorator spec.
CDN
unpkg.com 提供基于npm的 CDN 链接. 你可以选择对应版本的 Vue Class Component 来代替 @latest
版本 (例如 https://unpkg.com/[email protected]/dist/vue-class-component.js
就是使用版本为 7.2.2的Vue Class Component).
不同的打包方式
Vue Class Component 提供不同的打包方式, 用来在不同的环境下使用
- 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
装饰器可以让你能创建一个基于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
:
{{ message }}
The above component renders 上面的组件会在 注意如果初始化的值是 为了防止这种情况, 你需要使用 组件 计算属性可以通过Class属性的 getter / setter 定义: 对于其他的选项, 使用修饰器来配置它们: 如果你使用Vue的插件, 比如Vue Router, 你或许需要Class组件来解决它们提供的钩子. 在这个例子中, 在注册完这些钩子后, 就可以在Class组件中把它们当作Class属性方法来使用: 推荐在单独的文件中写注册钩子的代码, 因为你需要在其他组件定义前注册它们. 你可以在文件顶部使用 你可以通过创建你自己的装饰器来扩展功能性的库. Vue Class Component 提供 下面例子是创建一个 作为方法修饰器使用它: 在上面代码中, 当 你可以扩展一个存在的Class组件, 类似于原生的Class继承. 想象你有下面的名为Super的Class组件: 你可以扩展他, 通过使用原生的Class继承语法: 注意 名为Super的Class组件必须是一个Class组件. 换句话说, 它需要继承作为原本的 Vue Class Component 提供 例子中定义了名为 在Class组件中使用它们: 和名为Super的Class组件一样, 所有的mixins 必须被定义为一个 Class 组件. Vue Class Component 收集Class属性作为Vue实例的data, 通过在引擎下实例化原始的构造器. 当我们能像原生Class方法一样定义实例data时, 我们需要了解它时如何工作的. 如果你在类的属性中定义一个箭头函数, 箭头函数中访问 在下面的例子中, 你可以简单的定义一个方法代替一个Class属性, 因为Vue将自动绑定实例: 由于原始构造函数被调用来收集初始组件数据, 建议不要自己声明 上面的代码打算在组件初始化的时候用fetch来获取post列表, 但是fetch将会被调用两次, 因为Vue Class Component的运作 所以推荐写在生命周期函数里, 比如用 Vue Class Component没有提供用于 props 定义的专用API, 当然, 你可以使用 如果你有一个名为super的Class组件或者mixins来扩展, 使用 有时候, 你需要在Class组件外定义组件属性和方法. 例如, Vuex, Vue的状态管理库,提供 甚至在这个例子中, 你可以将组件选项作为 你需要在Class组件里手动的定义它们的类型: 通过在Class组件里重写 你可以访问 注意, 需要一个类型注解(使用符号 Vue Class Component 提供内建的钩子类型, 这将自动应用在Class组件中定义的 如果你想让它可以应用在自定义钩子上, 你可以自己手动添加:message
中渲染Hello World!
undefined
, Class属性将不是响应式的, 意思就是当其发生修改后, 将不会被侦测到:import Vue from 'vue'
import Component from 'vue-class-component'
@Component
export default class HelloWorld extends Vue {
// `message` 将不是响应式数据
message = undefined
}
null
来赋值, 或者使用 data
钩子来代替:import Vue from 'vue'
import Component from 'vue-class-component'
@Component
export default class HelloWorld extends Vue {
// `message` 将是响应式
message = null
data() {
return {
// `hello` 将是响应式的, 因为在data钩子里
hello: undefined
}
}
}
Methods
methods
将直接定义在Class方法属性中:
Computed Properties
Hooks
data
, render
以及所有Vue生命周期钩子可以直接在Class属性方法中直接定义, 但是你不能在实例本身上调用他们. 当定义自定义方法时, 你应该回避这些保留名import Vue from 'vue'
import Component from 'vue-class-component'
@Component
export default class HelloWorld extends Vue {
// 定义生命周七钩子函数
mounted() {
console.log('mounted')
}
// 定义渲染函数
render() {
return
其他选项
额外的钩子
Component.registerHooks
允许你注册这些钩子:// class-component-hooks.js
import Component from 'vue-class-component'
// 使用路由钩子函数的名字注册
Component.registerHooks([
'beforeRouteEnter',
'beforeRouteLeave',
'beforeRouteUpdate'
])
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()
}
}
import
引入:// main.js
// 确定在引入其他组件前注册
import './class-component-hooks'
import Vue from 'vue'
import App from './App'
new Vue({
el: '#app',
render: h => h(App)
})
自定义装饰器
createDecorator
帮助创建自定义装饰器. createDecorator
接受一个回调函数作为第一个参数, 回调将接受下面的参数:
options
: Vue 组件选项. 改变这个对象将影响所提供的组件.key
: 装饰器所需要的属性或方法的键.parameterIndex
: 如果自定义装饰器用于参数,则装饰参数的索引.Log
装饰器, 当修饰器方法被调用时, 打印log信息, 包括方法名和参数:// 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) {
// 打印一个 log.
console.log(`Invoked: ${key}(`, ...args, ')')
// 调用原始方法
originalMethod.apply(this, args)
}
})
import Vue from 'vue'
import Component from 'vue-class-component'
import { Log } from './decorators'
@Component
class MyComp extends Vue {
// 当`hello`方法被调用, 打印一个log
@Log
hello(value) {
// ...
}
}
hello
被调用, 并传入 42
, 将会打印处下面的log:Invoked: hello( 42 )
扩展 和 Mixins
扩展
// super.js
import Vue from 'vue'
import Component from 'vue-class-component'
// 定义一个叫super的Class组件
@Component
export default class Super extends Vue {
superValue = 'Hello'
}
import Super from './super'
import Component from 'vue-class-component'
// 扩展 名为Super的Class组件
@Component
export default class HelloWorld extends Super {
created() {
console.log(this.superValue) // -> Hello
}
}
Vue
构造器以及被 @Component
装饰器装饰.Mixins
mixins
助手函数来在Class风格中使用 mixins. 通过使用mixins
助手, TypeScript 可以推断mixin类型以及在组件类型中继承它们.Hello
和 World
的 mixins :// mixins.js
import Vue from 'vue'
import Component from 'vue-class-component'
// 你可以定义和组件风格一样的 mixins .
@Component
export class Hello extends Vue {
hello = 'Hello'
}
@Component
export class World extends Vue {
world = 'World'
}
import Component, { mixins } from 'vue-class-component'
import { Hello, World } from './mixins'
// 使用 `mixins` 助手函数代替 `Vue`.
// `mixins` 可以接收任何数量的参数.
@Component
export class HelloWorld extends mixins(Hello, World) {
created () {
console.log(this.hello + ' ' + this.world + '!') // -> Hello World!
}
}
Class 组件的注意事项
在属性中初始化
this
的值this
时, 将无法获取实例. 这是因为当初始化Class属性时, this
仅仅时Vue实例的代理:import Vue from 'vue'
import Component from 'vue-class-component'
@Component
export default class MyComp extends Vue {
foo = 123
// 不要这么做
bar = () => {
// 不能这样更新属性
// 事实上`this` 不是Vue实例.
this.foo = 456
}
}
import Vue from 'vue'
import Component from 'vue-class-component'
@Component
export default class MyComp extends Vue {
foo = 123
bar() {
// 正确的更新属性
this.foo = 456
}
}
通常使用生命周期函数代替
constructor
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
})
}
}
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.extend
API来做到这个:
Vue.extend
推算定义的 prop 的类型, 可以通过扩展它来在你的Class组件中使用.mixins
助手来关联定义的props:
Property Type Declaration
mapGetters
和 mapActions
助手来映射一个存储到组件属性和方法. 这些助手函数需要在组件对象的选项中使用.@Component
装饰器的参数. 然而, 在运行时方式工作时,它不能自动的在类型级别上定义属性和方法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的映射.
// 你或许需要添加 `!` 在属性的名字后
// 来避免编译错误 (definite assignment assertion).
// 给映射的posts getter 赋予类型
posts!: Post[]
// 给映射的 fetchPosts action 赋予类型
fetchPosts!: () => Promise
$refs
类型扩展$refs
组件的类型声明为处理所有可能的ref类型的最广泛的类型. 虽然从理论上来它是收集的,但在大多数情况下,每个ref在实践中仅具有特定的元素或组件.$refs
类型, 你可以指定一个特定的 ref 类型:
input
类型而不用抛出类型, 因为在上面示例中, 在Class组件上指定了$refs.input
的类型.:
) 而不是赋值(=
).Hooks Auto-complete
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')
import Vue from 'vue'
import { Route, RawLocation } from 'vue-router'
declare module 'vue/types/vue' {
// 增强组件实例的类型
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
}
}