前言
已經用TS寫了半年了多了,然后總結下如何如何在vue項目中使用ts吧,好復習
主要內容包含:組件,axios請求,vuex,父子組件傳值,Mixins等。
vue中常用功能點代碼用js/ts寫法分開書寫,這樣會好區別和理解
一、新建項目
vue create vue-ts-admin
按步驟開始安裝,安裝過程中選擇TypeScript,vuex,路由router; 使用npm run serve啟動項目
1. 在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的封裝,在之前的博客中已經講述過了,就不在封裝,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>
附: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>
總結
還是以vue2.0的版本為主的,后面在看看vue3 的寫法 ,其實也差不多吧,用法 繼續折騰吧 少年
