如何讓 Vue 項目快速支持 TypeScript 語法?

簡介

JavaScript 作為一種弱類型的語言为鳄,類型推斷只能提供很有限的支持,TypeScript 提供了一種描述對象形狀的方法歧斟∑危可以幫助提供更好的文檔俊扭,還可以驗證你的代碼可以正常工作,在一些大型的項目中捐康,使用 TypeScript 非常必要,從代碼層次就已經(jīng)避免了很多錯誤贮匕,而且方便文檔的書寫花枫,最主要的就是后期迭代特別爽劳翰,但是對于沒有接觸過強(qiáng)類型語言(Java、C)的童鞋來說乙墙,TypeScript 上手還是有點困難了溺蕉,單就目前前端發(fā)展趨勢來說疯特,TypeScript 還是很重要的,想了解更多 TypeScript 的童鞋可以自己去看 官網(wǎng)录别。

開始

我們需要利用 webpack 創(chuàng)建一個簡單的 vue 項目邻吞,為了方便抱冷,我就直接拿上一篇文章的 Demo 了,小伙伴可以直接 clone 下來并安裝依賴:

git clone https://gitee.com/vv_bug/vue-dist-demo.git && npm install

從 0 開始搭建一個 vue 項目也是非常簡單的赵讯,強(qiáng)烈推薦大家去看我的另外一篇文章 來和 webpack 談場戀愛吧耿眉。

然后我們試著運行一下項目:

npm run dev

然后打開瀏覽器看效果:


2-1.png

支持 TypeScript

這里我們提供兩種方案鸣剪,也是目前比較推薦的兩種。

方式一

利用 Babel@babel/plugin-transform-typescript 插件來實現(xiàn)债鸡。

安裝 @babel/core

babel 核心 API厌均。

npm install -D @babel/core --registry https://registry.npm.taobao.org

安裝 @babel/plugin-transform-typescript 插件

Babel 的 TypeScript 插件。

npm install -D @babel/plugin-transform-typescript --registry https://registry.npm.taobao.org

安裝 babel-loader

babel 加載器。

npm install -D babel-loader --registry https://registry.npm.taobao.org

ok镊屎!安裝完 Babel 的一些依賴后茄螃,我們開始配置 webpack归苍。

我們在工程目錄 vue-dist-demo 下創(chuàng)建一個文件 babel.config.js

touch babel.config.js

然后寫入以下代碼到 babel.config.js 文件:

module.exports = {
  presets: [
    [
      '@babel/preset-env', // 添加 preset-env 預(yù)設(shè)做語法轉(zhuǎn)換跟 polyfill 添加
      {
        corejs: 3,
        useBuiltIns: 'usage',
        modules: false,
      },
    ],
  ],
  plugins: [
    [
      '@babel/plugin-transform-runtime', // 利用 runtime 做 helpers 跟 regenerator 設(shè)置
      {
        corejs: false,
        helpers: true,
        useESModules: false,
        regenerator: true,
        absoluteRuntime: './node_modules',
      },
    ],
  ],
};

找到 webpack 的配置文件 webpack.config.js 文件拼弃,然后添加 babel-loader

... 
.module
        .rule('babel')
            .test(/\.t?j?s?$/) // 對 js、ts 文件進(jìn)行 babel 配置
            .use('babel-loader')
                .loader('babel-loader')
                .end()
            .end()
...

webpack.config.js 文件全部配置:

const path = require('path');
const config = new (require('webpack-chain'))();
config
    .context(path.resolve(__dirname, '.')) // webpack 上下文目錄為項目根目錄
    .entry('app') // 入口文件名稱為 app
        .add('./src/main.js') // 入口文件為 ./src/main.ts
        .end()
    .output
        .path(path.join(__dirname, './dist')) // webpack 輸出的目錄為根目錄的 dist 目錄
        .filename('[name].[hash:8].js') // 打包出來的 bundle 名稱為 "[name].[hash:8].js"
        .publicPath('/') // publicpath 配置為 "/"
        .end()
    .resolve
        .extensions
            .add('.js')
            .add('.vue') // 配置以 .js 等結(jié)尾的文件當(dāng)模塊使用的時候都可以省略后綴
            .end()
        .end()
    .module
        .rule('babel')
            .test(/\.t?j?s?$/) // 對 js、ts 文件進(jìn)行 babel 配置
            .use('babel-loader')
                .loader('babel-loader')
                .end()
            .end()
        .rule('vue') // vue-loader 相關(guān)配置
            .test(/\.vue$/) // 匹配 .vue 文件
            .use('vue-loader')
                .loader('vue-loader')
                .end()
            .end()
        .end()
    .plugin('vue-loader-plugin') // vue-loader 必須要添加 vue-loader-plugin
        .use(require('vue-loader').VueLoaderPlugin, [])
        .end()
    .plugin('html') // 添加 html-webpack-plugin 插件
        .use(require('html-webpack-plugin'), [
            {
                template: path.resolve(__dirname, './public/index.html'), // 指定模版文件
                chunks: ['app'], // 指定需要加載的 chunk
                inject: 'body', // 指定 script 腳本注入的位置為 body
            },
        ])
        .end()
    .devServer
        .host('0.0.0.0') // 服務(wù)器外部可訪問
        .disableHostCheck(true) // 關(guān)閉白名單校驗
        .contentBase(path.resolve(__dirname, './public')) // 設(shè)置一個 express 靜態(tài)目錄
        .historyApiFallback({
            disableDotRule: true, // 禁止在鏈接中使用 "." 符號
            rewrites: [
                { from: /^\/$/, to: '/index.html' }, // 將所有的 404 響應(yīng)重定向到 index.html 頁面
            ],
        })
        .port(8080) // 當(dāng)前端口號
        .hot(true) // 打開頁面熱載功能
        .sockPort('location') // 設(shè)置成平臺自己的端口
        .open(true);
module.exports = config.toConfig();

ok鲁森!就是這么簡單歌溉,接著我們來測試一下骑晶。

測試

我們修改一下 src/app.vue

<template>
    <div class="app">{{ msg }}</div>
</template>

<script lang="ts">
    export default {
        name: "app",
        data(){
            return {
                msg: "hello"
            }
        },
        created() {
            let name: string = "hello ts";
            alert(name);
        }
    }
</script>

可以看到透罢,我們給 script 標(biāo)簽加了一個 lang="ts" 屬性,并且在 create 方法中添加了一段 TypeScript 代碼乾胶,然后我們重新運行看一下效果:

npm run dev
2-2.png

可以看到识窿,webpack 正常編譯了我們的 ts 語法,項目正常運行缩宜。

方式二

利用 ts-loader 結(jié)合官方 typescript 庫來實現(xiàn)甥温。

安裝 typescript

ts 語法核心 API。

npm install -D typescript --registry https://registry.npm.taobao.org

安裝 ts-loader

ts 文件加載器宋梧。

npm install -D ts-loader --registry https://registry.npm.taobao.org

除了配置 ts-loader 外捂龄,我們還需要在工程目錄 vue-dist-demo 下創(chuàng)建一個 ts 配置文件 tsconfig.json

touch tsconfig.json

然后寫入以下代碼到 tsconfig.json 文件:

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "suppressImplicitAnyIndexErrors": true,
    "resolveJsonModule": true,
    "sourceMap": true,
    "baseUrl": ".",
    "types": ["webpack-env"],
    "paths": {
      "@/*": ["src/*"]
    },
    "lib": ["esnext", "dom", "dom.iterable"]
  },
  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
  "exclude": ["node_modules"]
}

然后找到 webpack 配置文件 webpack.config.js倦沧,添加 ts-loader

...
.module
        .rule("type-script") // 配置 ts-loader
            .test(/\.tsx?$/) // loader 加載的條件是 ts 或 tsx 后綴的文件
            .use("ts-loader")
                .loader("ts-loader")
                .options({ // ts-loader 相關(guān)配置
                    transpileOnly: true, // 只用于編譯
                    appendTsSuffixTo: ['\\.vue$'] // 給 .vue 文件添加后綴
                })
                .end()
            .end()
...

webpack.config.js 文件全部配置:

const path = require('path');
const config = new (require('webpack-chain'))();
config
    .context(path.resolve(__dirname, '.')) // webpack 上下文目錄為項目根目錄
    .entry('app') // 入口文件名稱為 app
        .add('./src/main.ts') // 入口文件為 ./src/main.ts
        .end()
    .output
        .path(path.join(__dirname, './dist')) // webpack 輸出的目錄為根目錄的 dist 目錄
        .filename('[name].[hash:8].js') // 打包出來的 bundle 名稱為 "[name].[hash:8].js"
        .publicPath('/') // publicpath 配置為 "/"
        .end()
    .resolve
        .extensions
            .add('.js')
            .add('.vue') // 配置以 .js 等結(jié)尾的文件當(dāng)模塊使用的時候都可以省略后綴
            .end()
        .end()
    .module
        .rule("type-script") // 配置 ts-loader
            .test(/\.tsx?$/) // loader 加載的條件是 ts 或 tsx 后綴的文件
            .use("ts-loader")
                .loader("ts-loader")
                .options({ // ts-loader 相關(guān)配置
                    transpileOnly: true, // 只用于編譯
                    appendTsSuffixTo: ['\\.vue$'] // 給 .vue 文件添加后綴
                })
                .end()
            .end()
        .rule('vue') // vue-loader 相關(guān)配置
            .test(/\.vue$/) // 匹配 .vue 文件
            .use('vue-loader')
                .loader('vue-loader')
                .end()
            .end()
        .end()
    .plugin('vue-loader-plugin') // vue-loader 必須要添加 vue-loader-plugin
        .use(require('vue-loader').VueLoaderPlugin, [])
        .end()
    .plugin('html') // 添加 html-webpack-plugin 插件
        .use(require('html-webpack-plugin'), [
            {
                template: path.resolve(__dirname, './public/index.html'), // 指定模版文件
                chunks: ['app'], // 指定需要加載的 chunk
                inject: 'body', // 指定 script 腳本注入的位置為 body
            },
        ])
        .end()
    .devServer
        .host('0.0.0.0') // 服務(wù)器外部可訪問
        .disableHostCheck(true) // 關(guān)閉白名單校驗
        .contentBase(path.resolve(__dirname, './public')) // 設(shè)置一個 express 靜態(tài)目錄
        .historyApiFallback({
            disableDotRule: true, // 禁止在鏈接中使用 "." 符號
            rewrites: [
                { from: /^\/$/, to: '/index.html' }, // 將所有的 404 響應(yīng)重定向到 index.html 頁面
            ],
        })
        .port(8080) // 當(dāng)前端口號
        .hot(true) // 打開頁面熱載功能
        .sockPort('location') // 設(shè)置成平臺自己的端口
        .open(true);
module.exports = config.toConfig();

測試

重命名 src/main.jssrc/main.ts,然后跟方式一一樣愈污,修改一下 src/app.vue 文件:

<template>
    <div class="app">{{ msg }}</div>
</template>

<script lang="ts">
    export default {
        name: "app",
        data(){
            return {
                msg: "hello"
            }
        },
        created() {
            let name: string = "hello ts-loader";
            alert(name);
        }
    }
</script>

然后我們重新運行看一下效果:

npm run dev
2-3.png

可以看到暂雹,webpack 正常編譯了我們的 ts 語法创夜,項目正常運行驰吓。

總結(jié)

“方式一” 還是有局限性的,只是單純的去轉(zhuǎn)換 ts 語法姑廉,在平時項目開發(fā)中還是推薦使用 “方式二”翁涤,因為更符合 TypeScript 官網(wǎng)規(guī)范萌踱,支持的功能也更豐富号阿,而且通過設(shè)置 tsconfig.json 配置文件簡單清晰明了扔涧,并且能夠與 IDE 完美結(jié)合,實現(xiàn) ts 語法智能提示功能弯汰。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末湖雹,一起剝皮案震驚了整個濱河市劝枣,隨后出現(xiàn)的幾起案子织鲸,更是在濱河造成了極大的恐慌,老刑警劉巖稳诚,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異迎吵,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)俏让,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進(jìn)店門首昔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來糙俗,“玉大人巧骚,你說我怎么就攤上這事格二【匏埃” “怎么了草添?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵远寸,是天一觀的道長。 經(jīng)常有香客問我肆资,道長灶芝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任犯犁,我火速辦了婚禮酸役,結(jié)果婚禮上驾胆,老公的妹妹穿的比我還像新娘。我一直安慰自己入桂,他們只是感情好驳阎,可當(dāng)我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布搞隐。 她就那樣靜靜地躺著劣纲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪劫瞳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天涮因,我揣著相機(jī)與錄音伺绽,去河邊找鬼奈应。 笑死,一個胖子當(dāng)著我的面吹牛肩榕,可吹牛的內(nèi)容都是我干的惩妇。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼挺份!你這毒婦竟也來了匀泊?” 一聲冷哼從身側(cè)響起朵你,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎躲因,沒想到半個月后忌傻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體水孩,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡俘种,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了苍姜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衙猪。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖潘拨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情饶号,我是刑警寧澤铁追,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站茫船,受9級特大地震影響琅束,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜算谈,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一涩禀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧然眼,春花似錦艾船、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鲸匿。三九已至爷怀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間带欢,已是汗流浹背运授。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留乔煞,地道東北人吁朦。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像渡贾,于是被迫代替她去往敵國和親喇完。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,500評論 2 359

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