vue 和 TypeScript 結合的情況下,很多寫法和我們平時的寫法都不太一樣,這裡總結我專案開發過程中遇到的問題和問題的解決方案 有些問題可能還沒解決,歡迎各位大佬給與提點。 另外,使用本文前可以先看vue 官方文件關於 typescript 的使用講解
整個 vue 專案的目錄結構
- 大體用 vue-cli 建立的專案,結構基本不變。
這裡只寫我後來為了解決問題改動的地方
main.ts 中,提示import App from './App.vue'處,找不到 App.vue 這個模組
解決方案: 1、將 shims-vue.d.ts 檔案一分為二。 2、在 shims-vue.d.ts 檔案同級目錄下新建 vue.d.ts(名字不一定叫 vue,如 xxx.d.ts 也可以),然後此檔案包含程式碼如下
// vue.d.ts
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}複製程式碼
3、而原來的 shims-vue.d.ts 程式碼修改、新增如下:
// shims-vue.d.ts
import Vue from 'vue'
import VueRouter, { Route } from 'vue-router'
import { Store } from 'vuex'
declare module 'vue/types/vue' {
interface Vue {
$router: VueRouter;
$route: Route;
$store: Store<any>;
// 以下是在main.ts中掛載到Vue.prototype上的變數
$api: any;
$mock: any;
$configs: any;
}
}複製程式碼
main.ts 中,往 Vue 的原型 prototype 上掛載全域性變數
1、main.ts 配置
// main.ts
import api from "./api/request";
import mock from "./api/mock";
import configs from "./utils/config";
Vue.prototype.$api = api;
Vue.prototype.$mock = mock;
Vue.prototype.$configs = configs;複製程式碼
2、shims-vue.d.ts 配置
// shims-vue.d.ts 新增如下
declare module 'vue/types/vue' {
interface Vue {
// ...
// 以下是在main.ts中掛載到Vue.prototype上的變數
$api: any;
$mock: any;
$configs: any;
}
}複製程式碼
全域性元件註冊
註冊
// main.ts
import Page from "@/components/page.vue";
import AllComponent from "@/common/AllComponent.vue";
Vue.component("Page", Page);
Vue.component("all-component", AllComponent);複製程式碼
使用
寫法一:
<Page />
寫法二:
<all-component />複製程式碼
SFC 單 vue 檔案元件的基本寫法和結構
一個簡陋的 demo,展示 ts 下的 vue 檔案中,對於相關功能的使用,重點關注<Script>
裡的程式碼
<template>
<!-- 結構示例,指令基礎用法同vue -->
<div class="minos-system-setting" v-if="hideHeader">
<h3>結構示例</h3>
<span>{{ selfKey1 }}</span>
<ul>
<li :key="item" v-for="item in fatherKey">{{ item }}</li>
</ul>
<button @click="addText">追加文字</button>
<AnotherVue
:class="['default-class', selfKey1.length > 10 ? 'one' : 'two']"
/>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop, Watch } from "vue-property-decorator";
import { Route } from "vue-router";
import AnotherVue from "@/components/AnotherVue.vue";
@Component({
// 元件註冊
components: {
AnotherVue
// 'another-vue': AnotherVue
},
// 過濾器
filters: {
filterFn1() {}
},
// 屬性傳遞
props: {
hideHeader: {
type: Boolean,
required: false,
default: false // 預設屬性的預設值
}
}
})
export default class ComponentName extends Vue {
@Prop({
type: Boolean,
required: false,
default: false // 預設屬性的預設值
})
private hideHeader!: boolean | undefined;
@Prop() private fatherKey: string[]; // 其他沒有預設值的傳值
selfKey1: string = "自己的一個變數";
// 生命週期
created() {}
mounted() {}
// 計算屬性
get computedKey() {
return this.selfKey1.length;
}
// 監聽器
@Watch("computedKey")
getcomputedKey(newVal) {
console.log(newVal);
}
// 導航守衛函式
private beforeRouteEnter(to: Route, from: Route, next: () => void): void {
console.log("beforeRouteEnter", to, from, next);
next();
}
// 方法
addText() {
this.selfKey1 += ",追加文字!";
}
}
</script>
<style lang="scss" scoped>
@import "@/assets/styles/demo.scss";
</style>複製程式碼
computed 計算屬性的寫法
// 計算屬性
get computedKey() {
return this.selfKey1.length
}複製程式碼
watch 監聽器的使用
同一個 vue 頁面中使用
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
@Watch('boxHeight')
getboxHeight(val) { // get+上邊括號裡的名字
// xxx
}複製程式碼
父子兩個 vue 頁面傳值後使用 watch 監聽
子元件監聽從父元件傳過來的值 1、父元件用屬性傳值【前提是父元件引入子元件、註冊並呼叫了】
<ziZuJian :oneKey="oneKeyObj" />複製程式碼
2、子元件要使用的工具引入工作
import { Component, Vue, Prop, Watch } from "vue-property-decorator";複製程式碼
3、子元件 Prop 接受
export default class ZiZuJian extends Vue {
@Prop() private oneKey: object
}複製程式碼
4、子元件 Watch 監聽
@Watch('oneKey')
getoneKey(newVal,oldVal) {
// 監聽成功後要做
log(newVal)
this.myfunction(newVal)
}複製程式碼
5、父元件(內部)改動值,會被子元件監聽
export default class FuZuJian extends Vue {
oneKeyObj = {}
...
mounted(){
$.ajax().then(()=>{
// 適時情況下改動props傳遞的值,就會被子元件監聽到改變
oneKeyObj = {
name : '測試'
}
oneKeyObj.age = 18
})
}
}複製程式碼
Watch 監聽 store 中的資料改變
主要思路是計算屬性獲取 state 裡的資料,watch 再監聽計算屬性
import { Component, Vue, Prop, Watch } from 'vue-property-decorator' // 引入Watch
get stateSomeKey() { // 計算屬性
// 監聽state下的stateSomeKey物件中的keyName屬性,return返回該值
return this['$store'].state.stateSomeKey.keyName
}
@Watch('stateSomeKey') // 與上邊計算屬性同名
getstateSomeKey(val) { // get+上邊括號裡的名字
// 監聽到變化後,執行對應的內容
this.myFunction()
...
}複製程式碼
其中,第七行,監聽器那裡也可以這麼寫
@Watch('stateSomeKey') // 與上邊計算屬性同名
watchMenuState(val) { // 這裡可以這麼寫:或用watch+上邊括號裡的名字也可以(雖然不太確定為什麼,只是程式碼這麼寫成功了)
// 下同
// ...
}複製程式碼
vue+ts 中,使用 filter 過濾器
定義:(在@Component 裡邊,寫 filters,注意 s 單詞)
<script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator";
@Component({
filters: {
filterValue(value) {
return Number(value).toLocaleString();
}
// otherFilterFn(value) { 其他filter示例
// return ...
// }
},
components: {}
})
export default class Container extends Vue {
// ...
}
</script>複製程式碼
使用:同之前,正常使用:
<span v-if="showSpan">{{showValue | filterValue}}</span>複製程式碼
自定義指令 過濾器【待補充】
// 待補充複製程式碼
watch 監聽 router 的變化
1、shims-vue.d.ts 的設定
// shims-vue.d.ts
import Vue from 'vue'
import VueRouter, {Route} from 'vue-router';
declare module 'vue/types/vue' {
interface Vue {
$router: VueRouter; // 這表示this下有這個東西
$route: Route;
}
}複製程式碼
2、main.ts 的設定
// main.ts
import { Component } from "vue-class-component";
Vue.config.productionTip = false;
Component.registerHooks([
"beforeRouteEnter", //進入路由之前
"beforeRouteLeave", //離開路由之前
"beforeRouteUpdate"
]);複製程式碼
3、需要監聽路由鉤子的 SCF 元件:
<script lang="ts">
// xxx.vue 的script標籤內
import { Component, Vue, Prop, Watch } from "vue-property-decorator";
import { Route, RawLocation } from 'vue-router';
// # 下邊兩段,看你需要什麼了:
// 1/監聽路由變化
@Watch('$route',{ immediate: true })
private changeRouter(route: Route){
console.log(route)
}
// 2/定義路由鉤子函式
private beforeRouteEnter(to: Route, from: Route, next: () => void): void {
console.log('beforeRouteEnter', to, from, next)
next(); // 沒有next將不會進入路由內部,跟vue文件用法一致
}
private beforeRouteUpdate(to: Route, from: Route, next: () => void): void {
console.log('beforeRouteUpdate'); // 暫時不生效,版本問題
next();
}
private beforeRouteLeave(to: Route, from: Route, next: () => void): void {
console.log('beforeRouteLeave');
next();
}
</script>複製程式碼
監聽路由的第二種寫法 (如果只是想更新檢視的話可以考慮監聽路由)
@Watch('$route')
routeWatch() {
this.loadData();
}複製程式碼
main.ts 中註冊路由導航守衛並在元件中使用路由鉤子函式
基本同上 1、shims-vue.d.ts 的設定
// shims-vue.d.ts
import Vue from 'vue'
import VueRouter, {Route} from 'vue-router';
declare module 'vue/types/vue' {
interface Vue {
$router: VueRouter; // 這表示this下有這個東西
$route: Route;
}
}複製程式碼
2、main.ts 的設定
// main.ts
import { Component } from "vue-class-component";
Component.registerHooks([
"beforeRouteEnter", //進入路由之前
"beforeRouteLeave", //離開路由之前
"beforeRouteUpdate"
]);複製程式碼
3、需要監聽路由鉤子的 SCF 元件:
<script lang="ts">
// xxx.vue 的script標籤內
import { Component, Vue, Prop, Watch } from "vue-property-decorator";
import { Route, RawLocation } from 'vue-router';
// # 下邊兩段,看你需要什麼了:
// 1/監聽路由變化
@Watch('$route',{ immediate: true })
private changeRouter(route: Route){
console.log(route)
}
// 2/定義路由鉤子函式
private beforeRouteEnter(to: Route, from: Route, next: () => void): void {
console.log('beforeRouteEnter', to, from, next)
next(); // 沒有next將不會進入路由內部,跟vue文件用法一致
}
private beforeRouteUpdate(to: Route, from: Route, next: () => void): void {
console.log('beforeRouteUpdate'); // 暫時不生效,版本問題
next();
}
private beforeRouteLeave(to: Route, from: Route, next: () => void): void {
console.log('beforeRouteLeave');
next();
}
</script>複製程式碼
父子傳值 - 子元件修改觸發父元件的方法執行
父元件內部: 1、呼叫子元件、並繫結傳值:
<ZiZuJian @chuanDiGuoQu="FuQinZiJiYong"></ZiZuJian>複製程式碼
準備好一會會被子元件觸發的函式:
FuQinZiJiYong(){
console.log('我是父親內部待被觸發的方法')
}複製程式碼
子元件 ZiZuJian 內部在需要觸發的地方執行$emit
export default class Menu extends Vue {
// 在需要觸發的地方,執行如下程式碼
this.$emit('chuanDiGuoQu', '')
}複製程式碼
最後還有另一種網友總結很麻煩的寫法:參見地址
@Prop 預設引數
第一種:github 找到的 demo 這樣。如下程式碼中hideHeader
就是由預設引數的父元件傳過來的屬性
export default class ComponentName extends Vue {
@Prop({
type: Boolean,
required: false,
default: false // 預設屬性的預設值
})
private hideHeader!: boolean | undefined
}複製程式碼
第二種:vue 原生的寫法,並寫到了@component 構造器中就好了: 如果不傳值此函式預設就是 true,傳 false 就是 false 了。並且能嚴格判斷只能傳 Boolean 型別。挺好。
@Component({
props: {
hideHeader: {
type: Boolean,
required: false,
default: false // 預設屬性的預設值
}
}
})複製程式碼
中央匯流排註冊與使用
// 待解決複製程式碼
vue + ts 中使用 vue-echarts
安裝
npm i -S vue-echarts echarts複製程式碼
main.ts 中引入並註冊
// main.ts
// 引用
import ECharts from "vue-echarts";
// 用到的模組要單獨引用
import "echarts/lib/chart/line"; // 線圖為例,其他圖一樣
import "echarts/lib/component/title.js"; // 標題
import "echarts/lib/component/legend"; // 圖例
import "echarts/lib/component/tooltip"; // 提示框
import "echarts/lib/component/toolbox"; // 工具(如下載功能與按鈕)
// 註冊
Vue.component("v-chart", ECharts);複製程式碼
vue.config.js 中設定
// vue.config.js
module.exports = {
// For Vue CLI 3+, add vue-echarts and resize-detector into transpileDependencies in vue.config.js like this:
transpileDependencies: ["vue-echarts", "resize-detector"]
};複製程式碼
tsconfig.json 中也要設定
// tsconfig.json
{
"compilerOptions": {
"types": ["webpack-env", "echarts"]
}
}複製程式碼
SFC 應用
<v-chart :options="echartsOptions" id="myCharts" ref="myCharts" />複製程式碼
vue + ts 中使用 Element-ui
// main.ts
import ElementUI from "element-ui";
Vue.use(ElementUI);複製程式碼
全域性 scss 變數
在 assets/styles 下新建_variable.scss 檔案,用於存放 scss 變數。 然後再 vue.config.js 中設定全域性變數
// vue.config.js
module.exports = {
css: {
loaderOptions: {
sass: {
prependData: `
@import "@/assets/styles/_variable.scss";
`
}
}
}
};複製程式碼
alias 別名設定
同時解決問題alias 配置的路徑別名,在 vscode 中報錯模組查詢失敗
和問題vue-cli 配置了 resolve alias 來宣告的路徑別名,在引用了 ts 後,vscode 會報錯不能識別、模組查詢失敗
。其中,vscode 報錯在 win 環境還需要一個外掛安裝,解決方案見下邊
vue.config.js 配置
// vue.config.js
module.exports = {
chainWebpack: config => {
// 別名配置
config.resolve.alias
.set("comp", resolve("src/components"))
.set("css", resolve("src/assets/styles"));
// ...同上,路徑核對好就行
}
};複製程式碼
jsconfig.json 配置。注意這裡的名字要和上邊 set 後邊的名字保持一致
// jsconfig.json
{
"compilerOptions": {
"paths": {
"@/*": [
"src/*" // 這個本來就有
],
// 後邊追加
"comp/*": [
"src/components/*"
],
"css/*": [
"src/assets/styles/*"
],
// ... 同上,路徑核對好就行
},
}
};複製程式碼
SCF 使用設定的別名
// main.ts
import MyError from "view/error/Error.vue";複製程式碼
/* SCF單頁中scss路徑引用 */
@import "css/_variable.scss";複製程式碼
請求介面的代理設定
vue.config.js 配置
// vue.config.js
module.exports = {
devServer: {
proxy: {
"/api": {
target: "http://11.11.11.111/", // 示例ip地址,也可以填域名,需要的是後端介面地址的相同部分
changeOrigin: true,
pathRewrite: {
"^/api": ""
}
}
}
}
};複製程式碼
axios 請求地址時的寫法:
注意/api
一定要有,且在路徑的最前邊,代替相同的路徑。
axios
.get("/api/wo/de/di/zhi") // 前邊的'/api'一定要有,它代表的就是vue.config.js中proxy.target的路徑
.then(() => {
// 介面成功...
});複製程式碼
本地服務域名修改
vue.config.js 配置
// vue.config.js
module.exports = {
devServer: {
disableHostCheck: true, // 用域名代替localhost,禁用主機檢查
host: "www.haha.com"
// 另外埠也可以在這裡改,只不過我寫到了package.json總,見下邊
}
};複製程式碼
package.json dev 命令的配置
{
"scripts": {
"dev": "npm run serve",
"serve": "vue-cli-service serve --port 80 --open", # 埠設定為80,--open執行完畢後自動開啟地址
}
}複製程式碼
本地 host 配置
127.0.0.1 www.haha.com # 這裡注意和vue.config.js中的host的值對應複製程式碼
此時,npm run dev
成功後,瀏覽器跑專案輸入地址http://www.haha.com
即可
vue + ts 在 vscode 中的問題
vue-cli 配置了 resolve alias 來宣告的路徑別名,在引用了 ts 後,vscode 會報錯不能識別、模組查詢失敗:
1、擴充套件商店安裝外掛 - Path Intellisense
2、配置程式碼(vscode setting.json 中設定)
"path-intellisense.mappings": {
"@": "\${workspaceRoot}/src"
}複製程式碼
3、在 package.json 統計目錄下建立 jsconfig.json 檔案,並填入下邊程式碼
// jsconfig.json
{
"compilerOptions": {
"paths": {
"@/*": ["src/*"]
}
}
}複製程式碼
持續更新中...