實現(xiàn)目標:搭建 Vue 的 TS 版編程環(huán)境,并學習下 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,沒有默認,必須設(shè)置為production或development
Chosen mode tells webpack to use its built-in optimizations accordingly
*/
//--mode=development會將 process.env.NODE_ENV 的值設(shè)為 development。并啟動相應的插件
mode:"development",
/* 基礎(chǔ)目錄,用于從配置中解析入口起點(entry point)和 loader */
// context: path.resolve(__dirname, "app")
/* 應用程序的起點入口。從這個起點開始,應用程序啟動執(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.
使用默認瀏覽器打開服務(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.
默認瀏覽的頁面是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可以將運行進度輸出到控制臺。
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!")
}
})
打開網(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的僅運行時版本,而模板編譯器不可用。可以將模板預編譯為渲染函數(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 指定完整版了?,F(xiàn)在我們?nèi)ブ付ㄏ?Vue 的版本,修改 webpack.config.js,需要注意的是:因為運行時版本相比完整版體積要小大約30%,所以應該盡可能使用這個版本。如果你仍然希望使用完整版,則需要在打包工具里配置一個別名:
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 對應的 loader:
安裝依賴:
npm i -D vue-loader vue-template-compiler
注意:
vue-template-compiler 需要獨立安裝的原因是你可以單獨指定其版本。
每個 vue 包的新版本發(fā)布時,一個相應版本的 vue-template-compiler 也會隨之發(fā)布。編譯器的版本必須和基本的 vue 包保持同步,這樣 vue-loader 就會生成兼容運行時的代碼。這意味著你每次升級項目中的 vue 包時,也應該匹配升級 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 應用到所有擴展名為 .vue 的文件上之外,還在 plugins 里使用了 VueLoaderPlugin 插件。這個插件是干啥的?
這個插件是必須的! 它的職責是將你定義過的其它規(guī)則復制并應用到 .vue 文件里相應語言的塊。例如,如果你有一條匹配
/\.css$/的規(guī)則,那么它會應用到 .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'
]
}
]
},
完成顯示:
三、啟動 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的作用?
要與文件名匹配的正則表達式列表。如果文件名與其中一個正則表達式匹配,則會在該文件名后面附加一個.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 默認并不支持 *.vue 后綴的文件,所以在 vue 項目中引入的時候需要創(chuàng)建一個 vue-shims.d.ts 文件,意思是告訴 TypeScript 以 *.vue 為后綴的文件可以交給 vue 模塊來處理 (真正的原因是導入的模塊需要一個 Vueconstructor)。
而在代碼中導入 *.vue 文件的時候,需要寫上 .vue 后綴。原因是因為 TypeScript 默認只識別 *.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. */
演示效果:
更多官網(wǎng) API :https://class-component.vuejs.org/
五、Vue 裝飾器
上面的案例我們使用了一個裝飾器,挺好用的是不是,但是可以只有一個,如果想要更多可以參考官網(wǎ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>
演示效果:
更多 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 另外開一個線程來進行類型檢查,安裝依賴:
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分