0
点赞
收藏
分享

微信扫一扫

vue + typescript 类组件教程

以沫的窝 2022-04-06 阅读 72


typescript 简介

​TypeScript​​​ 是 ​​JavaScript​​​ 的强类型版本。然后在编译期去掉类型和特有语法,生成纯粹的 ​​JavaScript​​​ 代码。由于最终在浏览器中运行的仍然是 ​​JavaScript​​​,所以 ​​TypeScript​​ 并不依赖于浏览器的支持,也并不会带来兼容性问题。

​TypeScript​​​ 是 ​​JavaScript​​​ 的超集,这意味着他支持所有的 ​​JavaScript​​​ 语法。并在此之上对 ​​JavaScript​​​ 添加了一些扩展,如 ​​class​​​ / ​​interface​​​ / ​​module​​ 等。这样会大大提升代码的可阅读性。

与此同时,​​TypeScript​​​ 也是 ​​JavaScript ES6​​​ 的超集,​​Google​​​ 的 ​​Angular 2.0​​​ 也宣布采用 ​​TypeScript​​ 进行开发。这更是充分说明了这是一门面向未来并且脚踏实地的语言。

强类型语言的优势在于静态类型检查,具体可以参见 http://www.zhihu.com/question... 的回答。概括来说主要包括以下几点:

  1. 静态类型检查

  2. IDE 智能提示

  3. 代码重构

  4. 可读性

静态类型检查可以避免很多不必要的错误, 不用在调试的时候才发现问题

项目搭建

  • Generate project in current directory?是否在当前目录创建项目

vue + typescript 类组件教程_vue

  • Choose Vue version,Babel,Ts,Router,Vuex,CSS Pre-processor

vue + typescript 类组件教程_vue_02

  • Use class-style component syntax?

vue + typescript 类组件教程_数据可视化_03

  • Use Babel alongside Typescript

vue + typescript 类组件教程_类_04

  • Use history mode for router?

vue + typescript 类组件教程_类_05

  • Pick a CSS Pre-processor:Less

vue + typescript 类组件教程_数据可视化_06

  • Where do you prefer placing config for Babel,ESLint,

vue + typescript 类组件教程_类_07

  • Save this as a preset for future project?

  • Save preset as

vue + typescript 类组件教程_vue_08

可能需要等待几分钟安装依赖,安装成功之后,使用 ​​yarn serve​​ 运行

vue + typescript 类组件教程_python_09

概览

Vue 类组件是一个库,可让你使用类的语法制作 Vue 组件。例如,我们可以使用 Vue 类语法制作一个计算器组件:

在 ​​src/components​​​ 新建 ​​Counter.vue​​​。​​html​​ 部分跟往常一样。

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

重点部分就是 ​​javascript​​:

您可以通过使用​​@Component​​装饰器为类添加注释,从而以直观和标准的类语法定义组件数据和方法。您可以简单地用类样式的组件替换组件定义,因为它等同于组件定义的普通options对象样式。

通过以类样式定义组件,不仅可以更改语法,还可以利用某些ECMAScript语言功能,例如类继承和装饰器。Vue类组件还提供了一个用于mixin继承的​​mixins​​​助手,以及一个轻松创建自己的装饰器的​​createDecorator​​函数。

您可能还需要检查Vue Property Decorator提供的​​@Prop​​​和​​@Watch​​装饰器

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

// Define the component in class-style
@Component
export default class Counter extends Vue {
// Class properties will be component data
count = 0

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

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

在 ​​src/views/About.vue​​​ 引入 ​​Couter.vue​​。

通过 ​​vue-class-component​​​ 导入 ​​Component​​​ 装饰器函数,注册局部组件 ​​Couter​

<template>
<div class="about">
<h1>This is an about page</h1>
<Counter />
</div>
</template>

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

@Component({
components: {
Counter
}
})
export default class About extends Vue {

}
</script>

效果如下:

vue + typescript 类组件教程_python_10

类组件

数据

我们可以这样初始化 ​​data​​ 数据:

在 ​​About​​​ 类组件中,定义 ​​message​​​ 变量,在模板中使用 ​​{{}}​​ 插值。

<template>
<div class="about">
<h1>{{message}}</h1>
<Counter />
</div>
</template>

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

@Component({
components: {
Counter
}
})
export default class About extends Vue {
message = "This is an about page"
}
</script>

上面的组件按原样在组件数据中呈现​​Hello World!​​​在​​<h1>​​​元素中​​message​​。

请注意,如果初始值为​​undefined​​,则class属性不会是反应性的,这意味着将不会检测到对属性的更改:

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

@Component
export default class About extends Vue {
// `message` will not be reactive value
message = undefined
}

为了避免这种情况,您可以使用​​null​​​value或使用​​data​​hook来代替:

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

@Component
export default class About extends Vue {
// `message` will be reactive with `null` value
message = null

// See Hooks p for details about `data` hook inside class.
data() {
return {
// `hello` will be reactive as it is declared via `data` hook.
hello: undefined
}
}
}

vue + typescript 类组件教程_类_11

方法

组件​​methods​​可以直接声明为类原型方法:

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

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

@Component
export default class About extends Vue {
// Declared as component method
hello() {
console.log('Hello World!')
}
}
</script>

vue + typescript 类组件教程_python_12

计算属性

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

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

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

// Declared as computed property getter
get name() {
return this.firstName + ' ' + this.lastName
}

// Declared as computed property setter
set name(value) {
const splitted = value.split(' ')
this.firstName = splitted[0]
this.lastName = splitted[1] || ''
}
}
</script>

vue + typescript 类组件教程_python_13

生命钩子

​data​​​,​​render​​所有Vue生命周期挂钩也可以直接声明为类原型方法,但是您不能在实例本身上调用它们。声明自定义方法时,应避免使用这些保留名称。

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

@Component
export default class About extends Vue {
// Declare mounted lifecycle hook
mounted() {
console.log('mounted')
}

// Declare render function
render() {
return <div>Hello World!</div>
}
}

组件注册

<template>
<div class="about">
<h1>This is an about page</h1>
<Counter />
</div>
</template>

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

@Component({
components: {
Counter
}
})
export default class About extends Vue {

}
</script>

路由钩子

如果使用Vue Router等Vue插件,则可能希望类组件解析它们提供的钩子。在这种情况下,​​Component.registerHooks​​允许您注册这样的钩子:

<template>
<div class="about">
<h1>{{message}}</h1>
<Counter />
<button v-on:click="hello">Click</button>
<input v-model="name" />
</div>
</template>

<script>
import Vue from "vue";
import Component from "vue-class-component";
import Counter from "../components/Counter";
// Register the router hooks with their names
Component.registerHooks([
'beforeRouteEnter',
'beforeRouteLeave',
'beforeRouteUpdate'
])

@Component({
components: {
Counter
}
})
export default class About extends Vue {
message = "This is an about page";
firstName = "John";
lastName = "Doe";

// Declared as computed property getter
get name() {
return this.firstName + " " + this.lastName;
}

// Declared as computed property setter
set name(value) {
const splitted = value.split(" ");
this.firstName = splitted[0];
this.lastName = splitted[1] || "";
}
hello() {
console.log("Hello World!");
}
// Declare mounted lifecycle hook
mounted() {
console.log("mounted");
}

// Declare render function
render() {
return <div > Hello World! < /div>;
}

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

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

beforeRouteLeave(to, from, next) {
console.log('beforeRouteLeave')
next()
}
}
</script>

vue + typescript 类组件教程_数据可视化_14

定制装饰器

您可以通过创建自己的装饰器来扩展此库的功能。Vue类组件为​​createDecorator​​​创建自定义装饰器提供了帮助。​​createDecorator​​期望将回调函数作为第一个参数,并且该回调将接收以下参数:

  • ​options​​:Vue组件选项对象。对该对象所做的更改将影响所提供的组件。

  • ​key​​:应用装饰器的属性或方法键。

  • ​parameterIndex​​:如果自定义修饰符用于参数,则修饰参数的索引。

创建​​Log​​装饰器的示例,该装饰器在调用装饰的方法时输出带有方法名称和传递的参数的日志消息:

在 ​​src/components/​​​ 新建 ​​decorators.js​

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

// Declare Log decorator.
export const Log = createDecorator((options, key) => {
// Keep the original method for later.
const originalMethod = options.methods[key]

// Wrap the method with the logging logic.
options.methods[key] = function wrapperMethod(...args) {
// Print a log.
console.log(`Invoked: ${key}(`, ...args, ')')

// Invoke the original method.
originalMethod.apply(this, args)
}
})

使用它作为方法装饰器:

<template>
<div class="about">
<h1>{{message}}</h1>
<Counter />
<button v-on:click="hello">Click</button>
<input v-model="name" />
</div>
</template>

<script>
import Vue from "vue";
import Component from "vue-class-component";
import Counter from "../components/Counter";
import {
Log
} from '../components/decorators'
// Register the router hooks with their names
Component.registerHooks([
'beforeRouteEnter',
'beforeRouteLeave',
'beforeRouteUpdate'
])

@Component({
components: {
Counter
}
})
export default class About extends Vue {
message = "This is an about page";
firstName = "John";
lastName = "Doe";

// Declared as computed property getter
get name() {
return this.firstName + " " + this.lastName;
}

// Declared as computed property setter
set name(value) {
const splitted = value.split(" ");
this.firstName = splitted[0];
this.lastName = splitted[1] || "";
}
@Log
hello(value) {
console.log("Hello World!", value);
}
// Declare mounted lifecycle hook
mounted() {
console.log("mounted");
}

// Declare render function
render() {
return <div > Hello World! < /div>;
}

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

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

beforeRouteLeave(to, from, next) {
console.log('beforeRouteLeave')
next()
}
}
</script>

vue + typescript 类组件教程_数据可视化_14

扩展和混合

扩展

您可以将现有的类组件扩展为本机类继承。假设您具有以下超类组件:

在 ​​src/components/​​​ 新建 ​​super.js​

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

// Define a super class component
@Component
export default class Super extends Vue {
superValue = 'Hello'
}

您可以使用本机类继承语法对其进行扩展:

<template>
<div class="about">
<h1>{{message}}</h1>
<Counter />
<button v-on:click="hello">Click</button>
<input v-model="name" />
</div>
</template>

<script>
import Vue from "vue";
import Component from "vue-class-component";
import Counter from "../components/Counter";
import {
Log
} from '../components/decorators'
import Super from '../components/super'
// Register the router hooks with their names
Component.registerHooks([
'beforeRouteEnter',
'beforeRouteLeave',
'beforeRouteUpdate'
])

@Component({
components: {
Counter
}
})
export default class About extends Super {
message = "This is an about page";
firstName = "John";
lastName = "Doe";

// Declared as computed property getter
get name() {
return this.firstName + " " + this.lastName;
}

// Declared as computed property setter
set name(value) {
const splitted = value.split(" ");
this.firstName = splitted[0];
this.lastName = splitted[1] || "";
}
@Log
hello(value) {
console.log("Hello World!", value);
}
// Declare mounted lifecycle hook
mounted() {
console.log("mounted");
}
created() {
console.log(this.superValue)
}
// Declare render function
render() {
return <div > Hello World! < /div>;
}

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

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

beforeRouteLeave(to, from, next) {
console.log('beforeRouteLeave')
next()
}
}
</script>

vue + typescript 类组件教程_数据可视化_16

混入

Vue类组件提供了​​mixins​​​辅助功能,以类样式方式使用mixins。通过使用​​mixins​​帮助程序,TypeScript可以推断混合类型并在组件类型上继承它们。

声明mixins ​​Hello​​​和的示例​​World​​:

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

// You can declare mixins as the same style as components.
@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'

// Use `mixins` helper function instead of `Vue`.
// `mixins` can receive any number of arguments.
@Component
export class About extends mixins(Hello, World) {
created () {
console.log(this.hello + ' ' + this.world + '!') // -> Hello World!
}
}

类组件警告

Vue类组件通过实例化底层的原始构造函数,将类属性收集为Vue实例数据。尽管我们可以像本地类方式那样定义实例数据,但有时我们需要知道其工作方式。

​this​​ 属性初始值设定项中的值

如果将箭头函数定义为类属性并对其进行访问​​this​​​,则它将无法正常工作。这是因为​​this​​在初始化类属性时,它只是Vue实例的代理对象:

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

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

// DO NOT do this
bar = () => {
// Does not update the expected property.
// `this` value is not a Vue instance in fact.
this.foo = 456
}
}

在这种情况下,您可以简单地定义方法而不是类属性,因为Vue会自动绑定实例:

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

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

// DO this
bar() {
// Correctly update the expected property.
this.foo = 456
}
}

始终使用生命周期挂钩而不是 ​​constructor​

当调用原始构造函数以收集初始组件数据时,建议不要​​constructor​​自己声明:

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

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

// DO NOT do this
constructor() {
fetch('/posts.json')
.then(res => res.json())
.then(posts => {
this.posts = posts
})
}
}

上面的代码打算在组件初始化时获取帖子列表,但是由于Vue类组件的工作方式,提取将被意外调用两次。

建议写生命周期挂钩,例如​​created​​​,而不是​​constructor​​:

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

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

// DO this
created() {
fetch('/posts.json')
.then(res => res.json())
.then(posts => {
this.posts = posts
})
}
}

Prop 属性定义

Vue类组件没有提供用于道具定义的专用API。但是,您可以通过使用规范​​Vue.extend​​API 来做到这一点:

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

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

// Define the props by using Vue's canonical way.
const GreetingProps = Vue.extend({
props: {
name: String
}
})

// Use defined props by extending GreetingProps.
@Component
export default class Greeting extends GreetingProps {
get message(): string {
// this.name will be typed
return 'Hello, ' + this.name
}
}
</script>

当​​Vue.extend​​推断定义的prop类型时,可以通过扩展它在类组件中使用它们。

如果您要扩展一个超类组件或mixin,请使用​​mixins​​辅助程序将定义的prop与它们组合:

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

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

// Define the props by using Vue's canonical way.
const GreetingProps = Vue.extend({
props: {
name: String
}
})

// Use `mixins` helper to combine defined props and a mixin.
@Component
export default class Greeting extends mixins(GreetingProps, Super) {
get message(): string {
// this.name will be typed
return 'Hello, ' + this.name
}
}
</script>



举报

相关推荐

0 条评论