TypeScript(03)——vue全家桶中如何使用TypeScript語法合集

前言

之前已經學過TypeScript函數,class類其他語法及ts在html頁面中如何使用案例
這篇學習如何在vue項目中使用ts,其中包含:組件,axios請求,vuex,父子組件傳值,Mixins等。。。
爲了更方便理解,我把在vue中常用功能點代碼用js/ts寫法分開書寫,如果你對vue比較熟悉的,可以直接跳轉到最後,最後附有demo源碼;

正文

一、準備工作

1.1安裝vue cil4

npm install -g @vue/cli
npm install -g @vue/cli-service-global

1.2新建項目

vue create vue-ts-sticky

按步驟開始安裝,安裝過程中選擇TypeScript,vuex,路由router;
使用npm run serve啓動項目

1.3 在vue中書寫ts的必備插件!

vue-class-component 強化 Vue 組件,使用裝飾器語法使 Vue 組件更好的跟TS結合使用。
vue-property-decorator在 vue-class-component 的基礎上增加了更多與 Vue 相關的裝飾器,使Vue組件更好的跟TS結合使用。

npm i vue-class-component -s-d
npm i  vue-property-decorator -s-d

二、ts寫vue單文件寫法

2.1單頁面格式怎麼寫

vue單頁面的格式的寫法不變,同樣由template、script、style組成;
唯一區別:<script src="ts">

<template>
  <div class="hello"></div>
</template>
<script src="ts"></script>
<style scoped></style>

2.1.1 vue項目中的mian.ts及app.vue

main.ts寫法

import Vue from 'vue'
import App from './App.vue'
//vuex
import store from './store'
// 路由
import router from './router'
Vue.config.productionTip = false

new Vue({
  store,
  router,
  render: h => h(App)
}).$mount('#app')

app.vue寫法

<template>
  <div id="app">
    <!-- 使用路由的方法,也可以不用路由,直接引子組件 -->
    <router-view></router-view>
  </div>
</template>

<script lang="ts">
//注意點:1.下面的代碼必須在每個頁面都中引入
import { Component, Vue } from 'vue-property-decorator';
@Component
//注意點:2.每個頁面都有組件名稱:App/自定義
export default class App extends Vue {

}
</script>

2.2 如何在Data雙向綁定值

  • js寫法
<template>
  <div class="hello">
    <h1>{{msg}}</h1>
  </div>
</template>
<script>
export default {
   data() {
    return {
      msg: "",
    };
  },
}
</script>
  • ts寫法
<template>
  <div class="hello">
    <h1>{{msg}}</h1>
  </div>
</template>
<script src="ts">
import { Component, Vue, } from "vue-property-decorator";
@Component
export default class Home extends Vue {
  //注意點:3.public是公用的意思,可省略;沒有data,return,直接放要綁定的值
  public msg!: number | string;
  // msg!: number | string;
}
</script>

2.3 如何引入子組件及組件傳值

  • js寫法
<template>
  <div class="hello">
    <MenuBar :setMsg="msg"/>
  </div>
</template>
<script>
import MenuBar from "../components/MenuBar.vue";
export default {
  props: {
    // 父組件的值
    fatherMsg: {
      type: String
    }
  },
  components: {
    MenuBar
  },
  data() {
    return {
      msg: "",
    };
  },
}
</script>
  • ts寫法
<template>
  <div class="hello">
    <MenuBar :setMsg="msg" />
  </div>
</template>
<script src="ts">
import { Component, Vue, } from "vue-property-decorator";
import MenuBar from "../components/MenuBar.vue";
@Component({
  components: {
    MenuBar
  },
})
export default class Home extends Vue {
  // 父組件的傳遞過來的值
  @Prop() private fatherMsg!: string;
  //傳遞給子組件的值
  public msg!: number | string;
}
</script>

2.4 生命週期的用法

  • js寫法
<template>
  <div class="hello">
    <h1>{{msg}}</h1>
  </div>
</template>
<script>
var data = {name: "小明",age: 18};
export default {
   data() {
    return {
      msg: "",
    };
  },
  created() {
    this.msg = data.name + data.age + "歲";
  },
}
</script>
  • ts寫法
<template>
  <div class="hello">
    <h1>{{msg}}</h1>
  </div>
</template>
<script  src="ts">
import { Component, Vue, } from "vue-property-decorator";
var data = {name: "小明",age: 18};
@Component
export default class Home extends Vue {
  public msg!: number | string;
  created(): void {
    console.log("created");
    this.msg = data.name + data.age + "歲";
  }
  beforeCreate(): void {
    console.log("beforecreate");
  }
  beforeMount(): void {
    console.log("beforemounted");
  }
  mounted(): void {
    console.log("mounted");
  }
}
</script>

2.5 methods方法

  • js寫法
<template>
  <div class="hello">
    <h1>{{count}}</h1>
    <button class="btn" @click="addCount">add</button>
  </div>
</template>
<script>
var data = {name: "小明",age: 18};
export default {
   data() {
    return {
      count: 0,
    };
  },
  methods: {
    addCount() {
      return this.count++;
    }
  }
}
</script>
  • ts寫法
<template>
  <div class="hello">
    <h1>{{count}}</h1>
    <button class="btn" @click="addCount">add</button>
  </div>
</template>
<script  src="ts">
import { Component, Vue, } from "vue-property-decorator";
var data = {name: "小明",age: 18};
@Component
export default class Home extends Vue {
  public count: number = 0;
  //   方法也是直接寫到外層
  addCount(): number {
    return this.count++;
  }
}
</script>

2.6 計算屬性(computed)和監聽屬性(watch)

  • js寫法
<template>
  <div class="hello">
  <h1>計算屬性:{{countChange}},結果+2:{{watchMsg}}</h1>
    <button class="btn" @click="addCcountChange">計算屬性:add</button>
    <h1>監聽:{{count}},結果+1:{{watchMsg}}</h1>
    <button class="btn" @click="addCount">監聽add</button>
  </div>
</template>
<script>
var data = {name: "小明",age: 18};
export default {
   data() {
    return {
      count: 0,
      watchMsg: ""
    };
  },
  watch: {
    count: {
      handler(newVal, oldVal) {
        if (newVal < 10) {
          this.watchMsg = "我是數字" + newVal;
        } else {
          this.watchMsg = "我會繼續增長";
        }
      },
      immediate: true
    },
    watchMsg: {
      handler(newVal, oldVal) {
        console.log(newVal);
      },
      immediate: true
    }
  },
  computed: {
    countChange: {
      get() {
        return this.count;
      },
      set(val) {
        this.count = val + 1;
      }
    }
  },
  methods: {
   addCcountChange() {
      return this.countChange;
    },
    addCount() {
      return this.count++;
    }
  }
}
</script>
  • ts寫法
<template>
  <div class="hello">
    <h1>計算屬性:{{countChange}},結果+2:{{watchMsg}}</h1>
    <button class="btn" @click="addCcountChange">計算屬性:add</button>
    <h1>監聽:{{count}},結果+1:{{watchMsg}}</h1>
    <button class="btn" @click="addCount">監聽add</button>
  </div>
</template>
<script  src="ts">
// 注意1.導入Watch
import { Component, Vue,Watch } from "vue-property-decorator";
var data = {name: "小明",age: 18};
@Component
export default class Home extends Vue {
  public count: number = 0;
  public watchMsg: string = "開始";
    //   計算屬性
  get countChange(): number {
    return this.count;
  }
  set countChange(val) {
    this.count = val + 1;
  }
  // 注意2. 監聽多個就導入多個Watch,命名自定義 clgMsg(newVal: string)
  @Watch("count")
  Count(newVal: number) {
    if (newVal < 10) {
      this.watchMsg = "我是數字" + newVal;
    } else {
      this.watchMsg = "我會繼續增長";
    }
  }
  @Watch("watchMsg")
  clgMsg(newVal: string) {
    console.log(newVal);
  }
   //   方法
  addCcountChange(): number {
    return this.countChange++;
  }
  addCount(): number {
    return this.count++;
  }
}
</script>

2.7 Mixins混入如何使用

Mixins混入是公共方法同一調用;

2.7.1 Mixins文件的寫法

  • js寫法
export const TestMixins = {
    data(){
      return{
        form:{}
      }
    },
    methods:{
      handleSubmit(name): {
        return new Promise((resolve) => {
            resolve()
        })
      }
      handleReset(name){
        console.log(name)
        return name
      }
    }
}

  • TS寫法
//必須引入
import { Component, Vue, } from "vue-property-decorator";
// 導出模塊
declare module 'vue/types/vue' {
    interface Vue {
        form: Object
        handleSubmit(name: any): Promise<any>
        handleReset(name: any): void
    }
}
@Component
export default class TestMixins extends Vue {
    form: Object = {}
    handleSubmit(name: any): Promise<any> {
        return new Promise((resolve) => {
            resolve()
        })
    }
    handleReset(name: any): void {
        console.log(name)
        return name
    }
}

2.7.2 調用Mixins的vue文件寫法

  • js寫法
<template>
  <div class="hello">
     <h1>{{handleReset("測試js-mixins")}}</h1>
  </div>
</template>
<script>
import TestMixins from "../assets/mixin";
export default {
   mixins: [TestMixins],
   data() {
    return {
      count: 0,
    };
   },
}
</script>
  • ts寫法
<template>
  <div class="hello">
      <h1>{{handleReset("測試TS-mixins222")}}</h1>
  </div>
</template>
<script  src="ts">
import TestMixins from "../assets/mixin";
import { Component, Vue, Mixins} from "vue-property-decorator";
// 寫在@Component內
@Component({
  mixins: [TestMixins]
})
export default class Home extends Vue {
  public count: number = 0;
}
</script>

2.8 路由vue-Router及路由守衛

2.8.1 安裝導入路由—— mian.ts

import Vue from 'vue'
import App from './App.vue'
import store from './store'
import router from './router'
//1.導入組件
import Component from 'vue-class-component'
Vue.config.productionTip = false

// 1.全局的路由守衛,js和ts的寫法一致;
// 2.組件內路由守衛,如果要在組件內部使用路由監聽,路由鉤子beforeRouteEnter,beforeRouteLeave,beforeRouteUpdate不生效。所以在此註冊;
Component.registerHooks([
  'beforeRouteEnter',//進入路由之前
  'beforeRouteLeave',//離開路由之前
  'beforeRouteUpdate'
])

new Vue({
  store,
  router,
  render: h => h(App)
}).$mount('#app')

2.8.2 路由index文件寫法—— router/index.ts

import Vue from 'vue';
import Router from 'vue-router';
import stickyNotePage from '../page/stickyNotePage.vue';

export default new Router({
    // history模式
    mode: 'history',
    base: process.env.BASE_URL,
    routes: [
        // 常規模塊加載
        {
            path: '/',
            name: 'stickyNotePage',
            component: stickyNotePage,
        },
        // 路由懶加載寫法
        {
            path: '/hello',
            name: 'hello',
            component: () => import(/* webpackChunkName: "hello" */'../page/HelloWorld.vue'),
        },
        {
            path: '/learn',
            name: 'learn',
            component: () => import(/* webpackChunkName: "learn" */'../page/learn.vue'),
        },
    ],
})
Vue.use(Router);

2.8.3 在頁面中如何使用路由守衛

全局的路由守衛,js和ts的寫法一致;
// 全局守衛
import router from './router'
router.beforeEach((to, from, next) => {    
    console.log(to.path);
    next()    
})
// 全局後置鉤子
router.afterEach((to,from)=>{
  alert("after each");
})
組件內路由守衛
  • js寫法
<template>
  <div class="hello">
    <h1>{{count}}</h1>
    <button class="btn" @click="addCount">add</button>
  </div>
</template>
<script>
var data = {name: "小明",age: 18};
export default {
   data() {
    return {
      count: 0,
    };
  },
   // 進入路由觸發
  beforeRouteEnter(to,from,next)=>{
    console.log("beforeRouteEnter111");
    next();
  }
  beforeRouteUpdate(to,from,next)=>{
    console.log("beforeRouteUpdate111");
    next();
  }
  // 離開路由觸發
  beforeRouteLeave(to,from,next)=>{
    console.log("beforeRouteLeave111");
    next();
  }

}
</script>
  • ts寫法
<template>
  <div class="hello">
    <h1>{{count}}</h1>
  </div>
</template>
<script  src="ts">
import { Component, Vue, } from "vue-property-decorator";
@Component
export default class Home extends Vue {
  public count: number = 0;
   // 進入路由觸發
  beforeRouteEnter(to: any, from: any, next: () => void): void {
    console.log("beforeRouteEnter111");
    next();
  }
  beforeRouteUpdate(to: any, from: any, next: () => void): void {
    console.log("beforeRouteUpdate111");
    next();
  }
  // 離開路由觸發
  beforeRouteLeave(to: any, from: any, next: () => void): void {
    console.log("beforeRouteLeave111");
    next();
  }
}
</script>

2.9 vuex

2.9.1 vuex/scr/store/index.ts寫法

js、ts寫法除了類型判斷其他區別不大;

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    isShowEdit: false,
    onlySticky: null,
  },
  mutations: {
    SHOW_EDIT(state: any, editMemo: any) {
      console.log(editMemo)
      state.onlySticky = editMemo;
      state.isShowEdit = true;
    }
  },
  actions: {
  },
  modules: {
  }
})

2.9.2 vuex調用單頁面的寫法

  • js寫法
<template>
  <div class="hello">
    <button @click="showEdit('vuex')">測試vuex</button>
  </div>
</template>
<script>
import { mapActions, mapState, mapGetters, mapMutations } from "vuex";
export default {
   data() {
    return {
      stickyList:[],
    };
   },
   created() {
    this.stickyList = this.$store.state.onlySticky;
  },
    methods: {
    ...mapMutations(["SHOW_EDIT"]),
    showEdit(item) {
      this.$store.commit("SHOW_EDIT", item);
    },
  }
}
</script>
  • ts寫法
<template>
  <div class="hello">
     <button @click="showEdit('vuex')">測試vuex</button>
  </div>
</template>
<script  src="ts">
import { Component, Vue,} from "vue-property-decorator";
import ItemData from "../model/ItemData"; //導入類
// 寫在@Component內
@Component
export default class Home extends Vue {
  stickyList: [ItemData] = this.$store.state.onlySticky;
 // vuex,如果this.$store一直報錯,則在單頁面引入 import Vuex from 'vuex'
  showEdit(item) {
    this.$store.commit("SHOW_EDIT", item);
  }
}
</script>

2.10 axios請求數據

2.10.1 main.ts

import axios from 'axios'
Vue.prototype.$axios = axios;

關於axios的封裝,在之前的博客中已經講述過了,就不在封裝了,有需要查看vue+axios封裝請求,ts語法除了類型其他區別不大,這裏直接使用的是掛載axios和引入axios的方式,

2.10.2 單頁使用

  • js寫法
<template>
  <div class="hello">
    axios請求
  </div>
</template>
<script>
export default {
   data() {
    return {
    };
   },
   created() {
    // 請求地址  https://www.foobar.com/my-app/user/add
    const url1 = "https://www.foobar.com/my-app/user/add";
    this.$axios.get(url1, { params: { type: "js" } }).then(res => {
      console.log(res);
    });
    // 使用vue代理
    const url2 = "/my-app/user/add";
    this.$axios.get(url2, { params: { type: "Ts" } }).then(res => {
      console.log(res);
    });
  },
}
</script>
  • ts寫法
<template>
  <div class="hello">
    axios請求
  </div>
</template>
<script  src="ts">
import { Component, Vue,} from "vue-property-decorator";
import axios from "axios";
// 寫在@Component內
@Component
export default class Home extends Vue {
   created(): void {
    const url1 = "https://www.foobar.com/my-app/user/add";
    axios.get(url1, { params: { type: "js" } }).then((res: any) => {
      console.log(res);
    });
    // 使用vue代理
    const url2 = "/my-app/user/add";
    axios.get(url2, { params: { type: "Ts" } }).then((res: any) => {
      console.log(res);
    });
  }
}
</script>

2.11 使用less/sass/scss

vue/cil4.2 已經內置了各種的css-loader,所以直接下載npm包即可;如果有特殊的需求,則需要配置vue.config.js的參數,當然這個在此不多論述,會另開篇講vue/cil4的配置;

附:js常規書寫vue–demo源碼

<template>
  <div class="hello">
    <MenuBar />
    <router-link :to="{path:'hello'}">測試路由守衛-->去helloword</router-link>
    <h1>{{handleReset("測試mixins222")}}</h1>
    <h1>{{msg}}</h1>
    <h1>{{fatherMsg}}</h1>
    <h1>計算屬性:{{countChange}},結果:{{watchMsg}}</h1>
    <button class="btn" @click="addCcountChange">計算屬性:add</button>
    <h1>監聽:{{count}},結果:{{watchMsg}}</h1>
    <button class="btn" @click="addCount">監聽add</button>
    <button @click="showEdit('vuex')">測試vuex</button>
  </div>
</template>
<script>
import MenuBar from "../components/MenuBar.vue";
import TestMixins from "../assets/mixin";
import { mapActions, mapState, mapGetters, mapMutations } from "vuex";

var data = {
  name: "小明",
  age: 18
};
export default {
  props: {
    fatherMsg: {
      type: String
    }
  },
  mixins: [TestMixins],
  components: {
    MenuBar
  },
  data() {
    return {
      msg: "",
      count: 0,
      watchMsg: "",
      stickyList: []
    };
  },
  created() {
    this.msg = data.name + data.age + "歲";
    this.stickyList = this.$store.state.onlySticky;
  },
  mounted() {
    // 頁面內部使用路由守衛
    this.$router.beforeEach((to, from, next) => {
      console.log("我要去" + from.name);
      next();
    });
    // axios請求
    // 請求地址  https://www.foobar.com/my-app/user/add
    const url1 = "https://www.foobar.com/my-app/user/add";
    this.$axios.get(url1, { params: { type: "js" } }).then(res => {
      console.log(res);
    });
    // 使用代理
    const url2 = "/my-app/user/add";
    this.$axios.get(url2, { params: { type: "Ts" } }).then(res => {
      console.log(res);
    });
  },
  watch: {
    countChange: {
      handler(newVal, oldVal) {
        if (newVal < 5) {
          this.watchMsg = "我是數字" + newVal;
        } else {
          this.watchMsg = "我會繼續增長";
        }
      },
      immediate: true
    },
    watchMsg: {
      handler(newVal, oldVal) {
        console.log(newVal);
      },
      immediate: true
    }
  },
  computed: {
    countChange: {
      get() {
        return this.count;
      },
      set(val) {
        this.count = val + 2;
      }
    }
  },
  methods: {
    ...mapMutations(["SHOW_EDIT"]),
    showEdit(item) {
      this.$store.commit("SHOW_EDIT", item);
    },
    addCcountChange() {
      return this.countChange++;
    },
    addCount() {
      return this.count++;
    }
  }
};
</script>

demo-上述代碼用Ts書寫vue


<template>
  <div class="hello">
    <MenuBar />
    <router-link :to="{path:'learn'}">測試路由守衛-->去learn</router-link>
    <h1>{{handleReset("測試mixins111")}}</h1>
    <h1>{{msg}}</h1>
    <h1>{{fatherMsg}}</h1>
    <h1>計算屬性:{{countChange}},結果+2:{{watchMsg}}</h1>
    <button class="btn" @click="addCcountChange">計算屬性:add</button>
    <h1>監聽:{{count}},結果+1:{{watchMsg}}</h1>
    <button class="btn" @click="addCount">監聽add</button>
    <button @click="showEdit('vuex')">測試vuex</button>
  </div>
</template>

  <!-- 使用ts書寫js -->
<script lang="ts">
import router from "../router";
import { Component, Prop, Vue, Watch, Mixins } from "vue-property-decorator";
import MenuBar from "../components/MenuBar.vue";
import TestMixins from "../assets/mixin";
import ItemData from "../model/ItemData"; //導入類
import axios from "axios";
var data = {
  name: "小明",
  age: 18
};
// 組件
@Component({
  components: {
    MenuBar
  },
  mixins: [TestMixins]
})
export default class Home extends Vue {
  @Prop() private fatherMsg!: string;
  //   掛載值
  public msg!: number | string;
  public count: number = 0;
  public watchMsg: string = "開始";
  stickyList: [ItemData] = this.$store.state.onlySticky;
  //   聲明週期
  created(): void {
    console.log("created");
    this.msg = data.name + data.age + "歲";

    const url1 = "https://www.foobar.com/my-app/user/add";
    axios.get(url1, { params: { type: "js" } }).then((res: any) => {
      console.log(res);
    });
    // 使用vue代理
    const url2 = "/my-app/user/add";
    axios.get(url2, { params: { type: "Ts" } }).then((res: any) => {
      console.log(res);
    });
  }
  beforeCreate() {
    console.log("beforecreate");
  }

  beforeMount() {
    console.log("beforemounted");
  }
  mounted() {
    console.log("mounted");
    // 頁面內部使用路由守衛
    // router.beforeEach((to, from, next) => {
    //   console.log("我來自" + from.name);
    //   next();
    // });
  }
  // 進入路由觸發
  beforeRouteEnter(to: any, from: any, next: () => void): void {
    console.log("beforeRouteEnter111");
    console.log(to.path);
    next();
  }

  beforeRouteUpdate(to: any, from: any, next: () => void): void {
    console.log("beforeRouteUpdate111");
    next();
  }
  // 離開路由觸發
  beforeRouteLeave(to: any, from: any, next: () => void): void {
    console.log("beforeRouteLeave111");
    next();
  }
  //   計算屬性
  get countChange(): number {
    return this.count;
  }
  set countChange(val) {
    this.count = val + 1;
  }
  //   監聽
  @Watch("count")
  Count(newVal: number) {
    if (newVal < 10) {
      this.watchMsg = "我是數字" + newVal;
    } else {
      this.watchMsg = "我會繼續增長";
    }
  }
  @Watch("watchMsg")
  clgMsg(newVal: string) {
    console.log(newVal);
  }
  //   方法
  addCcountChange(): number {
    return this.countChange++;
  }
  addCount(): number {
    return this.count++;
  }
  // vuex
  showEdit(item) {
    this.$store.commit("SHOW_EDIT", item);
  }
}
</script>


<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

結語

經過以上的學習,對ts在vue項目中的常規用法也應該熟悉起來了,好了,在VUE/CIL項目中的用法已經學完了,下一篇講:使用VueCil4 + TypeScript + Vuex + LocalStorage 完成的本地便籤案例;

附:TS學習筆記合集:

如果本文對你有幫助的話,請不要忘記給我點贊打call哦~o( ̄▽ ̄)do
over~有其他問題請留言

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章