據說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。。。最後感謝一塊兒敲代碼成長的朋友們,感謝衆多大神們的技術分享啦,在掘金水了那麼久,那些分享帖子着實在開發過程幫了很大忙啊!!!!