學(xué)習(xí)搭建Vue的TypeScript環(huán)境

實現(xiàn)目標(biāo):搭建 Vue 的 TS 版編程環(huán)境稠项,并學(xué)習(xí)下 Vue 的類組件使用

一瓜晤、配置 webpack

首先通過:

npm init

來創(chuàng)建依賴文件:package.json爆班。

接下來安裝 webpack矩乐,因為 webpack 屬于開發(fā)環(huán)境下的依賴印蔬,所以安裝在 devDependencies 里面。

npm install -D webpack webpack-cli webpack-dev-server

接下來整理和創(chuàng)建文件基显,文件結(jié)構(gòu)為:


├─ package.json
│  webpack.config.js
│  
├─public
│      index.html
│      
└─src
        main.js

先說下怎么獲取這個文檔樹的蘸吓,windows下打開 CMD:

tree /f > filetree.txt

tree 只能獲取到文件夾,/f 能獲取到具體文件撩幽,然后通過 > filetree.txt 寫入文件库继。如果有 node_modules 的影響箩艺,我們可以忽略這個文件夾,這時的借助 git 來實現(xiàn):

-I命令允許你使用正則匹配來排除掉你不想看到的文件夾
tree -I "node_modules"
也可以使用|同時排除掉多個文件夾:
tree -I "node_modules|cache|test_*"
有時候文件夾層級很深宪萄,我們只關(guān)心前兩級的文件艺谆,可以使用如下-L :
tree -L 2

這時來看看各個文件的內(nèi)容:
webpack.config.js:

module.exports = {
    
    /* 
        mode:"production" | "development" | "none" 
        production:enable many optimizations for production builds
        development:enabled useful tools for development
        none: no defaults,沒有默認(rèn)拜英,必須設(shè)置為production或development
        Chosen mode tells webpack to use its built-in optimizations accordingly
    */
    //--mode=development會將 process.env.NODE_ENV 的值設(shè)為 development静汤。并啟動相應(yīng)的插件
    mode:"development",

    /* 基礎(chǔ)目錄,用于從配置中解析入口起點(entry point)和 loader */
    // context: path.resolve(__dirname, "app")

    /* 應(yīng)用程序的起點入口聊记。從這個起點開始撒妈,應(yīng)用程序啟動執(zhí)行。
    */
    entry:"./src/main.js",

    output:{
        filename:"bundle.js",
        // 
        publicPath:"virtual"
    },
    module:{
        rules:[

        ]
    },
    resolve:{
        extensions:[".js",".vue",".css",".json"]
    },
    devServer:{
        open: true,
        /* When open is enabled, the dev server will open the browser.
        使用默認(rèn)瀏覽器打開服務(wù)
        open: true
        Usage via the CLI//命令行中使用
        webpack-dev-server --open
        If no browser is provided (as shown above), your default browser will be used. To specify a different browser, just pass its name:
        //指定瀏覽器
        webpack-dev-server --open 'Google Chrome */


        /* 指定打開瀏覽器時要瀏覽的頁面排监。
        Specify a page to navigate to when opening the browser. 
        默認(rèn)瀏覽的頁面是webpack.config.js所在目錄的index.html文件
        */
        openPage: './public/index.html'
    },
    plugins:[

    ]
}

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue中使用TypeScript</title>
</head>
<body>
    <script async src="../virtual/bundle.js"></script>
</body>
</html>

main.js:

alert(process.env.NODE_ENV);//development

歐克了狰右,現(xiàn)在來啟動服務(wù):

啟動服務(wù), --progress可以將運行進(jìn)度輸出到控制臺舆床。
npx webpack-dev-server --progress

二棋蚌、啟動 JS 版 Vue

安裝 Vue:

npm i -S vue

在 index.html 增加一個一行:

<div id="app"></div>

這時更改main.js文件:

import Vue from "vue";

new Vue({
    el:"#app",
    render:function(h){
        return h("h1","TypeScript!")
    }
})
image

打開網(wǎng)址查看挨队,沒有問題谷暮。但是如果把 main.js 改成這樣:

import Vue from "vue";

new Vue({
    el:"#app",
    data:{
        name: "TypeScript!"
    },
    template:"<h1>{{name}}</h1>"
})

會看到瀏覽器報錯:

vue.runtime.esm.js:620 [Vue warn]: You are using the runtime-only build of Vue where the
template compiler is not available. Either pre-compile the templates into render functions,
or use the compiler-included build.
[Vue警告]:您正在使用Vue的僅運行時版本,而模板編譯器不可用盛垦∈遥可以將模板預(yù)編譯為渲染函數(shù),
也可以使用包含編譯器的內(nèi)部版本腾夯。

Vue 官網(wǎng)演示案例:

https://cn.vuejs.org/v2/guide/installation.html#%E6%9C%AF%E8%AF%AD
// 需要編譯器
new Vue({
  template: '<div>{{ hi }}</div>'
})

// 不需要編譯器
new Vue({
  render (h) {
    return h('div', this.hi)
  }
})

如果你使用 render(h) 渲染函數(shù)就不需要去 webpack 指定完整版了〖瞻#現(xiàn)在我們?nèi)ブ付ㄏ?Vue 的版本,修改 webpack.config.js蝶俱,需要注意的是:因為運行時版本相比完整版體積要小大約30%班利,所以應(yīng)該盡可能使用這個版本。如果你仍然希望使用完整版榨呆,則需要在打包工具里配置一個別名:

module.exports = {
  // ...
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js' // 用 webpack 1 時需用 'vue/dist/vue.common.js'
    }
  }
}
//https://cn.vuejs.org/v2/guide/installation.html

這個時候在 main.js 同級目錄下創(chuàng)建 App.vue 文件罗标,同時更改 main.js 文件的內(nèi)容為:

import Vue from "vue";
import App from "./App";

new Vue({
    el:"#app",
    render:h=>h(App)
});

不過這時候我們還不能使用單文件組件,需要 loader 對應(yīng)的 loader:
安裝依賴:

npm i -D vue-loader vue-template-compiler

注意:

vue-template-compiler 需要獨立安裝的原因是你可以單獨指定其版本积蜻。
每個 vue 包的新版本發(fā)布時闯割,一個相應(yīng)版本的 vue-template-compiler 也會隨之發(fā)布。編譯器的版本必須和基本的 vue 包保持同步竿拆,這樣 vue-loader 就會生成兼容運行時的代碼宙拉。這意味著你每次升級項目中的 vue 包時,也應(yīng)該匹配升級 vue-template-compiler如输。

去 webpack.config.js 配置下loader:

// webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')

module.exports = {
  module: {
    rules: [
      // ... 其它規(guī)則
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  plugins: [
    // 請確保引入這個插件!
    new VueLoaderPlugin()
  ]
}

你肯定好奇,vue-Loader 的配置和其它的 loader 怎么不太一樣不见。除了通過一條規(guī)則將 vue-loader 應(yīng)用到所有擴展名為 .vue 的文件上之外澳化,還在 plugins 里使用了 VueLoaderPlugin 插件。這個插件是干啥的稳吮?

這個插件是必須的缎谷! 它的職責(zé)是將你定義過的其它規(guī)則復(fù)制并應(yīng)用到 .vue 文件里相應(yīng)語言的塊。例如灶似,如果你有一條匹配 /\.css$/ 的規(guī)則列林,那么它會應(yīng)用到 .vue 文件里的 <style> 塊。

現(xiàn)在我們還沒有安裝處理 css 的 loader酪惭,這時我們對 App.vue 寫上樣式就會報錯希痴, App.vue 的內(nèi)容:

<template>
    <h1>哈哈</h1>
</template>
<style scoped>
h1{
    color: rgb(55, 142, 230);
    font-size: 25px;
    text-align: center;
    width: 200px;
    line-height: 200px;
    border: 10px solid #999;
    border-radius: 5px;
}
</style>

安裝處理樣式的依賴:

npm i -D css-loader vue-style-loader

在去配置下 webpack :

module:{
    rules:[
        {
            test: /\.vue$/,
            loader: 'vue-loader'
        },
        {
            test: /\.css$/,
            use: [
                //順序不能倒過來
                'vue-style-loader',
                'css-loader'
            ]
        }
    ]
},

完成顯示:

image

三、啟動 TS 版 Vue

Vue 時自帶 xx.d.ts 聲明文件的春感,所以不用我們安裝支持 TS 的第三方插件砌创。

接著配置,安裝插件并生成 ts.config.json

npm i -D ts-loader typescript
npx tsc --init

修改 webpack.config.js 的配置:

{ 
    test: /\.ts?$/, 
    loader: "ts-loader",
    options: {
        // .vue文件必須加
        appendTsSuffixTo: [/\.vue$/]
    },
    exclude:/node_modules/
}

appendTsSuffixTo的作用鲫懒?
要與文件名匹配的正則表達(dá)式列表嫩实。如果文件名與其中一個正則表達(dá)式匹配,則會在該文件名后面附加一個.ts或.tsx后綴窥岩。這對于*.vue文件格式現(xiàn)在很有用甲献。(將來可能會受益于新的單一文件格式。)

App.vue 文件內(nèi)容改為:

<template>
    <h1>{{name}}</h1>
</template>
<script lang="ts">
//也執(zhí)行tsx,也就是jsx語法
import Vue from "vue";

export default Vue.extend({
    data(){
        return {
            name:"TypeScript!"
        }
    }
})
</script>
<style scoped>
h1{
    color: rgb(55, 142, 230);
    font-size: 25px;
    text-align: center;
    width: 200px;
    line-height: 200px;
    border: 10px solid #999;
    border-radius: 5px;
}
</style>
//下面這種寫法也行
<template>
    <h1>{{name}}</h1>
</template>
<script lang="ts">
export default {
    data(){
        return {
            name:123
        }
    }
}
</script>
<style scoped>
h1{
    color: rgb(55, 142, 230);
    font-size: 25px;
    text-align: center;
    width: 200px;
    line-height: 200px;
    border: 10px solid #999;
    border-radius: 5px;
}
</style>

main.js 改為 main.ts 內(nèi)容改成:

import Vue from "vue";
// 不能不加.vue后綴颂翼,不加會報錯
import App from "./App.vue";

new Vue({
    el:"#app",
    render:h=>h(App)
});

這時啟動服務(wù)會有個問題:

Cannot find module './App.vue'.

不能找到模塊晃洒,由于 TypeScript 默認(rèn)并不支持 *.vue 后綴的文件,所以在 vue 項目中引入的時候需要創(chuàng)建一個 vue-shims.d.ts 文件,意思是告訴 TypeScript 以 *.vue 為后綴的文件可以交給 vue 模塊來處理 (真正的原因是導(dǎo)入的模塊需要一個 Vueconstructor)疚鲤。

而在代碼中導(dǎo)入 *.vue 文件的時候锥累,需要寫上 .vue 后綴。原因是因為 TypeScript 默認(rèn)只識別 *.ts 文件集歇,不識別 *.vue 文件桶略。
在 src 目錄下新建 vue-shims.d.ts:

declare module '*.vue' {
    import Vue from 'vue';
    export default Vue;
}

四、類組件

上面單文件組件 Vue 的寫法固然是沒問題诲宇,但是不能很好的去使用 ES6 類等語法际歼,所以我們要改成類組件的形式。

安裝依賴:

npm i -D vue-class-component

然后去修改下 App.vue 文件:

<template>
    <div class="App">
        <h1>{{count}}</h1>
        <h3>{{name}}</h3>
        <button @click="increment">加加</button>
        <button @click="decrement">減減</button>
        <h5>計算屬性{{computed}}</h5>
        <button @click="computed++">設(shè)置計算屬性</button>
    </div>
</template>
<script lang="ts">
import Vue from 'vue';
import Component from 'vue-class-component';

// Define the component in class-style
//@Component 裝飾器使您的類成為Vue組件:
@Component({
    // 對于所有其他選項姑蓝,請將它們傳遞給裝飾器函數(shù):
    // 如果沒有可以省略
    props:["name"],
    components:{

    }
})
export default class App extends Vue {
    // Class properties will be component data
    //類屬性就是data中return 的數(shù)據(jù)
    private count:number = 0;

    // Methods will be component methods
    //組件methods可以直接聲明為類原型方法
    private increment():void {
        this.count++
    }

    private decrement():void {
        this.count--
    }

    // 生命周期
    private mounted():void{
        console.log(this.count);
    }

    // 計算屬性聲明為類屬性getter / setter:
    get computed():number{
        return this.count ** 2 ;
    }

    //  A 'set' accessor cannot have a return type annotation.
    // set訪問器不能設(shè)置返回類型
    set computed(value:number){
        this.count = value++;
        console.log(value);
    }
}
</script>
<style scoped>
.App{
    margin: 100px auto;
    color: rgb(55, 142, 230);
    font-size: 25px;
    text-align: center;
    width: 300px;
    height: 400px;
    border: 10px solid #999;
    border-radius: 5px;
}
</style>

注意 main.ts 也被修改了:

import Vue from "vue";
// 不能不加.vue鹅心,會報錯
import App from "./App.vue";

new Vue({
    el:"#app",
    components:{
        App
    },
    template:"<App name='TypeScript' />"
});

又因為我們用到了裝飾器,所以的更改下 tsconfig.js 文件纺荧,打開裝飾器語法:

"experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */

演示效果:

image

更多官網(wǎng) API :https://class-component.vuejs.org/

五旭愧、Vue 裝飾器

上面的案例我們使用了一個裝飾器颅筋,挺好用的是不是,但是可以只有一個输枯,如果想要更多可以參考官網(wǎng)自定義裝飾器來給我們使用议泵,我們當(dāng)然不使用這種方式,我們使用插件:

npm i -D vue-property-decorator

看看官網(wǎng)提供了多少個裝飾器:

There are several decorators and 1 function (Mixin):

@Prop
@PropSync
@Model
@Watch
@Provide
@Inject
@ProvideReactive
@InjectReactive
@Emit
@Ref
@Component (provided by vue-class-component)
Mixins (the helper function named mixins provided by vue-class-component)

注意這個插件是依賴vue-class-component的桃熄。

我們來看幾個最常用的先口,現(xiàn)在 App.vue 是 Children.vue 的父親,main.ts 內(nèi)容不變瞳收,來開始吧:
App.vue

<template>
    <div class="App">
        <h1>{{count}}</h1>
        <h3>{{name}}</h3>
        <Children @info="receive" @returnValue="returnValue"/>
    </div>
</template>

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

// Define the component in class-style
//@Component 裝飾器使您的類成為Vue組件:
@Component({
    components:{
        Children
    },
    watch:{
        /* count(value){
            console.log(value);
        } */
        count:{
            handler:(value , oldValue)=>{
                console.log(value,oldValue);
            },
            immediate:true,
            deep: true
        }
    }
})
export default class App extends Vue {
    
    // String開頭字母大寫
    @Prop(String) name:string | undefined;
    private count:number = 0;
    
    
    private receive(val:number):void {
        this.count = val;
    }
    private returnValue(val:number):void {
        this.count = val;
    }

    @Watch("count" , { immediate: true, deep: true })
    watch_count(val:any, oldVal:any) {
        console.log(val,oldVal);
    }

}
</script>
<style scoped>
.App{
    margin: 100px auto;
    color: rgb(55, 142, 230);
    font-size: 25px;
    text-align: center;
    width: 300px;
    height: 400px;
    border: 10px solid #999;
    border-radius: 5px;
}
</style>

Children.vue

<template>
    <div class="chilren">
        <p>我是子組件children</p>
        <button @click="send">點我發(fā)射</button>
        <button @click="returnValue">click me send</button>
    </div>
</template>

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

@Component
export default class Chilren extends Vue {
    private val:number = 0;

    @Emit("returnValue")
    returnValue() {
        this.val++;
        return this.val;
    }

    private send():void{
        this.val++;
        this.$emit("info",this.val);
    }
}
</script>

演示效果:

image

更多 API 參考官網(wǎng):https://www.npmjs.com/package/vue-property-decorator

六碉京、提升編譯速度

現(xiàn)在項目編譯一次需要時間為:

Time: 6236ms

As your project becomes bigger, compilation time increases linearly. It's because typescript's semantic checker has to inspect all files on every rebuild. The simple solution is to disable it by using the transpileOnly: true option, but doing so leaves you without type checking and will not output declaration files.

隨著項目變得越來越大,編譯時間線性增加螟深。這是因為在每次重建時谐宙,類型腳本的語義檢查器必須檢查所有文件。簡單的解決方案是通過使用transfileOnly:true選項來禁用它血崭,但是這樣做會使您沒有類型檢查卧惜,并且不會輸出聲明文件。

所以我們配置下 webpack.config.js 文件:

{
    test: /\.tsx?$/,
    use: [
        {
            loader: 'ts-loader',
            options: {
                transpileOnly: true
            }
        }
    ]
}

再次編譯用時:

Time: 1659ms

快了不是一星半點夹纫。但是現(xiàn)在類型檢查沒了咽瓷,這則么能夠,這時我們使用 fork-ts-checker-webpack-plugin 另外開一個線程來進(jìn)行類型檢查舰讹,安裝依賴:

npm install -D fork-ts-checker-webpack-plugin
//使用:
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
plugins:[
    new ForkTsCheckerWebpackPlugin()
]

再次編譯用時:

//無類型檢查編譯用時
Time: 1947ms
//加上類型檢查用時
Time: 3284ms

就這也快了一半茅姜。

2020年2月15日 01點05分

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市月匣,隨后出現(xiàn)的幾起案子钻洒,更是在濱河造成了極大的恐慌,老刑警劉巖锄开,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件素标,死亡現(xiàn)場離奇詭異,居然都是意外死亡萍悴,警方通過查閱死者的電腦和手機头遭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來癣诱,“玉大人计维,你說我怎么就攤上這事∷河瑁” “怎么了鲫惶?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長实抡。 經(jīng)常有香客問我欠母,道長欢策,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任赏淌,我火速辦了婚禮猬腰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘猜敢。我一直安慰自己,他們只是感情好盒延,可當(dāng)我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布缩擂。 她就那樣靜靜地躺著,像睡著了一般添寺。 火紅的嫁衣襯著肌膚如雪胯盯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天计露,我揣著相機與錄音博脑,去河邊找鬼。 笑死票罐,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播勺卢,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼往衷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蚕礼?” 一聲冷哼從身側(cè)響起烟具,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎奠蹬,沒想到半個月后朝聋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡囤躁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年冀痕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片割以。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡金度,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出严沥,到底是詐尸還是另有隱情猜极,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布消玄,位于F島的核電站跟伏,受9級特大地震影響丢胚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜受扳,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一携龟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧勘高,春花似錦峡蟋、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至赖舟,卻和暖如春蓬戚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宾抓。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工子漩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人石洗。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓幢泼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親讲衫。 傳聞我的和親對象是個殘疾皇子旭绒,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容