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... 的回答。概括来说主要包括以下几点:
- 静态类型检查
- IDE 智能提示
- 代码重构
- 可读性
❝静态类型检查可以避免很多不必要的错误, 不用在调试的时候才发现问题 ❞
项目搭建
- Generate project in current directory?是否在当前目录创建项目
- Choose Vue version,Babel,Ts,Router,Vuex,CSS Pre-processor
- Use class-style component syntax?
- Use Babel alongside Typescript
- Use history mode for router?
- Pick a CSS Pre-processor:Less
- Where do you prefer placing config for Babel,ESLint,
- Save this as a preset for future project?
- Save preset as
可能需要等待几分钟安装依赖,安装成功之后,使用 yarn serve
运行
概览
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>
效果如下:
类组件
数据
我们可以这样初始化 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 section for details about `data` hook inside class.
data() {
return {
// `hello` will be reactive as it is declared via `data` hook.
hello: undefined
}
}
}
方法
组件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>
计算属性
代码语言:javascript复制<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>
生命钩子
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>
}
}
组件注册
代码语言:javascript复制<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类组件为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)
}
})
使用它作为方法装饰器:
代码语言:javascript复制<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>
扩展和混合
扩展
您可以将现有的类组件扩展为本机类继承。假设您具有以下超类组件:
在 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'
}
您可以使用本机类继承语法对其进行扩展:
代码语言:javascript复制<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类组件提供了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'
}
在类样式组件中使用它们:
代码语言:javascript复制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会自动绑定实例:
代码语言:javascript复制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>