關(guān)于Vue新搭檔TypeScript快速入門實踐
Vue官方從2.6.X版本開始就部分使用Ts重寫了。
我個人對更嚴(yán)格類型限制沒有積極的看法,畢竟各類轉(zhuǎn)類型的騷寫法寫習(xí)慣了。
然鵝最近的一個項目中,是TypeScript+ Vue,毛計喇,學(xué)之...…真香!
注意此篇標(biāo)題的“前”,本文旨在講Ts混入框架的使用,不講Class API
1. 使用官方腳手架構(gòu)建
npm install -g @vue/cli# ORyarn global add @vue/cli
新的Vue CLI工具允許開發(fā)者 使用 TypeScript 集成環(huán)境 創(chuàng)建新項目。
只需運(yùn)行vue create my-app。
然后,命令行會要求選擇預(yù)設(shè)。使用箭頭鍵選擇Manually select features。
接下來,只需確保選擇了TypeScript和Babel選項,如下圖:
完成此操作后,它會詢問你是否要使用class-style component syntax。
然后配置其余設(shè)置,使其看起來如下圖所示。
Vue CLI工具現(xiàn)在將安裝所有依賴項并設(shè)置項目。
接下來就跑項目
2. 項目目錄解析
通過tree指令查看目錄結(jié)構(gòu)后可發(fā)現(xiàn)其結(jié)構(gòu)和正常構(gòu)建的大有不同。
這里主要關(guān)注shims-tsx.d.ts和 shims-vue.d.ts兩個文件
兩句話概括:
shims-tsx.d.ts
允許你以.tsx結(jié)尾的文件,在Vue項目中編寫jsx代碼shims-vue.d.ts
主要用于 TypeScript 識別.vue 文件,Ts默認(rèn)并不支持導(dǎo)入 vue 文件,這個文件告訴ts 導(dǎo)入.vue 文件都按VueConstructor<Vue>處理。
此時我們打開親切的src/components/HelloWorld.vue,將會發(fā)現(xiàn)寫法已大有不同
<template> <div class="hello"> <h1>{{ msg }}</h1> <!-- 省略 --> </div> </template> <script lang="ts"> import { Component, Prop, Vue } from 'vue-property-decorator'; @Component export default class HelloWorld extends Vue { @Prop() private msg!: string; } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped></style>
至此,準(zhǔn)備開啟新的篇章 TypeScript極速入門 和 vue-property-decorator
3. TypeScript極速入門
3.1 基本類型和擴(kuò)展類型
Typescript與Javascript共享相同的基本類型,但有一些額外的類型。
- 元組 Tuple
- 枚舉 enum
- Any 與Void
1. 基本類型合集
// 數(shù)字,二、八、十六進(jìn)制都支持 let decLiteral: number = 6; let hexLiteral: number = 0xf00d; // 字符串,單雙引都行 let name: string = "bob"; let sentence: string = `Hello, my name is ${ name }. // 數(shù)組,第二種方式是使用數(shù)組泛型,Array<元素類型>: let list: number[] = [1, 2, 3]; let list: Array<number> = [1, 2, 3]; let u: undefined = undefined; let n: null = null;
2. 特殊類型
1. 元組 Tuple
想象 元組 作為有組織的數(shù)組,你需要以正確的順序預(yù)定義數(shù)據(jù)類型。
const messyArray = [' something', 2, true, undefined, null]; const tuple: [number, string, string] = [24, "Indrek" , "Lasn"]
如果不遵循 為元組 預(yù)設(shè)排序的索引規(guī)則,那么Typescript會警告。
? (tuple第一項應(yīng)為number類型)
2. 枚舉 enum
enum類型是對JavaScript標(biāo)準(zhǔn)數(shù)據(jù)類型的一個補(bǔ)充。 像C#等其它語言一樣,使用枚舉類型可以為一組數(shù)值賦予友好的名字。
// 默認(rèn)情況從0開始為元素編號,也可手動為1開始 enum Color {Red = 1, Green = 2, Blue = 4} let c: Color = Color.Green; let colorName: string = Color[2]; console.log(colorName); // 輸出'Green'因為上面代碼里它的值是2
另一個很好的例子是使用枚舉來存儲應(yīng)用程序狀態(tài)。
3. Void
在Typescript中,你必須在函數(shù)中定義返回類型。像這樣:
若沒有返回值,則會報錯:
我們可以將其返回值定義為void:
此時將無法 return
4. Any
emmm...就是什么類型都行,當(dāng)你無法確認(rèn)在處理什么類型時可以用這個。
但要慎重使用,用多了就失去使用Ts的意義。
let person: any = "前端勸退師" person = 25 person = true
主要應(yīng)用場景有:
- 接入第三方庫
- Ts菜逼前期都用
5. Never
用很粗淺的話來描述就是:"Never是你永遠(yuǎn)得不到的爸爸。"
具體的行為是:
throw new Error(message) return error("Something failed") while (true) {} // 存在無法達(dá)到的終點
3. 類型斷言
簡略的定義是:可以用來手動指定一個值的類型。
有兩種寫法,尖括號和as:
let someValue: any = "this is a string"; let strLength: number = (<string>someValue).length; let strLength: number = (someValue as string).length;
使用例子有:
當(dāng) TypeScript 不確定一個聯(lián)合類型的變量到底是哪個類型的時候,我們只能訪問此聯(lián)合類型的所有類型里共有的屬性或方法:
function getLength(something: string | number): number { return something.length; } // index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'. // Property 'length' does not exist on type 'number'.
如果你訪問長度將會報錯,而有時候,我們確實需要在還不確定類型的時候就訪問其中一個類型的屬性或方法,此時需要斷言才不會報錯:
function getLength(something: string | number): number { if ((<string>something).length) { return (<string>something).length; } else { return something.toString().length; } }
3.2 泛型:Generics
軟件工程的一個主要部分就是構(gòu)建組件,構(gòu)建的組件不僅需要具有明確的定義和統(tǒng)一的接口,同時也需要組件可復(fù)用。支持現(xiàn)有的數(shù)據(jù)類型和將來添加的數(shù)據(jù)類型的組件為大型軟件系統(tǒng)的開發(fā)過程提供很好的靈活性。
在C#和Java中,可以使用"泛型"來創(chuàng)建可復(fù)用的組件,并且組件可支持多種數(shù)據(jù)類型。這樣便可以讓用戶根據(jù)自己的數(shù)據(jù)類型來使用組件。
1. 泛型方法
在TypeScript里,聲明泛型方法有以下兩種方式:
function gen_func1<T>(arg: T): T { return arg; } // 或者 let gen_func2: <T>(arg: T) => T = function (arg) { return arg; }
調(diào)用方式也有兩種:
gen_func1<string>('Hello world'); gen_func2('Hello world'); // 第二種調(diào)用方式可省略類型參數(shù),因為編譯器會根據(jù)傳入?yún)?shù)來自動識別對應(yīng)的類型。
2. 泛型與Any
Ts 的特殊類型 Any 在具體使用時,可以代替任意類型,咋一看兩者好像沒啥區(qū)別,其實不然:
// 方法一:帶有any參數(shù)的方法 function any_func(arg: any): any { console.log(arg.length); return arg; } // 方法二:Array泛型方法 function array_func<T>(arg: Array<T>): Array<T> { console.log(arg.length); return arg; }
- 方法一,打印了arg參數(shù)的length屬性。因為any可以代替任意類型,所以該方法在傳入?yún)?shù)不是數(shù)組或者帶有l(wèi)ength屬性對象時,會拋出異常。
- 方法二,定義了參數(shù)類型是Array的泛型類型,肯定會有l(wèi)ength屬性,所以不會拋出異常。
3. 泛型類型
泛型接口:
interface Generics_interface<T> { (arg: T): T; } function func_demo<T>(arg: T): T { return arg; } let func1: Generics_interface<number> = func_demo; func1(123); // 正確類型的實際參數(shù) func1('123'); // 錯誤類型的實際參數(shù)
3.3 自定義類型:Interface vs Type alias
Interface
,國內(nèi)翻譯成接口。Type alias
,類型別名。
1. 相同點
都可以用來描述一個對象或函數(shù):
interface User { name: string age: number } type User = { name: string age: number }; interface SetUser { (name: string, age: number): void; } type SetUser = (name: string, age: number): void;
都允許拓展(extends):
interface 和 type 都可以拓展,并且兩者并不是相互獨立的,也就是說interface可以 extends type, type 也可以 extends interface 。 雖然效果差不多,但是兩者語法不同。
interface extends interface
interface Name {? ? name: string;? } interface User extends Name {? ? age: number;? }
type extends type
type Name = {? ? name: string;? } type User = Name & { age: number ?};
interface extends type
type Name = {? ? name: string;? } interface User extends Name {? ? age: number;? }
type extends interface
interface Name {? ? name: string;? } type User = Name & {? ? age: number;? }
2. 不同點
type 可以而 interface 不行
type 可以聲明基本類型別名,聯(lián)合類型,元組等類型
// 基本類型別名 type Name = string // 聯(lián)合類型 interface Dog { wong(); } interface Cat { miao(); } type Pet = Dog | Cat // 具體定義數(shù)組每個位置的類型 type PetList = [Dog, Pet]
type 語句中還可以使用 typeof獲取實例的 類型進(jìn)行賦值
// 當(dāng)你想獲取一個變量的類型時,使用 typeof let div = document.createElement('div'); type B = typeof div
其他騷操作
type StringOrNumber = string | number; type Text = string | { text: string }; type NameLookup = Dictionary<string, Person>; type Callback<T> = (data: T) => void; type Pair<T> = [T, T]; type Coordinates = Pair<number>; type Tree<T> = T | { left: Tree<T>, right: Tree<T> };
interface可以而 type不行
interface 能夠聲明合并
interface User { name: string age: number } interface User { sex: string } /* User 接口為 { name: string age: number sex: string } */
interface 有可選屬性和只讀屬性
可選屬性
接口里的屬性不全都是必需的。 有些是只在某些條件下存在,或者根本不存在。 例如給函數(shù)傳入的參數(shù)對象中只有部分屬性賦值了。帶有可選屬性的接口與普通的接口定義差不多,只是在可選屬性名字定義的后面加一個?符號。如下所示
interface Person { name: string; age?: number; gender?: number; }
只讀屬性
顧名思義就是這個屬性是不可寫的,對象屬性只能在對象剛剛創(chuàng)建的時候修改其值。 你可以在屬性名前用 readonly來指定只讀屬性,如下所示:
interface User { readonly loginName: string; password: string; }
上面的例子說明,當(dāng)完成User對象的初始化后loginName就不可以修改了。
3.4 實現(xiàn)與繼承:implements vs extends
extends
很明顯就是ES6里面的類繼承,那么implement又是做什么的呢?它和extends有什么不同?implement
,實現(xiàn)。與C#或Java里接口的基本作用一樣,TypeScript也能夠用它來明確的強(qiáng)制一個類去符合某種契約
implement基本用法:
interface IDeveloper { name: string; age?: number; } // OK class dev implements IDeveloper { name = 'Alex'; age = 20; } // OK class dev2 implements IDeveloper { name = 'Alex'; } // Error class dev3 implements IDeveloper { name = 'Alex'; age = '9'; }
而extends是繼承父類,兩者其實可以混著用:
class A extends B implements C,D,E
搭配 interface和type的用法有:
3.5 聲明文件與命名空間:declare 和 namespace
前面我們講到Vue項目中的shims-tsx.d.ts和shims-vue.d.ts,其初始內(nèi)容是這樣的:
// shims-tsx.d.ts import Vue, { VNode } from 'vue'; declare global { namespace JSX { // tslint:disable no-empty-interface interface Element extends VNode {} // tslint:disable no-empty-interface interface ElementClass extends Vue {} interface IntrinsicElements { [elem: string]: any; } } } // shims-vue.d.ts declare module '*.vue' { import Vue from 'vue'; export default Vue; }
declare:當(dāng)使用第三方庫時,我們需要引用它的聲明文件,才能獲得對應(yīng)的代碼補(bǔ)全、接口提示等功能。
這里列舉出幾個常用的:
declare var 聲明全局變量 declare function 聲明全局方法 declare class 聲明全局類 declare enum 聲明全局枚舉類型 declare global 擴(kuò)展全局變量 declare module 擴(kuò)展模塊
namespace
:“內(nèi)部模塊”現(xiàn)在稱做“命名空間”module X {
相當(dāng)于現(xiàn)在推薦的寫法namespace X {
)
跟其他 JS 庫協(xié)同
類似模塊,同樣也可以通過為其他 JS 庫使用了命名空間的庫創(chuàng)建 .d.ts 文件的聲明文件,如為 D3 JS 庫,可以創(chuàng)建這樣的聲明文件:
declare namespace D3{ export interface Selectors { ... } } declare var d3: D3.Base;
所以上述兩個文件:
shims-tsx.d.ts
, 在全局變量 global中批量命名了數(shù)個內(nèi)部模塊。shims-vue.d.ts
,意思是告訴 TypeScript *.vue 后綴的文件可以交給 vue 模塊來處理。
3.6 訪問修飾符:private、public、protected
其實很好理解:
- 默認(rèn)為public
- 當(dāng)成員被標(biāo)記為private時,它就不能在聲明它的類的外部訪問,比如:
class Animal { ? ? private name: string; ? ? constructor(theName: string) { ? ? ? ? this.name = theName; ? ? } } ? let a = new Animal('Cat').name; //錯誤,‘name'是私有的
protected和private類似,但是,protected成員在派生類中可以訪問
class Animal { ? ? protected name: string; ? ? constructor(theName: string) { ? ? ? ? this.name = theName; ? ? } } ? class Rhino extends Animal { ? ? constructor() { ? ? ? ? super('Rhino'); ? ?} ? ? ? ?? ? ?getName() { ? ? ? ?console.log(this.name) //此處的name就是Animal類中的name ? ?} }?
3.7 可選參數(shù) ( ?: )和非空斷言操作符(!.)
可選參數(shù)
function buildName(firstName: string, lastName?: string) { ? ? return firstName + ' ' + lastName } ? // 錯誤演示 buildName("firstName", "lastName", "lastName") // 正確演示 buildName("firstName") // 正確演示 buildName("firstName", "lastName")
非空斷言操作符:
能確定變量值一定不為空時使用。
與可選參數(shù) 不同的是,非空斷言操作符不會防止出現(xiàn) null 或 undefined。
let s = e!.name; ?// 斷言e是非空并訪問name屬性
拓展
- 1. 屬性或參數(shù)中使用 ?:表示該屬性或參數(shù)為可選項
- 2. 屬性或參數(shù)中使用 ?。罕硎緩?qiáng)制解析(告訴typescript編譯器,這里一定有值),常用于vue-decorator中的@Prop
- 3. 變量后使用 ?。罕硎绢愋屯茢嗯懦齨ull、undefined
4. Vue組件的Ts寫法
從 vue2.5 之后,vue 對 ts 有更好的支持。根據(jù)官方文檔,vue 結(jié)合 typescript ,有兩種書寫方式:
Vue.extend
? import Vue from 'vue' ? ? const Component = Vue.extend({ ? ?? ?// type inference enabled ? })
vue-class-component
import { Component, Vue, Prop } from 'vue-property-decorator' ? @Component export default class Test extends Vue { ? @Prop({ type: Object }) ? private test: { value: string } }
理想情況下,Vue.extend 的書寫方式,是學(xué)習(xí)成本最低的。在現(xiàn)有寫法的基礎(chǔ)上,幾乎 0 成本的遷移。
但是Vue.extend模式,需要與mixins 結(jié)合使用。在 mixin 中定義的方法,不會被 typescript 識別到,這就意味著會出現(xiàn)丟失代碼提示、類型檢查、編譯報錯等問題。
菜鳥才做選擇,大佬都挑最好的。直接講第二種吧:
4.1 vue-class-component
我們回到src/components/HelloWorld.vue
<template> <div class="hello"> <h1>{{ msg }}</h1> <!-- 省略 --> </div> </template> <script lang="ts"> import { Component, Prop, Vue } from 'vue-property-decorator'; @Component export default class HelloWorld extends Vue { @Prop() private msg!: string; } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped></style>
有寫過python的同學(xué)應(yīng)該會發(fā)現(xiàn)似曾相識:
vue-property-decorator這個官方支持的庫里,提供了函數(shù) **裝飾器(修飾符)**語法
1. 函數(shù)修飾符 @
“@”,與其說是修飾函數(shù)倒不如說是引用、調(diào)用它修飾的函數(shù)。
或者用句大白話描述:@: "下面的被我包圍了。"
舉個栗子,下面的一段代碼,里面兩個函數(shù),沒有被調(diào)用,也會有輸出結(jié)果:
test(f){ console.log("before ..."); f() console.log("after ..."); } @test func(){ console.log("func was called"); }
直接運(yùn)行,輸出結(jié)果:
before ...
func was called
after ...
上面代碼可以看出來:
- 只定義了兩個函數(shù):test和func,沒有調(diào)用它們。
- 如果沒有 @test,運(yùn)行應(yīng)該是沒有任何輸出的。
但是,解釋器讀到函數(shù)修飾符“@”的時候,后面步驟會是這樣:
- 去調(diào)用test函數(shù),test函數(shù)的入口參數(shù)就是那個叫“func”的函數(shù);
- test函數(shù)被執(zhí)行,入口參數(shù)的(也就是func函數(shù))會被調(diào)用(執(zhí)行);
換言之,修飾符帶的那個函數(shù)的入口參數(shù),就是下面的那個整個的函數(shù)。有點兒類似JavaScript里面的 function a (function () { ... });
2. vue-property-decorator和vuex-class提供的裝飾器
vue-property-decorator的裝飾器:
@Prop
@PropSync
@Provide
@Model
@Watch
@Inject
@Provide
@Emit
@Component
(provided by vue-class-component)Mixins
(the helper function named mixins provided by vue-class-component)
vuex-class的裝飾器:
@State
@Getter
@Action
@Mutation
我們拿原始Vue組件模版來看:
import {componentA,componentB} from '@/components'; ? export default { ?? ?components: { componentA, componentB}, ?? ?props: { ? ? propA: { type: Number }, ? ? propB: { default: 'default value' }, ? ? propC: { type: [String, Boolean] }, ? } ? // 組件數(shù)據(jù) ? data () { ? ? return { ? ? ? message: 'Hello' ? ? } ? }, ? // 計算屬性 ? computed: { ? ? reversedMessage () { ? ? ? return this.message.split('').reverse().join('') ? ? } ? ? // Vuex數(shù)據(jù) ? ? step() { ? ? ?? ?return this.$store.state.count ? ? } ? }, ? methods: { ? ? changeMessage () { ? ? ? this.message = "Good bye" ? ? }, ? ? getName() { ? ? ?? ?let name = this.$store.getters['person/name'] ? ? ?? ?return name ? ? } ? }, ? // 生命周期 ? created () { }, ? mounted () { }, ? updated () { }, ? destroyed () { } }
以上模版替換成修飾符寫法則是:
import { Component, Vue, Prop } from 'vue-property-decorator'; import { State, Getter } from 'vuex-class'; import { count, name } from '@/person' import { componentA, componentB } from '@/components'; ? @Component({ ? ? components:{ componentA, componentB}, }) export default class HelloWorld extends Vue{ ?? ?@Prop(Number) readonly propA!: number | undefined ? ? @Prop({ default: 'default value' }) readonly propB!: string ? ? @Prop([String, Boolean]) readonly propC!: string | boolean | undefined ?? ? // 原data ? message = 'Hello' ?? ? // 計算屬性 ?? ?private get reversedMessage (): string[] { ? ?? ?return this.message.split('').reverse().join('') ? } ? // Vuex 數(shù)據(jù) ? @State((state: IRootState) => state . booking. currentStep) step!: number ?? ?@Getter( 'person/name') name!: name ?? ? // method ? public changeMessage (): void { ? ? this.message = 'Good bye' ? }, ? public getName(): string { ? ? let storeName = name ? ? return storeName ? } ?? ?// 生命周期 ? private created ():void { }, ? private mounted ():void { }, ? private updated ():void { }, ? private destroyed ():void { } }
正如你所看到的,我們在生命周期 列表那都添加private XXXX方法,因為這不應(yīng)該公開給其他組件。
而不對method做私有約束的原因是,可能會用到@Emit來向父組件傳遞信息。
4.2 添加全局工具
引入全局模塊,需要改main.ts:
import Vue from 'vue'; import App from './App.vue'; import router from './router'; import store from './store'; ? Vue.config.productionTip = false; ? new Vue({ ? router, ? store, ? render: (h) => h(App), }).$mount('#app');
npm i VueI18n
import Vue from 'vue'; import App from './App.vue'; import router from './router'; import store from './store'; // 新模塊 import i18n from './i18n'; ? Vue.config.productionTip = false; ? new Vue({ ? ? router,? ? ? store,? ? ? i18n, // 新模塊 ? ? render: (h) => h(App), }).$mount('#app');
但僅僅這樣,還不夠。你需要動src/vue-shim.d.ts:
// 聲明全局方法 declare module 'vue/types/vue' { ? interface Vue { ? ? ? ? readonly $i18n: VueI18Next; ? ? ? ? $t: TranslationFunction; ? ? } }
之后使用this.$i18n()的話就不會報錯了。
4.3 Axios 使用與封裝
Axios的封裝千人千面
如果只是想簡單在Ts里體驗使用Axios,可以安裝vue-axios 簡單使用Axios
$ npm i axios vue-axios
main.ts添加:
import Vue from 'vue' import axios from 'axios' import VueAxios from 'vue-axios' ? Vue.use(VueAxios, axios)
然后在組件內(nèi)使用:
Vue.axios.get(api).then((response) => { ? console.log(response.data) }) ? this.axios.get(api).then((response) => { ? console.log(response.data) }) ? this.$http.get(api).then((response) => { ? console.log(response.data) })
1. 新建文件request.ts
文件目錄:
-api ? ? - main.ts ? // 實際調(diào)用 -utils ? ? - request.ts ?// 接口封裝
2. request.ts文件解析
import * as axios from 'axios'; import store from '@/store'; // 這里可根據(jù)具體使用的UI組件庫進(jìn)行替換 import { Toast } from 'vant'; import { AxiosResponse, AxiosRequestConfig } from 'axios'; ? ?/* baseURL 按實際項目來定義 */ const baseURL = process.env.VUE_APP_URL; ? ?/* 創(chuàng)建axios實例 */ const service = axios.default.create({ ? ? baseURL, ? ? timeout: 0, // 請求超時時間 ? ? maxContentLength: 4000, }); ? service.interceptors.request.use((config: AxiosRequestConfig) => { ? ? return config; }, (error: any) => { ? ? Promise.reject(error); }); ? service.interceptors.response.use( ? ? (response: AxiosResponse) => { ? ? ? ? if (response.status !== 200) { ? ? ? ? ? ? Toast.fail('請求錯誤!'); ? ? ? ? } else { ? ? ? ? ? ? return response.data; ? ? ? ? } ? ? }, ? ? (error: any) => { ? ? ? ? return Promise.reject(error); ? ? }); ? ?? export default service;
為了方便,我們還需要定義一套固定的 axios 返回的格式,新建ajax.ts:
export interface AjaxResponse { ? ? code: number; ? ? data: any; ? ? message: string; }
3. main.ts接口調(diào)用:
// api/main.ts import request from '../utils/request'; ? // get export function getSomeThings(params:any) { ? ? return request({ ? ? ? ? url: '/api/getSomethings', ? ? }); } ? // post export function postSomeThings(params:any) { ? ? return request({ ? ? ? ? url: '/api/postSomethings', ? ? ? ? methods: 'post', ? ? ? ? data: params ? ? }); }
5. 編寫一個組件
為了減少時間,我們來替換掉src/components/HelloWorld.vue,做一個博客帖子組件:
<template> ?? ?<div class="blogpost"> ?? ??? ?<h2>{{ post.title }}</h2> ?? ??? ?<p>{{ post.body }}</p> ?? ??? ?<p class="meta">Written by {{ post.author }} on {{ date }}</p> ?? ?</div> </template> ? <script lang="ts"> import { Component, Prop, Vue } from 'vue-property-decorator'; ? // 在這里對數(shù)據(jù)進(jìn)行類型約束 export interface Post { ?? ?title: string; ?? ?body: string; ?? ?author: string; ?? ?datePosted: Date; } ? @Component export default class HelloWorld extends Vue { ?? ?@Prop() private post!: Post; ? ?? ?get date() { ?? ??? ?return `${this.post.datePosted.getDate()}/${this.post.datePosted.getMonth()}/${this.post.datePosted.getFullYear()}`; ?? ?} } </script> ? <style scoped> h2 { ? text-decoration: underline; } p.meta { ? font-style: italic; } </style>
然后在Home.vue中使用:
<template> ? <div class="home"> ? ? <img alt="Vue logo" src="../assets/logo.png"> ? ??? ?<HelloWorld v-for="blogPost in blogPosts" :post="blogPost" :key="blogPost.title" /> ? </div> </template> ? <script lang="ts"> import { Component, Vue } from 'vue-property-decorator'; import HelloWorld, { Post } from '@/components/HelloWorld.vue'; // @ is an alias to /src ? @Component({ ? components: { ? ? HelloWorld, ? }, }) export default class Home extends Vue { ? ? private blogPosts: Post[] = [ ? ? ? ? { ? ? ? ? ? title: 'My first blogpost ever!', ? ? ? ? ? body: 'Lorem ipsum dolor sit amet.', ? ? ? ? ? author: 'Elke', ? ? ? ? ? datePosted: new Date(2019, 1, 18), ? ? ? ? }, ? ? ? ? { ? ? ? ? ? title: 'Look I am blogging!', ? ? ? ? ? body: 'Hurray for me, this is my second post!', ? ? ? ? ? author: 'Elke', ? ? ? ? ? datePosted: new Date(2019, 1, 19), ? ? ? ? }, ? ? ? ? { ? ? ? ? ? title: 'Another one?!', ? ? ? ? ? body: 'Another one!', ? ? ? ? ? author: 'Elke', ? ? ? ? ? datePosted: new Date(2019, 1, 20), ? ? ? ? }, ? ? ? ]; } </script>
這時候運(yùn)行項目:
這就是簡單的父子組件
而關(guān)于Class API撤銷,其實還是挺舒服的。 用class 來編寫 Vue組件確實太奇怪了。 (所以本篇Ts入門壓根沒寫Class API)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Element?Plus的el-tree-select組件懶加載+數(shù)據(jù)回顯詳解
el-tree-select組件是el-tree和el-select的結(jié)合體,他們的原始屬性未被更改,下面這篇文章主要給大家介紹了關(guān)于Element?Plus的el-tree-select組件懶加載+數(shù)據(jù)回顯的相關(guān)資料,需要的朋友可以參考下2022-11-11vue實現(xiàn)微信公眾號h5跳轉(zhuǎn)小程序的示例代碼
本文主要介紹了vue實現(xiàn)微信公眾號h5跳轉(zhuǎn)小程序的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08Vue生產(chǎn)環(huán)境如何自動屏蔽console
這篇文章主要介紹了Vue生產(chǎn)環(huán)境如何自動屏蔽console問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06vue 2.8.2版本配置剛進(jìn)入時候的默認(rèn)頁面方法
今天小編就為大家分享一篇vue 2.8.2版本配置剛進(jìn)入時候的默認(rèn)頁面方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09element?ui組件中element.style怎么改詳解
element.style是一種內(nèi)聯(lián)樣式,一般都是代碼里寫死的,下面這篇文章主要給大家介紹了關(guān)于element?ui組件中element.style怎么改的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06