vue2+ts+vue-property-decorator

前言

其實看 Vue2 的官方文檔惠桃,對 Typescript 的支持還是相對比較少的,有兩種方式定義組件

  • 使用 Vue.componentVue.extend 定義Vue組件
  • 使用 vue-class-component 裝飾器來定義基于類的Vue組件

其實市面上還有一種實現(xiàn)方式辖试,那就是由社區(qū)基于 vue-class-component 裝飾器的二次封裝 vue-property-decorator, 它也是今天我們要分享主要內容

npm vue-property-decorator 文檔地址

搭建 webpack5 + Vue2 + Ts

安裝 node, vue-cli, webpack ts 這些在這里我就不過多描述了辜王,不懂的小伙伴可以看我前幾篇文章,有關于環(huán)境搭建的罐孝,那搭建項目也比較簡單呐馆,參考 vue-cli 搭建項目

進入需要創(chuàng)建工程的目錄,cmd 執(zhí)行命令

vue create vue-ts-demo 
復制代碼

回車莲兢,我們選擇第三個自定義選項

image.png

然后汹来,根據自己的選擇來勾選,記住勾選 typescript 改艇,如下是我都選的

image.png

回車之后收班,選擇 2.x 因為我們分享的是 vue 2.x + ts

image.png

緊跟著是一些里的描述于配置,yes/no 自己決定谒兄,當然也可以一系列 yes 到底摔桦,然后就到了這里

image.png

很熟悉,讓我們進入工程目錄承疲,然后啟動項目邻耕,接下來我們可以簡單看一下此時的工程目錄結構,目錄結構大多數(shù)都是很熟悉的東西燕鸽,就是有一點小小的改變

image.png
  • 添加了 tsconfig.json (ts 配置文件)
  • shims-tsx.d.ts (支持 tsx)
  • shims-vue.d.ts (支持vue使用 ts)

其它的不同就是打開文件后發(fā)現(xiàn) vue 都是用 ts 編寫的兄世,拿一個 Home.vue 展示一下吧

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src

@Component({
  components: {
    HelloWorld,
  },
})
export default class Home extends Vue {}
</script>

復制代碼

看起來很不一樣哈,跟我們之前寫的 vue 模板語法都不一樣啊研,不要著急御滩,我們今天就是來分享vue2 如何使用 ts 的,好了悲伶,進入整體艾恼,我們直接開始擼代碼

如何編寫組件

這里呢住涉,我不動目前所有的工程結構麸锉,直接拿 Aboud.vue 組件來演示如何使用 vue-property-deractor 來創(chuàng)建基于類的 vue 組件

@Component 創(chuàng)建組件

@Component 裝飾器可以接收一個對象作為參數(shù),可以在對象中聲明 components 舆声,filters花沉,directives等未提供裝飾器的選項柳爽,(下文會做一些演示)

基礎模板語法

<template>
  <div class="about">
    <h1>我是about.vue組件</h1>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from "vue-property-decorator";

@Component
export default class about extends Vue {}
</script>

復制代碼

@Prop 接收參數(shù)

@Prop裝飾器接收一個參數(shù),常常這樣用

  • @Prop('String') : 指定 prop 的類型,字符串 String碱屁,Number磷脯,Boolean
  • @Prop({ default: 1, type: Number }) : 對象,指定默認值 {default:'1'}
  • @Prop([String,Number]) : 數(shù)組娩脾,指定 prop 的可選類型 [String, Boolean]
// 父組件:
<template>
  <div class="Props">
    <Child :name="name" :age="age" :sex="sex"></Child>
  </div>
</template>
 
<script lang="ts">
import {Component, Vue,} from 'vue-property-decorator';
import Child from './Child.vue';
 
@Component({
  components: {Child}, // 上邊有說 @Component 可接受的參數(shù)
})
export default class PropsPage extends Vue {
  private name = 'Hs';
  private age = 18;
  private sex = 1;
}
</script>
 
// 子組件:
<template>
  <div>
    name: {{name}} | age: {{age}} | sex: {{sex}}
  </div>
</template>
 
<script lang="ts">
import {Component, Vue, Prop} from 'vue-property-decorator';
 
@Component
export default class Child extends Vue {
   @Prop(String) readonly name!: string | undefined;
   @Prop({ default: 20, type: Number }) private age!: number;
   @Prop([String, Number]) private sex!: string | number;
}
</script>
復制代碼

@Propsync 不一樣的@Prop

@PropSync裝飾器與@prop用法基本類似赵誓,只是多了一個參數(shù), 父組件在傳遞的時候需要配合 .sync

  • 第一個參數(shù)是父組件傳遞過來的屬性名
  • 第二個參數(shù)與@Prop的第一個參數(shù)一樣
  • @PropSync 會生成一個新的計算屬性 , 可逆向修改父組件傳遞過來的屬性,父組件會同步修改
// 父組件:
<template>
  <div class="Props">
    <Child :name.sync="name"></Child>
  </div>
</template>
 
<script lang="ts">
import {Component, Vue,} from 'vue-property-decorator';
import Child from './Child.vue';
 
@Component({
  components: { Child }, // 上邊有說 @Component 可接受的參數(shù)
})
export default class PropsPage extends Vue {
  private name = 'Hs';
}
</script>
 
// 子組件:
<template>
  <div>
    name: {{name_copy}}
    <button @click="setProp">修改prop</button>
  </div>
</template>
 
<script lang="ts">
import {Component, Vue, PropSync} from 'vue-property-decorator';
 
@Component
export default class Child extends Vue {
   @PropSync("name",String) name_copy!: string | undefined;
   setProp(){
       this.name_copy = "abcd" // 父組件會同步修改
   }
}
</script>
復制代碼

@Watch 監(jiān)聽

@Watch 裝飾器接收兩個參數(shù):

  • 被監(jiān)聽的屬性名
  • 可選屬性: {immediate?:boolean 監(jiān)聽開始之后是否立即調用該回調函數(shù),deep?:boolean 是否深度監(jiān)聽}
<template>
  <div class="about">
    <h3> {{age}}</h3>
  </div>
</template>

<script lang="ts">
import { Component, Vue, Watch } from "vue-property-decorator";

@Component
export default class About extends Vue {
  private age = 18;

  @Watch("age")
  // 可選參數(shù) @Watch('age', {immediate: true, deep: true})
  onChangeAge(v: number, o: number): void {}

}
</script>

復制代碼

@Emit 廣播事件

@Emit 裝飾器接收一個可選參數(shù)柿赊,廣播事件名俩功,如果沒有定義這個參數(shù),則是以回調方法名為廣播事件名

  • 回調函數(shù)的返回值默認為第二個參數(shù)碰声,如果返回是 promise 诡蜓,則會默認為 resolve 之后觸發(fā)回調
// 父組件
<template>
  <div class="about">
    <ChildComp @childEmit="chileEmit" />
  </div>
</template>

<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import ChildComp from "./Child.vue";

@Component
export default class About extends Vue {
  chileEmit(n: string): void {
    console.log("子組件 emit 觸發(fā),參數(shù):", n);
  }
}
</script>

//子組件
<template>
  <div>
    <h3>我是子組件</h3>
    <button @click="customClickName"> @emit age+1 </button>
  </div>
</template>

<script lang="ts">
import { Component, Emit, Vue } from "vue-property-decorator";

@Component
export default class Child extends Vue {
  @Emit("childEmit")
  customClickName(): string {
    return "hs"
  }
}
</script>

復制代碼

@ref 句柄

@Ref 裝飾器接收一個可選參數(shù)胰挑,用來指向元素或子組件的引用信息蔓罚,即 ref="這個值"

<template>
  <div class="about">
    <button @click="getRef"
            ref="child_btn">age++</button>
    <hr>
    <ChildComp :name="name"
               :age="age"
               ref="child_c"
               @childEmit="chileEmit" />
  </div>
</template>

<script lang="ts">
import { Component, Provide, Ref, Vue, Watch } from "vue-property-decorator";
import ChildComp from "./Child.vue";

@Component({
  components: { ChildComp }
})
export default class About extends Vue {
  @Ref("child_c") readonly child_comp!: ChildComp;
  @Ref("child_btn") readonly child_btn_dom!: HTMLButtonElement;
  getRef() {
    console.log(this.child_comp, this.child_btn_dom);
  }
}
</script>

復制代碼

@Provide / @Inject 和 @ProvideReactive / @InjectReactive`

提供/注入裝飾器,key可以為string或者symbol類型,使用方式都一樣

  • 相同: Provide/ProvideReactive提供的數(shù)據,在子組件內部使用Inject/InjectReactive都可取到
  • 不同: ProvideReactive 的值被父組件修改,子組件可以使用 InjectReactive 捕獲
// 頂層組件
<template>
  <div class="about">
    <ChildComp />
  </div>
</template>

<script lang="ts">
import { Component, Provide瞻颂,Vue } from "vue-property-decorator";
import ChildComp from "./Child.vue";

@Component({
  components: { ChildComp },
})
export default class About extends Vue {
  @Provide("provide_value") private p = "from provide";
}
</script>

// 子組件
<template>
  <div>
    <h3>我是子組件</h3>
    <h3>provide/inject 跨級傳參 {{provide_value}}</h3>

  </div>
</template>

<script lang="ts">
import { Component, Inject, Vue } from "vue-property-decorator";

@Component
export default class Child extends Vue {
  @Inject() readonly provide_value!: string;
}
</script>

復制代碼

如何使用 tsx

其實 tsx 用起來會讓我們有 react 的感覺豺谈,寫過 react 的都知道是使用 jsx javascript + xml, 那么 tsx 基本上跟 jsx 差不多,等同于 typescript + xml蘸朋,用一個實例來體現(xiàn)一下 現(xiàn)有的工程不變核无,我們搭建環(huán)境的時候已經支持了 tsx

創(chuàng)建一個 demo.tsx ,鍵入如下簡單內容藕坯,大致看下來团南,基本上跟我們上邊分享的類組件一樣,唯一有一點不一樣的就是模板變成 render 函數(shù)炼彪,這樣可以讓我們更加靈活吐根。

import { Component, Emit, Prop, PropSync, Vue, Watch } from "vue-property-decorator";

@Component
export default class Demo extends Vue {

  public name = "Hs"
  public str = "hello tsx"
  public data = [1, 2, 3, 4]

  // Prop
  @Prop() demo_name!: string
  @Prop(Number) demo_age!: number

  // Propsync
  @PropSync("propsync", Number) propsync_copy!: number | undefined

  // Computed
  get _age(): number {
    this.str = this.str + "-x"
    return this.demo_age * 10
  }

  //watch
  @Watch("str")
  onhangeStr(v: string, o: string) {
    console.log(v, o)
  }

  //emit
  @Emit("tsx_emit")
  clickEvent() { return "params 123" }

  // 渲染函數(shù)    
  render() {
    return (
      <div>
        <h2>data屬性: {this.name}-{this.str}</h2>
        <h2>prop: {this.demo_name}</h2>
        <h2>計算屬性: {this._age}</h2>
        <h2>prop-sync: {this.propsync_copy}</h2>
        <h2>遍歷</h2>
        {
          this.data.map(c => {
            return <span>{c} - </span>
          })
        }
        <button onClick={() => this.clickEvent()}>emit</button>
      </div>
    )
  }

}

復制代碼

ok,tsx 的使用基本上跟 ts 差不多辐马,這里都是簡單的例子和使用方式拷橘,便于理解和學習,更重要的是如何學習好 ts, 它真的很槍手喜爷。

貼出我的package.json 文件冗疮,僅供版本差異的參考:

{
  "name": "vue2-ts-demo2",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "vue": "^2.6.11",
    "vue-class-component": "^7.2.3",
    "vue-property-decorator": "^9.1.2",
    "vue-router": "^3.2.0",
    "vuex": "^3.4.0"
  },
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^4.18.0",
    "@typescript-eslint/parser": "^4.18.0",
    "@vue/cli-plugin-eslint": "^4.0.5",
    "@vue/cli-plugin-router": "^4.0.5",
    "@vue/cli-plugin-typescript": "^4.0.5",
    "@vue/cli-plugin-vuex": "^4.0.5",
    "@vue/cli-service": "^4.0.5",
    "@vue/eslint-config-prettier": "^6.0.0",
    "@vue/eslint-config-typescript": "^7.0.0",
    "eslint": "^6.7.2",
    "eslint-plugin-prettier": "^3.3.1",
    "eslint-plugin-vue": "^6.2.2",
    "node-sass": "^4.12.0",
    "prettier": "^2.2.1",
    "sass-loader": "^8.0.2",
    "typescript": "~4.1.5",
    "vue-template-compiler": "^2.6.11"
  },
  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },
    "extends": [
      "plugin:vue/essential",
      "eslint:recommended",
      "@vue/typescript/recommended",
      "@vue/prettier",
      "@vue/prettier/@typescript-eslint"
    ],
    "parserOptions": {
      "ecmaVersion": 2020
    },
    "rules": {}
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
  ]
}

參考鏈接:資料

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市檩帐,隨后出現(xiàn)的幾起案子术幔,更是在濱河造成了極大的恐慌,老刑警劉巖湃密,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诅挑,死亡現(xiàn)場離奇詭異四敞,居然都是意外死亡,警方通過查閱死者的電腦和手機拔妥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門忿危,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人没龙,你說我怎么就攤上這事铺厨。” “怎么了硬纤?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵努释,是天一觀的道長。 經常有香客問我咬摇,道長伐蒂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任肛鹏,我火速辦了婚禮逸邦,結果婚禮上,老公的妹妹穿的比我還像新娘在扰。我一直安慰自己缕减,他們只是感情好,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布芒珠。 她就那樣靜靜地躺著桥狡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪皱卓。 梳的紋絲不亂的頭發(fā)上裹芝,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機與錄音娜汁,去河邊找鬼嫂易。 笑死,一個胖子當著我的面吹牛掐禁,可吹牛的內容都是我干的怜械。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼傅事,長吁一口氣:“原來是場噩夢啊……” “哼枣宫!你這毒婦竟也來了吴旋?” 一聲冷哼從身側響起搏屑,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤踏堡,失蹤者是張志新(化名)和其女友劉穎燥翅,沒想到半個月后朽基,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了茴迁。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寄悯。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖堕义,靈堂內的尸體忽然破棺而出猜旬,到底是詐尸還是另有隱情,我是刑警寧澤倦卖,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布洒擦,位于F島的核電站,受9級特大地震影響怕膛,放射性物質發(fā)生泄漏熟嫩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一褐捻、第九天 我趴在偏房一處隱蔽的房頂上張望掸茅。 院中可真熱鬧,春花似錦柠逞、人聲如沸昧狮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逗鸣。三九已至,卻和暖如春绰精,著一層夾襖步出監(jiān)牢的瞬間撒璧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工笨使, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沪悲,地道東北人。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓阱表,卻偏偏與公主長得像殿如,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子最爬,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內容