据说typescript最近愈来愈流行,借着开启新项目,用vue + typescript尝试了下。夏日炎炎,趁着乘凉的略微时间,进行一下项目总结,望大佬们多多指点!javascript
vue-cli3
脚手架走一波,手动选择使用typescript
,blablabla...
具体就不细说啦. 首先附上一份官方目录配置:css
├── public // 静态页面
├── src // 主目录
├── assets // 静态资源
├── components // 组件
├── views // 页面
├── App.vue // 页面主入口
├── main.ts // 脚本主入口
├── registerServiceWorker.ts // PWA 配置
├── router.ts // 路由
├── shims-tsx.d.ts // 相关 tsx 模块注入
├── shims-vue.d.ts // Vue 模块注入
└── store.ts // vuex 配置
├── tests // 测试用例
├── .postcssrc.js // postcss 配置
├── package.json // 依赖
├── tsconfig.json // ts 配置
└── tslint.json // tslint 配置
复制代码
而后附上我的的目录html
├── public // 静态页面
├── src // 主目录
├── api // 后端接口
├── assets // 静态资源
├── components // 组件
├── filters // 过滤
├── router // 路由配置
├── store // vuex 配置
├── utils // 工具方法
├── mixins // 混合
├── views // 页面
├── App.vue // 页面主入口
├── main.ts // 脚本主入口
├── registerServiceWorker.ts // PWA 配置
├── shims-tsx.d.ts // 相关 tsx 模块注入
├── shims-vue.d.ts // Vue 模块注入
├── tests // 测试用例
├── .editorconfig // 编辑相关配置
├── .npmrc // npm 源配置
├── .postcssrc.js // postcss 配置
├── babel.config.js // preset 记录
├── cypress.json // e2e plugins
├── package.json // 依赖
├── README.md // 项目 readme
├── tsconfig.json // ts 配置
├── tslint.json // tslint 配置
└── vue.config.js // webpack 配置
复制代码
tsconfig.json
配置
首先贴一份网上找的配置,传送门:官方完整配置vue
{
// 编译选项
"compilerOptions": {
// 输出目录
"outDir": "./output",
// 是否包含能够用于 debug 的 sourceMap
"sourceMap": true,
// 以严格模式解析
"strict": true,
// 采用的模块系统
"module": "esnext",
// 如何处理模块
"moduleResolution": "node",
// 编译输出目标 ES 版本
"target": "es5",
// 容许从没有设置默认导出的模块中默认导入
"allowSyntheticDefaultImports": true,
// 将每一个文件做为单独的模块
"isolatedModules": false,
// 启用装饰器
"experimentalDecorators": true,
// 启用设计类型元数据(用于反射)
"emitDecoratorMetadata": true,
// 在表达式和声明上有隐含的any类型时报错
"noImplicitAny": false,
// 不是函数的全部返回路径都有返回值时报错。
"noImplicitReturns": true,
// 从 tslib 导入外部帮助库: 好比__extends,__rest等
"importHelpers": true,
// 编译过程当中打印文件名
"listFiles": true,
// 移除注释
"removeComments": true,
"suppressImplicitAnyIndexErrors": true,
// 在 .tsx文件里支持JSX: "React"或 "Preserve"。查看 JSX
"jsx": "preserve",
// 容许编译javascript文件
"allowJs": true,
// 解析非相对模块名的基准目录
"baseUrl": "./",
// 指定特殊模块的路径
"paths": {
"jquery": [
"node_modules/jquery/dist/jquery"
]
},
// 编译过程当中须要引入的库文件的列表
"lib": [
"dom",
"es2015",
"es2015.promise"
]
}
}
复制代码
而后再贴一份本身的配置,能够根据我的定制, 其中,若是指定了exclude选项,编译器会包含当前目录及子目录下的全部TypeScript文件(*.ts 或 *.tsx),不包括这些指定要排除的文件java
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": false,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"strictFunctionTypes": false,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env",
"mocha",
"chai"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"files": [
"shims-vue.d.ts"
],
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}
复制代码
tslint.json
配置{
"defaultSeverity": "error",
"jsRules": {},
"rules": {
// 容许使用 console
"no-console": false,
// 单引号
"quotemark": false,
// 设置修饰符顺序
"member-ordering": [
true,
{
"order": [
"public-static-field",
"public-static-method",
"protected-static-field",
"protected-static-method",
"private-static-field",
"private-static-method",
"public-instance-field",
"protected-instance-field",
"private-instance-field",
"public-constructor",
"protected-constructor",
"private-constructor",
"public-instance-method",
"protected-instance-method",
"private-instance-method"
]
}
],
// 容许带一个参数的箭头函数省去括号
"arrow-parens": [
true,
"ban-single-arg-parens"
],
// 对齐方式
"align": [
true,
"parameters",
"statements",
"members",
"elements"
]
},
"rulesDirectory": []
}
复制代码
在vue-cli3 默承认以选择设置支持jsx
了,具体相关的配置在tsconfig.json
:node
{
"compilerOptions": {
"jsx": "preserve"
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
]
}
复制代码
typescript中数据类型有Boolean,Number,String,Array,Tuple,Enum,Any,Void,Null,Undefined,Never,以下图: jquery
let isDone: boolean = false
webpack
let color: string = "blue"
git
let decimal: number = 6;
es6
有两种方式:
//第一种:在元素类型后面用中括号([])来表示这种类型元素的数组
let list: number[] = [1, 2, 3];
//第二种:使用泛型建立类型数组,Array<elemType/ *数组元素的类型* />
let list: Array<number> = [1, 2, 3];
复制代码
any 类型,则容许被赋值为任意类型
let notSure: any = 4;
TypeScript增长了一个很实用的Enum类型。好比C#,枚举给了咱们更友好的名称(数字类型)来辨别数值集合
enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;
复制代码
能够用 void 表示没有任何返回值的函数, 也能够声明一个 void 类型的变量,不过只赋值为 undefined和null
function warnUser(): void {
console.log("This is my warning message");
}
复制代码
let u: undefined = undefined;
let n: null = null;
复制代码
undefined 类型的变量只能被赋值为undefined,null 类型的变量只能被赋值为 null。
与 void 的区别是,undefined 和 null 是全部类型的子类型, undefined和null 类型的变量,能够赋值给 number 类型的变量,而void则不行
define
)typescript
中函数定义有函数声明法、直接量式等,和es6
差很少,不过就多了参数类型的定义
function add(x: number, y: number): number {
return x + y;
}
复制代码
add
函数定义了参数x和y都是number
类型,返回值也是number
类型,若是传入参数或返回值不是number
类型,就会报错
以下:
var getInfo=function(name:string,age:number):string{
return `${name} --- ${age}`;
}
复制代码
使用void 标识
function run():void{
console.log('run')
}
复制代码
定义可选参数在传参数时添加?就行,传参时能够传也能够不传
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
let result1 = buildName("Bob");
复制代码
Default
)ts
默认参数与es6
默认参数定义同样
function getInfo(name:string,age:number=20):string{
if(age){
return `${name} --- ${age}`;
}else{
return `${name} ---年龄保密`;
}
}
复制代码
Rest
)就是使用拓展字符...,接收其他的参数
function sum(a:number,b:number,...result:number[]):number{
var sum=a+b;
for(var i=0;i<result.length;i++){
sum+=result[i];
}
return sum;
}
alert(sum(1,2,3,4,5,6)) ;
复制代码
与es5
中重复定义函数会形成替换不一样,typecript
中的重载是为同一个函数提供多个函数类型定义来达处处理不一样函数参数的状况,实现不一样的逻辑功能
function getInfo(name:string):string;
function getInfo(name:string,age:number):string;
function getInfo(name:any,age?:any):any{
if(age){
return '我叫:'+name+'个人年龄是'+age;
}else{
return '我叫:'+name;
}
}
alert(getInfo('zhangsan')) //我叫:zhangsan
复制代码
class)
class
定义时对属性和方法进行类型约束
class Person{
name:string; //属性 前面省略了public关键词
constructor(n:string){
//构造器
this.name=n;
}
run():void{
alert(this.name);
}
}
var p=new Person('张三');
p.run()
复制代码
继承和es6的写法同样,用extends
和super
继承父类
class Person{
name:string;
constructor(name:string){
this.name=name;
}
run():string{
return `${this.name}在运动`
}
}
class Web extends Person{
constructor(name:string){
super(name); /*初始化父类的构造函数*/
}
}
var w=new Web('李四');
alert(w.run());
复制代码
typescript
有三种修饰符,public
、protected
、private
,属性若是不加修饰符 默认就是 公有 (public
);以下:
class Person{
public name:string; /*公有属性*/
constructor(name:string){
this.name=name;
}
run():string{
return `${this.name}在运动`
}
}
var p=new Person('哈哈哈');
alert(p.name);
复制代码
protected:保护类型,在类里面、子类里面能够访问 ,在类外部无法访问
class Person{
protected name:string; /*保护类型*/
constructor(name:string){
this.name=name;
}
run():string{
return `${this.name}在运动`
}
}
var p=new Person('哈哈哈');
alert(p.name);
复制代码
类外部访问会报错
private :私有属性,在类里面能够访问,子类、类外部都无法访问
class Person{
private name:string; /*私有*/
constructor(name:string){
this.name=name;
}
run():string{
return `${this.name}在运动`
}
}
class Web extends Person{
constructor(name:string){
super(name)
}
work(){
console.log(`${this.name}在工做`)
}
}
复制代码
子类访问会报错
abstract
)typescript中的抽象类使用abstract
定义,它是提供其余类继承的基类,不能直接被实例化。抽象类中abstract
定义的抽象方法不包含具体实现,但必须在派生类中实现。
首先使用abstract
定义抽象类Animal
和抽象方法eat
abstract class Animal {
public name: string;
constructor(name: string) {
this.name = name;
}
abstract eat(): any; //抽象方法不包含具体实现而且必须在派生类中实现。
run() {
console.log("其余方法能够不实现");
}
}
复制代码
而后在子类Dog
实现具体方法eat
,
class Dog extends Animal {
//抽象类的子类必须实现抽象类里面的抽象方法
constructor(name: any) {
super(name);
}
eat() {
console.log(this.name + "吃粮食");
}
}
var d = new Dog("小花花");
d.eat();
复制代码
若是没有定义eat
方法,就会报错;
Interfaces
)在 TypeScript 中,咱们可使用接口(Interfaces) 定义来对属性、函数、类、可索引数组或对象进行规范和限制
属性接口通常是对json对象类型的约束,定义以下:
interface FullName{
firstName:string; //注意;结束
readonly secondName:string;
optionalVal?:string;
}
function printName(name:FullName){
// 必须传入对象 firstName secondName
console.log(name.firstName+'--'+name.secondName);
}
var obj={ /*传入的参数必须包含 firstName secondName*/
age:20,
firstName:'张',
secondName:'三'
};
printName(obj);
复制代码
上面interface
定义了一个接口FullName ,接着定义了一个变量 obj传入printName中,它的键值必须具备 FullName中定义的firstName和secondName,且类型必须相同。这样,咱们就约束了obj 的数据类型必须和接口 FullName 一致.
其中readonly是只读的意思,不能从新赋值,与const的区别在于,const用于定义变量,readonly用于定义属性;
而定义时optionalVal后添加?号,表示这个是可选的属性,传入时无关紧要
函数类型接口定义时对传入参数和返回值进行约束。
// 加密的函数类型接口
interface encrypt{
(key:string,value:string):string;
}
var md5:encrypt=function(key:string,value:string):string{
//模拟操做
return key+value;
}
console.log(md5('name','zhangsan'));
复制代码
上面定义了encrypt
函数接口,规定了传入参数和返回值都是string
类型。
TypeScript
定义数组或对象接口时,索引签名必须是 string
或者 number
或symbols
//定义数组接口
interface UserArr{
[index:number]:string
}
var arr:UserArr=['aaa','bbb'];
console.log(arr[0]);
//定义对象接口
interface UserObj{
[index:string]:string
}
var arr:UserObj={name:'张三'};
复制代码
定义class
类型接口和抽象类差很少,不过是使用implements
来实现具体的派生类,implements
这个单词有器具、工具、落实、实现的意思。看看怎 样implements
:
//定义class类型
interface Animal {
name: string;
eat(str: string): void;
}
class Cat implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
eat(food: string) {
console.log(this.name + "吃" + food);
}
}
var c = new Cat("小花");
c.eat("什么猫砂?");
复制代码
Animal
有name
属性和eat
方法,Cat
实现Animal
这个类,一样必须有name
和eat
方法
接口的扩展至关于接口的继承,使用extends
进行继承。
interface Animal{
eat():void;
}
interface Person extends Animal{
work():void;
}
class Programmer{
public name:string;
constructor(name:string){
this.name=name;
}
coding(code:string){
console.log(this.name+code)
}
}
class WebDeveloper extends Programmer implements Person{
constructor(name:string){
super(name)
}
eat(){
console.log(this.name+'喜欢吃馒头')
}
work(){
console.log(this.name+'写代码');
}
}
var wo=new WebDeveloper('小陈');
wo.coding('写ts代码');
复制代码
上面代码先定义了Animal
类接口,而后Person
接口扩展了Animal
,Programmer
具体进行Person
实现,WebDeveloper
继承自Programmer
,WebDeveloper
中定义的方法eat
和work
都没有返回值,与Animal
和Person
中定义的类型一致。
定义函数时,若是传入参数类型和返回值不肯定时,用any
能够搞定,先看以下代码:
function getData(value:any):any{
return '哈哈哈';
}
复制代码
Tadashize!使用any
,意味着放弃了类型检查,传入的参数类型和返回的参数类型能够不一致,当咱们函数传入参数类型以及返回值类型不肯定,但又但愿传入参数类型以及返回值类型是相同的,那怎么办?因而泛型就登场了,泛型就是用来解决类、接口、方法的复用性、以及对不特定数据类型的支持的。
定义一个泛型函数
function getData<T>(value:T):T{
return value;
}
getData<number>(123);
getData<string>('这是一个泛型');
复制代码
其中T表示泛型,要是喜欢用A或V也行,getData
函数调用时,限定了参数类型是number
或string
。
定义一个泛型类
下面是求最小值的泛型类实现。
class MinClas<T>{
public list:T[]=[];
add(value:T):void{
this.list.push(value);
}
min():T{
var minNum=this.list[0];
for(var i=0;i<this.list.length;i++){
if(minNum>this.list[i]){
minNum=this.list[i];
}
}
return minNum;
}
}
/*实例化类 而且制定了类的T表明的类型是number*/
var m1=new MinClas<number>();
m1.add(11);
m1.add(3);
m1.add(2);
console.log(m1.min())
复制代码
定义一个泛型接口
interface ConfigFn{
<T>(value:T):T;
}
var getData:ConfigFn=function<T>(value:T):T{
return value;
}
getData<string>('张三');
复制代码
interface ConfigFn<T>{
(value:T):T;
}
function getData<T>(value:T):T{
return value;
}
var myGetData:ConfigFn<string>=getData;
myGetData('20');
复制代码
水了那么多,下面开始写点项目相关。。。
首先,新建文件,修改后缀名为 .ts ,而后完事。
//index.ts
import Vue from "vue";
import Router from "vue-router";
import routes from "./routes";
Vue.use(Router);
export default new Router({
mode: "hash",
base: process.env.BASE_URL,
routes
});
//routes.ts
export default [
{
path: "/xxxx",
name: "xxxx",
component: () => import("@/views/xxxxx.vue"),
meta: {
title: ""
}
}
]
复制代码
这实际上是vue-class-component
的坑点,在组件中使用beforeRouteEnter等路由钩子函数时,得先在main.ts
主入口文件进行注册
//main.ts
import Component from 'vue-class-component'
// Register the router hooks with their names
Component.registerHooks([
'beforeRouteEnter',
'beforeRouteLeave',
'beforeRouteUpdate' // for vue-router 2.2+
])
复制代码
而后在组件引入使用,
//其余component.vue
import Vue from 'vue'
import Component from 'vue-class-component'
@Component
class MyComp extends Vue {
beforeRouteEnter (to, from, next) {
console.log('beforeRouteEnter')
next()
}
beforeRouteLeave (to, from, next) {
console.log('beforeRouteLeave')
next()
}
}
复制代码
使用this.$router.push()
时,跳转不生效,得先在shims-vue.d.ts
文件中添加定义
import Vue from 'vue';
import VueRouter, {Route} from 'vue-router';
declare module "*.vue" {
export default Vue;
}
declare module 'vue/types/vue' {
interface Vue {
$router: VueRouter; // 这表示this下有这个东西
$route: Route;
}
}
//而后就可使用啦
复制代码
而后聊点其余吧,天天水一水,总有一天升10级。。。
长话短说,hash模式经过监听window.onhashchange事件来监测url变化
if (!window.HashChangeEvent)(function(){
var lastURL = document.URL;
window.addEventListener("hashchange", function(event){
Object.defineProperty(event, "oldURL",
{enumerable:true,configurable:true,value:lastURL});
Object.defineProperty(event, "newURL",
{enumerable:true,configurable:true,value:document.URL});
lastURL = document.URL;
});
}());
复制代码
hashchange事件参数有两个属性newURL(跳转后的路径url)和oldURL (跳转前的路径url)
history模式,是调用history的 pushState() 和 replaceState()修改路径url
//pushState
let stateObj = {
foo: "bar",
};
history.pushState(stateObj, "page 2", "bar.html");
//replaceState
history.replaceState(stateObj, "page 3", "bar2.html");
复制代码
不就是按需加载嘛......通常有三种,just show the code:
//自动将你的构建代码切割成多个包
{
path: '/demo',
name: 'Demo',
component: resolve =>
require(['../components/Demo'], resolve)
}
复制代码
这会把多个路由指定相同的chunkName,会合并打包成一个js文件
{
path: '/demo',
name: 'Demo',
component: resolve => require.ensure([], () =>
resolve(require('../components/Demo')), 'demo')
},
{
path: '/hihihi',
name: 'hihihi',
// component: hihihi
component: resolve => require.ensure([], () =>
resolve(require('../components/hihihi')), 'demo')
}
复制代码
没错,这种实际上是第二种的升级版,建议使用
{
path: "/xxxx",
name: "xxxx",
component: () => import("@/views/xxxxx.vue"),
meta: {
title: ""
}
}
复制代码
vue-property-decorator
使用vue-property-decorator 是vue官方推荐的typescript支持库,依赖于vue-class-component, 在vue中可使用TypeScript装饰符
如图,它提供了7个装饰符和一个函数(Mixins),分别是:
@Emit ;//对应vue中的emit
@Inject ;//对应vue中的Inject
@Model ;//对应vue中的Model
@Prop ;//对应vue中的Prop
@Provide ;//对应vue中的Provide
@Watch ;//对应vue中的Watch
@Component ;//对应vue中的component
Mixins ;//对应vue中的mixins
复制代码
安装
npm i vue-class-component vue-property-decorator --save
复制代码
基础使用
@Component 使用
首先引入Component
import { Component, Vue } from "vue-property-decorator";
复制代码
而后注册组件
@Component({
components: {
your-component
}
})
复制代码
@Emit使用
//引入
import { Vue, Component, Emit } from 'vue-property-decorator'
//使用
@Component
export default class YourComponent extends Vue {
count = 0
@Emit()
addToCount(n: number) {
this.count += n
}
@Emit('reset')
resetCount() {
this.count = 0
}
@Emit()
returnValue() {
return 10
}
@Emit()
promise() {
return new Promise(resolve => {
setTimeout(() => {
resolve(20)
}, 0)
})
}
}
复制代码
上面会被对应编译成
export default {
data() {
return {
count: 0
}
},
methods: {
addToCount(n) {
this.count += n
this.$emit('add-to-count', n)
},
resetCount() {
this.count = 0
this.$emit('reset')
},
returnValue() {
this.$emit('return-value', 10)
},
promise() {
const promise = new Promise(resolve => {
setTimeout(() => {
resolve(20)
}, 0)
})
promise.then(value => {
this.$emit('promise', value)
})
}
}
}
复制代码
@Prop使用
这个也简单,先引入,而后在组件内,使用@prop
修饰符定义prop
,内在与原来的prop
是同样的,不过是写法上的花样不一样
//引入
import { Component, Vue, Prop } from
"vue-property-decorator";
//使用
@Component({})
export default class eventVideo extends Vue {
// 定义字符串类型
@Prop(String) public positionleft!: string;
@Prop(String) public positiontop!: string;
//定义Number类型
@Prop(Number) outerHeight!: number;
//定义数组类型
@Prop() public colorArr: Array<any>;
}
复制代码
或者定义时使用interface
,能够这样写:
// define interface eventDetailArr
interface eventDetailArr {
[index: number]: any;
}
@Component({})
export default class eventDetail extends Vue {
@Prop() eventDetailData!: eventDetailArr[];
}
复制代码
@Inject与@Provide
在vue
组件传值的时候,有prop
经常使用用于父子组件传值的形式,有ref
等直接操做简单粗暴的形式,有event-bus
的emit
传值的形式,有vuex
状态管理的形式,对于层级嵌套不是不少的时候,使用prop
和emit
挺方便,可是对于太太爷爷辈传话,一辈一辈传,混乱且麻烦,又不想用vuex
这种传家宝,那就能够用inject
和provide
,既保证基因代代相传,又使用简洁便捷
官方文档表示,
provide
传入的是对象或返回对象函数,inject
则能够多样
inject
传入的值是对象,默认有
from
和
default
属性,
from
表示其源属性,若是你
provide
的属性和
inject
的不是同一个名字的话。
default
表示默认值。
//太爷爷辈.vue--provide
<script lang="tsx">
import { Component, Inject, Provide, Vue } from
"vue-property-decorator";
@Component({})
export default class 太爷爷 extends Vue {
//提供基因
// Provide weather components position
@Provide("weather__position_left")
weather__position_left = "57%";
@Provide("weather__position__top")
weather__position__top = "40px";
}
</script>
//十八世孙
//weatherInfo.vue inject
<script lang="tsx">
import { Component, Vue, Prop, Inject, Provide } from
"vue-property-decorator";
@Component({
components: {}
})
export default class weatherinfo extends Vue {
//遗传基因
// Inject weather components position
@Inject("weather__position_left") readonly
weather__position_left!: string;
@Inject("weather__position__top") readonly
weather__position__top!: string;
async mounted() {
this.$nextTick(() => {
console.log(this.weather__position_left,
this.weather__position__top)
});
}
}
</script>
复制代码
最后舒适提醒:
provide
和inject
在咱们日常的项目并不推荐用哦,想一想顶层组件provide
了值,要是子组件多级复用,想改变,就一改全改,形成污染了,其实在基础组件库才适合使用,哈哈
@Watch
引入:
import { Component, Vue, Prop, Watch } from "vue-property-decorator";
复制代码
使用:
watch: {
'stationData': [
{
handler: 'onStationDataChange',
immediate: true,
deep: true
}
]
},
methods: {
onStationDataChange(val, oldVal) { },
}
复制代码
handler
是处理事件,immediate
属性设置为true
会让stationData
在初始定义时就执行handler
方法,deep
设置为true
会进行对象深度层级的监听,给对象全部属性添加监听器,一般这样开销就会增大了,因此替代方法是直接监听对象的属性,而不用设置deep:true
.
vuex-class
库,对
vuex
进行改造,目录结构以下:
index.ts
定义和导出vuex
import Vue from "vue";
import Vuex, { Store } from "vuex";
import actions from "./actions";
import mutations from "./mutations";
import state from "./state";
import getters from "./getters";
// modules
Vue.use(Vuex);
const store: Store<any> = new Vuex.Store({
actions,
mutations,
getters,
state,
modules: {
//添加自定义模块
}
});
export default store;
复制代码
types
经过定义一个接口RootStateTypes
,在RootStateTypes
中定义types
//types.ts
export interface RootStateTypes {
eventItem: object;
......
}
复制代码
state
首先引入RootStateTypes
,而后再定义state
,state
的类型与RootStateTypes
定义的类型约束一致
//state.ts
import { RootStateTypes } from "./types";
const state: RootStateTypes = {
eventItem: {}
};
export default state;
复制代码
getters
//getters.ts
import state from "./state";
import { RootStateTypes } from "./types";
import { GetterTree } from "vuex";
const getters: GetterTree<RootStateTypes, any> = {
eventItem: (state: RootStateTypes) => state.eventItem
};
export default getters;
复制代码
定义getters
时,同时引入泛型接口GetterTree
进行约束,getters
中使用的state
与RootStateTypes
类型保持一致,GetterTree
在vuex/types/index.d.ts
中定义以下:
mutations
//mutations.ts
import state from "./state";
import { RootStateTypes } from "./types";
import { MutationTree } from "vuex";
const mutations: MutationTree<RootStateTypes> = {
CLICK_EVENT(state: RootStateTypes, data: object) {
state.eventItem = data;
}
};
export default mutations;
复制代码
定义mutations
时,同时引入泛型接口MutationTree
进行约束,mutations
中使用的state
与RootStateTypes
类型保持一致,MutationTree
在vuex/types/index.d.ts
中定义以下:
actions
//actions.ts
import state from "./state";
import { RootStateTypes } from "./types";
import { ActionTree } from "vuex";
import { $important } from "@/api/importantData";
const actions: ActionTree<RootStateTypes, any> = {
// set default event
async SET_EVENT_ASYN({ commit, state: RootStateTypes }) {
let eventData = null;
await $important.getIngEventList().then((accidentIcon: any) => {
if (accidentIcon.length > 0) {
eventData = {
...accidentIcon[0],
type: accidentIcon[0].eventtypeName.includes("计划")
? "construction"
: "accident"
};
}
});
commit("CLICK_EVENT", eventData);
}
};
export default actions;
复制代码
定义actions
时,同时引入泛型接口ActionTree
进行约束,actions
中使用的state
与RootStateTypes
类型保持一致,ActionTree
在vuex/types/index.d.ts
中定义以下:
引入使用
使用@State
,@Getter
,@Action
,@Mutation
分别引入State
,Getter
,Action
,Mutation
,就是在前面添加@
修饰符就行了
//someComponent.ts
import Vue from 'vue'
import Component from 'vue-class-component'
import {
State,
Getter,
Action,
Mutation,
namespace
} from 'vuex-class'
export default class eventDetail extends Vue {
@State eventItem
@Getter eventItem
@Action SET_EVENT_ASYN
@Mutation CLICK_EVENT
created () {
this.eventItem // -> store.state.eventItem
this.eventItem // -> store.state.eventItem
this.SET_EVENT_ASYN({ value: obj }) // -> store.dispatch('SET_EVENT_ASYN', { value: obj })
this.CLICK_EVENT({ value: true }) // -> store.commit('CLICK_EVENT', { value: obj })
}
}
复制代码
TSX
其实就是JSX
语法结合typescript
一块儿使用,在vue
中使用jsx
render函数的使用
先看代码:
<script lang="tsx">
import { Component, Vue, Prop, Inject, Provide } from "vue-property-decorator";
@Component({
components: {}
})
export default class weatherinfo extends Vue {
// Inject weather components position
@Inject("weather__position_left") readonly weather__position_left!: string;
@Inject("weather__position__top") readonly weather__position__top!: string;
//weather information
WeatherInfo = null;
//weather refresh interval
weatherTimer = null;
async mounted() {
this.$nextTick(() => {
});
}
render(){
const weatherAttrs = {
style:`left:${this.weather__position_left};top:${this.weather__position__top}`,
class:`weather__info__container flex`
}
const weatherNode= this.WeatherInfo?
(<div {...{attrs:weatherAttrs}}>
<div class="flex flex-ver weather__info__item flex-between" >
<span>{this.WeatherInfo.realTime.weather}</span>
<div>
<img class="weather__icon"
src={require('@/assets/images/mapIcon/temperature.png')} />
<span>{this.WeatherInfo.realTime.temperature}℃</span>
</div>
</div>
<div class="flex flex-ver weather__info__item ">
<img class="weather__icon"
src={require('@/assets/images/mapIcon/humidity.png')}/>
<span>{this.WeatherInfo.realTime.humidity}</span>
</div>
</div>):''
return weatherNode
}
}
</script>
复制代码
使用时,在render
函数中返回html
内容,绑定数据的语法和vue
有点相似,又不一样
值的绑定
使用{}
进行值的绑定
render() {
return (<span>{this.WeatherInfo.realTime.humidity}</span>)
}
复制代码
标签属性的绑定
标签属性的绑定能够在标签上直接用{}
进行绑定
render() {
return (<img class="weather__icon"
src={require('@/assets/images/mapIcon/temperature.png')} />)
}
复制代码
也能够进行定义对象存储属性,而后经过...
解构展开属性,
render(){
const weatherAttrs = {
style:`left:${this.weather__position_left};top:${this.weather__position__top}`,
class:`weather__info__container flex`
}
return (<div {...{attrs:weatherAttrs}}></div>)
}
复制代码
这个属性定义,能够参考vue
官方罗列的属性
{
class: {
foo: true,
bar: false
},
style: {
color: 'red',
fontSize: '14px'
},
attrs: {
id: 'foo'
},
props: {
myProp: 'bar'
},
domProps: {
innerHTML: 'baz'
},
on: {
click: this.clickHandler
},
nativeOn: {
click: this.nativeClickHandler
},
directives: [
{
name: 'my-custom-directive',
value: '2',
expression: '1 + 1',
arg: 'foo',
modifiers: {
bar: true
}
}
],
scopedSlots: {
default: props => createElement('span', props.text)
},
key: 'myKey',
ref: 'myRef',
refInFor: true
}
复制代码
事件的绑定
标签的事件绑定可使用on-click
这种形式,也可使用onCliCK
,若是是定义组件的事件,要用nativeOnClick
,以下:
render (h) {
return (
<div
id="foo"
domPropsInnerHTML="bar"
onClick={this.clickHandler}
nativeOnClick={this.nativeClickHandler}
class={{ foo: true, bar: false }}
style={{ color: 'red', fontSize: '14px' }}
key="key"
ref="ref"
refInFor
slot="slot">
</div>
)
}
复制代码
要传参数的话,用这种onClick={()=>{this.clickHandler(value)}}
形式
return(
<ul class="table__content__item flex flex-between flex-ver" on-click={(e: any) =>
this.$emit('iconclick',element)}>
<li><img src={element.icon}/></li>
<li class="flex flex-center">
<span class="amount">{element.amount}</span>
<span>{element.title}</span>
</li>
</ul>
)
复制代码
条件渲染
像v-if
这样的指令,能够改写成三元条件判断就行
const weatherNode=(this.WeatherInfo?(
<div class="flex flex-ver weather__info__item flex-between" >
<div class="flex flex-ver flex-around">
<img class="weather__icon" src={weatherIcon}/>
<span>{this.WeatherInfo["realTime"]["temperature"]}℃</span>
</div>
<div class="flex flex-center">
<span>{this.WeatherInfo["realTime"]["city"]}</span>
<span>{this.WeatherInfo["realTime"].weather}</span>
</div>
</div>):'')
return weatherNode
复制代码
循环渲染
v-for
这样的循环渲染能够经过遍历数组方法map
或forEach
或其余来实现
render(){
const nodeAttrs = {
style:`left:${this.positionleft};top:${this.positiontop}`,
class:`list__container`
}
const renderNode=
(<div {...{attrs:nodeAttrs}}>
{/**组件能够直接渲染*/}
<panneltitle headerText="xxx展现"></panneltitle>
<div class="container flex flex-wrap flex-around">
{
Object.values(this.tableData).map(element => {
return(
<ul class="table__content__item flex flex-between flex-ver"
on-click={(e: any) =>
this.$emit('iconclick',element)}>
<li><img src={element.icon}/></li>
<li class="flex flex-center">
<span class="amount">
{element.amount}
</span>
<span>{element.title}</span>
</li>
</ul>
)
})
}
</div>
</div>)
return renderNode
}
复制代码
eventBus
eventBus
在跨组件通讯时比较经常使用,不像用vuex
那么繁琐,本质是实例化另外一个vue
,而后使用$emit
和$on
发布和监听事件
//在eventBus.ts中定义
import Vue from 'vue'
export default new Vue()
复制代码
comp.vue发布:
//引入
import eventBus from "eventBus.ts"
//使用
eventBus.$emit("topic",payload)
复制代码
otherComp.vue订阅:
//引入
import eventBus from "eventBus.ts"
//使用
eventBus.$on("topic",payload=>{
console.log(payload+"do some 骚操做")
})
复制代码
其实这不少人都用过,只是这里说一个坑点(当时没意识到),
eventBus
是全局实例,在组件中使用时,即便组件销毁了,它依然会监听,就致使有些冗余开销和订阅,本来只想在当前组件有效的时候才监听,结果多处接收到信息,这就很恼人了。因此记得在组件销毁前off
解除订阅。
beforeDestroy(){
eventBus.$off("topic")
}
复制代码
若是以为每次监听,又得解除,这样操做很麻烦,网上找了其余大神的方法,有兴趣能够看看。
构造组件
vue中能够经过扩展实例的方法进行高阶组件的构建,好比button、pannel等,彻底能够构建一套本身的基础组件。
pannel.tsx
中进行组件基本内容定义:/**scss */
import "./pannel.scss";
/**components */
import {
Component,
Vue,
Prop,
Inject,
Provide,
Watch,
Emit
} from "vue-property-decorator";
@Component({
components: {}
})
export default class pannel extends Vue {
/**prop */
@Prop() pannelStyle!: string;
@Prop() pannelClass!: string;
@Prop() content!: string;
/**property */
/**show or not */
private visible: boolean = false;
/**emit */
/**close pannel */
@Emit("close")
private handleClose(e) {
window.event ? (window.event.cancelBubble = true) : e.stopPropagation();
this.visible = false;
}
/**transition enter*/
@Emit("enter")
private afterEnter() {
console.log("enter");
}
/**transition leave*/
@Emit("closed")
private afterLeave() {}
/**mounted */
private async mounted() {
this.$nextTick(() => {});
}
/**render */
private render() {
const nodeAttrs = {
style: `${this.pannelStyle}`,
class: `pannel__container ${this.pannelClass}`
};
const renderNode = this.visible ? (
<transition name="fade" on-after-leave={this.afterLeave} on-enter={this.afterEnter} > <div {...{ attrs: nodeAttrs }}> <a class="close__btn" on-click={this.handleClose}> × </a> {this.content} </div> </transition>
) : (
""
);
return renderNode;
}
}
复制代码
pannel.scss
样式以下
.pannel__container{
background: #040f20;
opacity: 0.8;
padding: 20px;
width:580px;
height: 320px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-100%,-100%);
z-index: 2;
.close__btn{
color: #e9e9e9;
position: absolute;
top: 10px;
right: 10px;
display: block;
font-size: 25px;
width: 38px;
height: 38px;
border-radius: 50%;
background: #d34242;
font-style: normal;
line-height: 38px;
text-align: center;
text-transform: none;
text-rendering: auto;
}
}
复制代码
pannelConstructor.ts
使用Vue.extend
进行实例扩展import Vue from "vue";
import pannel from "./pannel";
// 扩展实例构造器
const PannelConstructor = Vue.extend(pannel);
const pannelInstances = [];
let initID = 1;
/**
* remove Pannel instance(移除实例)
* @param {vm} instance Pannel instance
*/
const removeInstance = (instance: any) => {
if (!instance) return;
let length = pannelInstances.length;
let index = pannelInstances.findIndex(
instanceItem => instance.id === instanceItem.id
);
pannelInstances.splice(index, 1);
length = index = null;
};
/**
*实例化组件
* @param options pannel options :Style,Class,slot
*/
const showPannel = (options: any) => {
const { ...rest } = options;
const instance: any = new PannelConstructor({
propsData: {
...rest
}
});
const id = `pannel__${initID++}`;
instance.id = id;
instance.vm = instance.$mount();
document.body.appendChild(instance.vm.$el);
instance.vm.visible = true;
pannelInstances.push(instance);
instance.vm.$on("closed", () => {
removeInstance(instance);
document.body.removeChild(instance.vm.$el);
instance.vm.$destroy();
});
instance.vm.$on("close", () => {
instance.vm.visible = false;
});
return instance.vm;
};
export default showPannel;
复制代码
index.ts
中注册组件与挂载到Vue.prototype
原型上//@ts-ignore
import pannel from "./pannel";
import showPannel from "./pannelConstructor";
export default (Vue: any) => {
Vue.component(pannel.name, pannel);
Vue.prototype.$showPannel = showPannel;
};
复制代码
main.ts
中引入,以插件形式全局注册import Pannel from "@/common/pannel";
Vue.use(Pannel);
复制代码
this
调用或经过标签使用this.specialPannel = this.$showPannel({
pannelStyle:
"width: 960px;height: 540px;transform: translate(-100%,-50%);"
});
复制代码
就这样!!这里对
typescript
的一些基础使用、vue使用
的一些注意点进行了简单总结介绍,尝试了二者的磨合,整体而言,还算顺利,不过对typescript
的使用仍是比较浅显,在摸索的过程当中看了下ant-design
等开源库的源码,发现我的对typescript
的使用简直是小巫见大巫(这个词用得对不对??),还有很长一段路要努力哎,六月份据说vue3.0
要来了,听说会提供更好的typescript
支持,仍是比较期待ing。。。最后感谢一块儿敲代码成长的朋友们,感谢众多大神们的技术分享啦,在掘金水了那么久,那些分享帖子着实在开发过程帮了很大忙啊!!!!