主讲:云隐 & 路白
一、项目需要使用 typeScript
1. 引入和使用
webpack 打包配置
webpack 打包配置 => vue-cli - vue init/create ${myProject} ${template} => 配置 webpack => 编译时
entry- 入口extentions加上ts文件area- 用于处理尝试的数据尾缀列表loaders-ts-loader,增加对于ts的处理 => 工程化
TS 配置
tsconfig.json 文件
2. vue / vuex + typescript
<template>
<div>
<vueComponent />
</div>
</template>
// 1. 定义组件的方式上: 形式上 - extends
// const Component = {
// TS 无法断定内部为 vue 组件,需要额外做申明处理 - Vue.prototype.xxxx
// }
// 申明当前组件模块 Vue.component or Vue.extend
import Vue from 'vue'
const Component = Vue.extend({
// 类型推断
})
// 2. 全面拥抱面向对象 - 官方 vue-class-component
import Component from 'vue-class-component'
// @Component 本质 —— 类装饰器 => 利用装饰器,统一描述 vue 模板等概念
@Component({
template: '<vueComponent />'
})
export default class myComponent extends Vue {
message: string = 'Hello'
onClick(): void {
console.log(this.message);
}
}
// 3. 申明 - 利用 ts 的额外补充模块 declare => 实现独立模块的声明,使之可以被独立引用
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
// 补充模块 - 通常使用 .d.ts 来做申明描述
declare module '/typings/vuePlugin.d.ts' {
interface Vue {
myProps: string
}
}
// 实例中使用
let vm = new Vue()
console.log(vm.myProps)
// 4. props - 提供 propType 原地声明复合变量
import { propType } from 'vue'
interface customPayload {
str: string,
number: number,
name: string
}
const Component = Vue.extend({
props: {
name: String, // 字符串类型
// 普通对象类型
success: {
type: String
},
payload: {
type: Object as propType<customPayload>
},
callback: {
type: Function as propType<() => void>
}
}
})
// 5. computed 以及 method 中包含 this 且有 return 的方法 需要声明返回类型
computed: {
getMsg(): string {
return this.click() + "!"
}
}
methods: {
click(): string {
return this.message + 'zhaowa'
}
}
// 6. vuex 的接入 ts - 声明使用
// vuex.d.ts 声明模块 - ComponentCustomProperties
import { ComponentCustomProperties } from 'vue'
declare module '@vue/runtime-core' {
interface State {
count: number
}
interface ComponentCustomProperties {
$store: Store<State>
}
}
// 7. api 形式编码实现 - 官方推荐
// store.ts
import { InjectionKey } from 'vue'
import { createStore, Store } from 'vuex'
export interface State {
count: number
}
export const key: InjectionKey<Store<State>> = Symbol()
export const store = createStore<State>({
state: {
count: 0
}
})
// ##################################
// main.ts
import { createApp } from 'vue'
import { store, key } from './store'
const app = createApp({
// 传入参数
})
// 利用了provider & inject
app.use(store, key) // => 传入injection Key => vue高级使用里会提到vue.use
app.mount('#app')
// ####################################
// 消费方
import { useStore } from 'vuex'
import { key } from './store'
export default {
const store = useStore(key)
store.state.count
}
// 标准接口形式的功能引入,核心利用了 vue 的 provide & inject
// 8. vuex 面向对象 - 使用 vuex-class
import { State, Action, Getter } from "vuex-class"
export default class App extends Vue {
// 属性装饰器,整合了 store 状态
@State login: boolean;
// 事件装饰器,整合了 store 方法
@Action setInit: () => void;
get isLogin: boolean;
mounted() {
this.setInit();
}
}
二、项目实战
初始化配置
vue.config.js
const { defineConfig } = require('@vue/cli-service');
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
hot: true,
open: true,
host: 'localhost',
},
});
src\router\index.ts
import Vue, { AsyncComponent } from 'vue';
import VueRouter, { RouteConfig } from 'vue-router';
const Main: AsyncComponent = (): any => import('@/views/MainView.vue');
Vue.use(VueRouter);
const routes: Array<RouteConfig> = [
{
path: '/',
redirect: '/index'
},
{
path: '/index',
name: 'main',
component: Main
}
];
const router = new VueRouter({
routes
});
export default router;
src\views\MainView.vue
<template>
<div class="main">
<div class="header">北京</div>
<div class="main-content">
<plates></plates>
<prodList></prodList>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import prodList from '@/components/prodList.vue';
import plates from '@/components/plates.vue';
@Component({
name: 'MainView',
components: {
prodList,
plates,
},
})
export default class MainView extends Vue {}
</script>
<style lang="less" scoped>
.main {
.header {
background-color: #ff1395;
padding: 20px;
width: 100%;
}
.main-content {
padding: 10px;
}
}
</style>
src\components\plates.vue
<template>
<div>plates</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
@Component
export default class Plates extends Vue {}
</script>
<style scoped lang="less"></style>
src\components\prodList.vue
<template>
<div>prodList</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
@Component
export default class ProdList extends Vue {}
</script>
<style scoped lang="less"></style>
yarn serve启动项目
初始化 store
src\store\index.ts
import Vue from 'vue';
import Vuex from 'vuex';
import actions from './actions';
import mutations from './mutations';
Vue.use(Vuex);
let state = {};
export default new Vuex.Store({
state,
actions,
mutations,
});
src\store\actions.ts
const actions = {};
export default actions;
src\store\mutations.ts
const mutations = {};
export default mutations;
src\store\types.ts
/**
* 类型映射
*/
export default {
SET_PLATES: 'SET_PLATES',
SET_PROD_LIST: 'SET_PROD_LIST',
};
- 设置
store的命名空间
declare namespace StoreState {
export interface plate {
// 板块
}
export interface prod {
// 商品
}
}
修改 store
src\store\actions.ts
import { ActionTree } from 'vuex';
import axios from 'axios';
import TYPES from './types';
const actions: ActionTree<any, any> = {
// 全局初始化
async initAjax({ dispatch }) {
// 获取板块
dispatch('getPlates');
// 获取列表
dispatch('getProdList');
},
// 获取板块
async getPlates({ state, commit }) {
const res: Ajax.AjaxResponse = await axios
.get('/plates')
.then(res => res.data)
.catch((e: string) => console.log(e));
if (res && res.code === 200) {
commit(TYPES.SET_PLATES, res.result.list);
}
},
// 获取板块
async getProdList({ state, commit }) {
const res: Ajax.AjaxResponse = await axios
.get('/prodList')
.then(res => res.data)
.catch((e: string) => console.log(e));
if (res && res.code === 200) {
commit(TYPES.SET_PROD_LIST, res.result.list);
}
},
};
export default actions;
src\store\mutations.ts
import { MutationTree } from 'vuex';
import TYPES from './types';
const mutations: MutationTree<any> = {
[TYPES.SET_PLATES](state, plates): void {
state.plates = plates;
},
[TYPES.SET_PROD_LIST](state, prodList): void {
state.prodList = prodList;
},
};
export default mutations;
src\store\index.ts
import Vue from 'vue';
import Vuex from 'vuex';
import actions from './actions';
import mutations from './mutations';
Vue.use(Vuex);
interface State {
plates: StoreState.plate[];
prodList: StoreState.prod[];
}
let state: State = {
plates: [],
prodList: [],
};
export default new Vuex.Store({
state,
actions,
mutations,
});
声明 ajax、store
src\typings\shims-ajax.d.ts
declare namespace Ajax {
// 返回
export interface AjaxResponse {
data: AjaxResponse;
}
// 返回接口数据
export interface AjaxResponse {
// 状态码
code: number;
// 数据
result: any;
// 提示信息
message?: string;
}
}
src\typings\shims-store.d.ts
declare namespace StoreState {
export interface plate {
// 板块
url: string;
imgUrl: string;
name: string;
}
export interface prod {
// 商品
url: string;
imgUrl: string;
prodName: string;
desc: string;
price: string;
}
}
Mock 数据
src\assets\mock\index.js
import Mock from 'mockjs';
import plates from './plates';
import prodList from './prod_list';
Mock.mock('/plates', 'get', () => {
return {
code: 200,
result: plates,
};
});
Mock.mock('/prodList', 'get', () => {
return {
code: 200,
result: prodList,
};
});
src\assets\mock\plates.js
export default {
list: [
[
{
url: '',
imgUrl:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAABxCAYAAAAea2caAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA39pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpjYWY3NTgyYS03ZGZhLTljNDgtYmJhYS0yZGMwNjQyNzcwOWQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NUM0QkE3NzdEMzk3MTFFOTg1OURDMThENEU4QUU4NkQiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NUM0QkE3NzZEMzk3MTFFOTg1OURDMThENEU4QUU4NkQiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjZiM2YxYzU1LTc4YWUtNWE0ZS1iMzhiLTA1OWY5NmZmNjUxMCIgc3RSZWY6ZG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjlhNWE3MDMwLTA2MzUtMTM0YS04MTlkLTVkMzc1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',
name: '蔬菜',
},
{
url: '',
imgUrl:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAABxCAYAAAAea2caAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA39pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpjYWY3NTgyYS03ZGZhLTljNDgtYmJhYS0yZGMwNjQyNzcwOWQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NUM0QkE3NzdEMzk3MTFFOTg1OURDMThENEU4QUU4NkQiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NUM0QkE3NzZEMzk3MTFFOTg1OURDMThENEU4QUU4NkQiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjZiM2YxYzU1LTc4YWUtNWE0ZS1iMzhiLTA1OWY5NmZmNjUxMCIgc3RSZWY6ZG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjlhNWE3MDMwLTA2MzUtMTM0YS04MTlkLTVkMzc1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',
name: '蛋类',
},
{
url: '',
imgUrl:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAABxCAYAAAAea2caAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA39pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpjYWY3NTgyYS03ZGZhLTljNDgtYmJhYS0yZGMwNjQyNzcwOWQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NUM0QkE3NzdEMzk3MTFFOTg1OURDMThENEU4QUU4NkQiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NUM0QkE3NzZEMzk3MTFFOTg1OURDMThENEU4QUU4NkQiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjZiM2YxYzU1LTc4YWUtNWE0ZS1iMzhiLTA1OWY5NmZmNjUxMCIgc3RSZWY6ZG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjlhNWE3MDMwLTA2MzUtMTM0YS04MTlkLTVkMzc1NjI3NmNiNiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PiCwwAYAAAiYSURBVHja7F0JjBRFFK3VBdEgKtGNIHLIjSJGuXZBIChBhI3ACmqMMcYQxCuaaLwVBVE0mhBNOKKJByqXizCCKELk2kUiBsQYQEFQQFHkNiLCjv+l/8R1qO6qnume6en5L3mZpHq6Z7pe969fv6p+lSSTSSWIL06TKhCBBSKwQAQWiMACEVggAgtEYBFYIAILCgKlmZ5YUZvoTB9Dib2J7YjnSnWGgn3ErcQaYoK40+2LNeWVp5SV+I1Fk7A30MdjxF5S93nBUuJE4kobgUt9CHsRfbxDHCh1nFcMYs4ijiMezLoNJnH70McmETdSuJm4kdgxK4FJ3Db0sYh4ntRp5NCS+JmX/1NqEBcPwAfEc1y+coJNxQLiduJ+qfNAUUbsQKwiwvcpcRF5BnF0Jl70bR7O1HqYCWrYf6j3QIgkwWIHcR1xJrE7cTbxEs33RhH7E1dYm2h+e590OfwlsW99cQWh4ytiD7aUOjzttw3ux/1bnbgDSNxjUuc5x34PkQfSS9naj8DDNGUQtUrEzbvIo2018xK4r6ZsGom7W+o471jPjm06+vgRuIOmrFrqNjLQadHRj8C6fu9WqdfIYIumrKnvQEcafpN6jQystPA1mkTtr99Z8uioY8SpFTsHq4lf+zi/EfF6YhfiP8oJl35KPOnjGvAlehIbE7cpJyp3MAYCJwMX2CceJz5DbJhWjgoeSTxuIcwcYjNNM1Fp0Vw04Yehd1r5UeK9xLeL4TUPa8Af0ZbnNOIqfqPvsrjGdI24KedvisX54zXiKn6Tcf7ZInDmQNz0dI/jowznd2G6YbByj4+ncKPHMZw7SATOHO0Mx9sbjrcxHEfQ/WKP42cYjrt1A0VgSxwwHDeNOh22+A2v7/xN/DPL/yACe2Ct4fg6w/FvlBMWdQOiabuy/A9rReDMsZi4weUYvOeXDOcfMjhSk4l1hmtM8ji2kB8iEThDoPL7cyVvZpO5m7s9GNf8zuIamNg3VjmjV0fY7K9gB+01i/OXE69mMfeyRYCojyr3YH3sEGY/GG3kE8xMO/IzmJliNbNoIRPfRWCBCCwQgQUisCDfAlfUJmSBWTSAOH8TGz39dpMOkMiywiH/wGT3jZryZkH0gw9I/UobLBCBBSKwQAQWeMO3k1VTXlni0Y2SGs0NWhN/DPwN9hJXkFPsUPppTbvFREsbLBCBBSKwQAQWiMACEVggAovAAhFYIAILIojSIr53pJe4ldhCOWmJsKzmhAgcD2DQBAm166cdQgb7p8RExwO91Kk5pQZLGxwf6FJInBCB4wNd/o6VInA8cJVy5hWnY64IHF/zvJ09aRE4pgLPjevNRlHgS4mvKCcFA/qmQwO8djelT4k/P8vrIosBMrJ/S3ydeLn0g/+Phuz4YB+g9DzV2JCiE/H7kJyrn5U5648XkItkYtoDeg9xDXEqW4fjxfoGt1VOxh3MBnxP6ZOQ4z+Wh2yek1lcs7tLOZJzYzMNpHt6UZmTu8VGYFiN4crZ7wdv5cPE8w3nBJHGuKvSbyKVbftr+m8XEB9R/2W6HZbLes+lwIj5jlfO5opo85Ar0maeNVIfLQ3g94dryvYoJ01TNljA4pmAe0Vq5AR77UgTVVboAuP6CAF+pJzJ2kgv3Nzy3FXKGQy4UvnLD+0GXW6s6izNM3CYLcMI4ieW10P+7Elsvt9XTj6vghIYZvhBIvZVWqLM2WdTSGW4g6PSj28+CHHhpF2mKZ8T0P2e5IcYbygSsb6gnORrJjQg3qKcKBqSnd9ZKAKPIb7qw7FA3sg7lLNC/QFllwkvW+/5F/Z0gwbML5KhI9vtTdzdswEewDdUwNv2htVN8jMys5a7GcjOHlbAf4RL37dOhQdsQTCvnhNmu3Nr1wD8gtDf4EM+2i9sbvl5iOK25XZc1/6GjToWuqGPc3YWgol+U5nzNQPIFIPU//crfdaYsPq+vxO/CFlceM2V3Afua3kOHvRlhSAwnIbWxAnEPyxERrADEaWXfXjZthjp8vaeDOneG7CzBD9ioYW48Lo/5GDOoKCbjTC7SfuUsyMmnI37lHnBMoR+iLtTb7l4vX6BYcEemvJ5Idwv9oFAquKf2FnqZPg+0hsjlNmencBQEpTnItDxl3IC8O3Zq1xv8Qbczt0GRH66BWyeU3mng8RE7tOie3ShxYM/ngM/d1sGSSItcP2+Yioh+DUcFDAB/crpAQs8nx2foIAHF6NJjQ3fQ0xgHFu0Zy2aroITuD6Ws3gYVptpqPCeGf5Gc5c+ZdBjvyZTXMsmGNGuacp7L4rYCJwCzDC2kccYLcaAj7p4vJmgysU8Lwv4Hra5OE4L2MGqYCeqLh8VHJUB/13sYLVgR2VXvX7ylAyvOUBT9nHA5lmxtwxncjM/jOgiYo41BjfW5LtiozbxHQGSydxtKmOHJNPuTIMcmOcUJjAjh6jOyYKJ25tlXzWhcXKWqCJDnJeuoC+K8V4MxWFiwawQzLMInGcrsIhZtJDloyKwQAQWxEPgitqEJCONDkrCeIPLpF4jg7JsBdZtvtFJ6jUy0Gmx34/AWzRlVVKvkYFOi81+BNbFUcdQO9xS6jbvwChZpaa8xo/ACzVljYjVJPKZUsd5A5b5zNaUI7CT8CPwKhczjRXyq0TkvImLZaqtNMeW1pRX7rQWmL6MJ2KSy2GIXEsid5Y6zxnKPcQFntcVmmLR7xLHKmfQOh2YK7WJRK5mc47RmmOiQ6A4i9iFHarrPL6HJT7aJDIlyWTSFNzAE7OBKDuPRhM7iFcQD5HV9R/oYLuOp2ef1GXkgHVQ1yqPlSRWkSwSGWtlsGZmidRpZDCT31zPabfWoUoS+VfiEOXMhlwt9ZsXYOLeYna4MFnxiOkEYxvs0TZjUdcQ/jHMDW6qijt7bRhA8hbMn0aEag2Lu8fjJQxOYEFhQMaDRWCBCCwQgQUisCAE/CvAAFPaxb4X8//pAAAAAElFTkSuQmCC',
name: '肉禽',
},
{
url: '',
imgUrl:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAABxCAYAAAAea2caAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA39pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpjYWY3NTgyYS03ZGZhLTljNDgtYmJhYS0yZGMwNjQyNzcwOWQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NUM0QkE3NzdEMzk3MTFFOTg1OURDMThENEU4QUU4NkQiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NUM0QkE3NzZEMzk3MTFFOTg1OURDMThENEU4QUU4NkQiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjZiM2YxYzU1LTc4YWUtNWE0ZS1iMzhiLTA1OWY5NmZmNjUxMCIgc3RSZWY6ZG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjlhNWE3MDMwLTA2MzUtMTM0YS04MTlkLTVkMzc1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',
name: '水果',
},
{
url: '',
imgUrl:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAABxCAYAAAAea2caAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA39pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpjYWY3NTgyYS03ZGZhLTljNDgtYmJhYS0yZGMwNjQyNzcwOWQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NUM0QkE3NzdEMzk3MTFFOTg1OURDMThENEU4QUU4NkQiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NUM0QkE3NzZEMzk3MTFFOTg1OURDMThENEU4QUU4NkQiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjZiM2YxYzU1LTc4YWUtNWE0ZS1iMzhiLTA1OWY5NmZmNjUxMCIgc3RSZWY6ZG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjlhNWE3MDMwLTA2MzUtMTM0YS04MTlkLTVkMzc1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',
name: '牛奶',
},
],
],
};
src\assets\mock\prod_list.js
export default {
list: [
{
url: '',
imgUrl: 'http://www.zhaowaedu.com/static/img/img_heishan.1361392.jpg',
prodName: 'zhaowa大餐',
desc: '上海 徐汇',
price: '200',
},
{
url: '',
imgUrl: 'http://www.zhaowaedu.com/static/img/img_heishan.1361392.jpg',
prodName: 'zhaowa大餐',
desc: '上海 徐汇',
price: '200',
},
{
url: '',
imgUrl: 'http://www.zhaowaedu.com/static/img/img_heishan.1361392.jpg',
prodName: 'zhaowa大餐',
desc: '上海 徐汇',
price: '200',
},
{
url: '',
imgUrl: 'http://www.zhaowaedu.com/static/img/img_heishan.1361392.jpg',
prodName: 'zhaowa大餐',
desc: '上海 徐汇',
price: '200',
},
],
};
src\main.ts
import Vue from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from './router'
import store from './store'
import './assets/mock' // 引入 mock 数据
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
开发页面组件
src\components\plates.vue
<template>
<div class="plates">
<div class="plate-flex" v-for="(page, index) in plates" :key="index">
<div class="plate" v-for="(module, index) in page" :key="index">
<img :src="module.imgUrl" />
<div class="name">{{ module.name }}</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import { State } from 'vuex-class';
@Component({
name: 'Plates',
})
export default class Plates extends Vue {
@State('plates') plates!: StoreState.plate[];
}
</script>
<style scoped lang="less">
.plates {
.plate-flex {
display: flex;
.plate {
flex: 1;
text-align: center;
img {
height: 30px;
width: 30px;
}
}
}
}
</style>
src\components\prodList.vue
<template>
<div class="prod-list">
<div class="prod" v-for="(prod, index) in products" :key="index">
<div class="prod-logo">
<img :src="prod.imgUrl" alt="img" />
</div>
<div class="content">{{ prod.prodName }} - {{ prod.desc }} - {{ prod.price }}</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import { State } from 'vuex-class';
@Component
export default class ProdList extends Vue {
@State('prodList') prodList!: StoreState.prod[];
get products(): StoreState.prod[] {
const prodList = this.prodList;
return prodList;
}
}
</script>
<style scoped lang="less">
.prod-list {
padding-top: 20px;
.prod {
padding: 0 10px 10px 10px;
display: flex;
}
.prod-logo {
position: relative;
img {
width: 100px;
height: 120px;
}
}
}
</style>
src\views\MainView.vue
<template>
<div class="main">
<div class="header">北京</div>
<div class="main-content">
<plates></plates>
<prodList></prodList>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { Action } from 'vuex-class';
import prodList from '@/components/prodList.vue';
import plates from '@/components/plates.vue';
@Component({
name: 'MainView',
components: {
prodList,
plates,
},
})
export default class MainView extends Vue {
@Action('initAjax') initAjax!: () => void;
mounted() {
this.initAjax();
}
}
</script>
<style lang="less" scoped>
.main {
.header {
background-color: #ff1395;
padding: 20px;
width: 100%;
}
.main-content {
padding: 10px;
}
}
</style>
三、常见面试题
1、你觉得使用 TS 的好处是什么?
TypeScript是JavaScript的加强版,它给JavaScript添加了可选的静态类型和基于类的面向对象编程,它拓展了JavaScript的语法。所以TS的功能比JS只多不少;Typescript是纯面向对象的编程语言,包含类和接口的概念;TS在开发时就能给出编译错误,而JS错误则需要在运行时才能暴露;- 作为强类型语言,你可以明确知道数据的类型;
TS中有很多很方便的特性,比如可选链;
2、type 和 interface 的异同
重点:用 interface 描述数据结构,用 type 描述类型。
- 都可以描述一个对象或者函数
interface User {
name: string;
age: number;
}
interface SetUser {
(name: string, age: number): void;
}
type User = {
name: string;
age: number;
};
type SetUser = (name: string, age: number) => void;
- 都允许拓展(
extends)
interface 和 type 都可以拓展,并且两者并不是相互独立的,也就是说 interface 可以 extends type,type 也可以 extends interface。
虽然效果差不多,但是两者语法不同。
// interface extends interface
interface Name {
name: string;
}
interface User extends Name {
age: number;
}
// type extends type
type Name = {
name: string;
};
type User = Name & { age: number };
// interface extends type
type Name = {
name: string;
};
interface User extends Name {
age: number;
}
// type extends interface
interface Name {
name: string;
}
type User = Name & {
age: number;
};
- 只有
type可以做的
type 可以声明基本类型别名,联合类型,元组等类型;
// 基本类型别名
type Name = string;
// 联合类型
interface Dog {
wong();
}
interface Cat {
miao();
}
type Pet = Dog | Cat;
// 具体定义数组每个位置的类型
type PetList = [Dog, Pet];
// 当你想获取一个变量的类型时,使用 typeof
let div = document.createElement('div');
type B = typeof div;
3、如何基于一个已有类型,扩展出一个大部分内容相似,但是有部分区别的类型?
Pick:复合类型中,取出几个想要的类型的组合;Omit:以一个类型为基础支持剔除某些属性,然后返回一个新类型;
interface Test {
name: string;
sex: number;
height: string;
}
type Sex = Pick<Test, 'sex'>;
/*
type Sex = {
sex: number;
}
*/
const a: Sex = { sex: 1 };
type WithoutSex = Omit<Test, 'sex'>;
/*
type WithoutSex = {
name: string;
height: string;
}
*/
const b: WithoutSex = { name: '1111', height: 'sss' };
4、什么是泛型,泛型的具体使用?
泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,使用时再去指定类型的一种特性。
可以把泛型理解为代表类型的参数。
interface Test<T = any> {
userId: T;
}
type TestA = Test<string>;
type TestB = Test<number>;
const a: TestA = {
userId: '111',
};
const b: TestB = {
userId: 2222,
};
5、实现一个路由跳转,通过 TS 约束参数的 routeHelper
routeHelper 的实现:
/*
要求:实现 RouterHelper,提前约束跳转页面的必选参数
须知:vue-router 里面的 key 和 value 都是 string
*/
import { Dictionary } from 'vue-router/types/router';
/*
type Dictionary<T> = { [key: string]: T };
*/
import Router, { RoutePath } from '../router';
export type BaseRouteType = Dictionary<string>;
/*
type BaseRouteType = {
[key: string]: string;
}
*/
export interface IndexParam extends BaseRouteType {
name: string;
}
export interface AboutPageParam extends BaseRouteType {
testName: string;
}
export interface UserPageParam extends BaseRouteType {
userId: string;
}
// 定义路径和参数的映射
export interface ParamsMap {
[RoutePath.Index]: IndexParam;
[RoutePath.About]: AboutPageParam;
[RoutePath.User]: UserPageParam;
}
export class RouterHelper {
/*
T extends RoutePath:定义一个泛型 T,T 被 RoutePath 所约束
RoutePath {
Index = '/',
About = '/about',
User = '/user'
}
*/
public static replace<T extends RoutePath>(routePath: T, params: ParamsMap[T]) {
Router.replace({
path: routePath,
query: params,
});
}
public static push<T extends RoutePath>(routePath: T, params: ParamsMap[T]) {
Router.push({
path: routePath,
query: params,
});
}
}
使用方式:
import { RouterHelper } from '../lib/routerHelper';
RouterHelper.push(RoutePath.About, { testName: '123' });
6、写一个计算时间的装饰器
代码实现:
/*
要求:实现倒计时
*/
import { EventEmitter } from 'eventemitter3';
// 定个接口:格式化的数据格式
export interface RemainTimeData {
/**
* 天数
*/
days: number;
/**
* 小时数
*/
hours: number;
/**
* 分钟数
*/
minutes: number;
/**
* 秒数
*/
seconds: number;
/**
* 毫秒数
*/
count: number;
}
export type CountdownCallback = (remainTimeData: RemainTimeData, remainTime: number) => void;
enum CountdownStatus {
running,
paused,
stopped,
}
export enum CountdownEventName {
START = 'start',
STOP = 'stop',
RUNNING = 'running',
}
interface CountdownEventMap {
[CountdownEventName.START]: []; // 空数组表示回调函数没有参数
[CountdownEventName.STOP]: [];
[CountdownEventName.RUNNING]: [RemainTimeData, number];
}
export function fillZero(num: number) {
return `0${num}`.slice(-2);
}
export class Countdown extends EventEmitter<CountdownEventMap> {
private static COUNT_IN_MILLISECOND: number = 1 * 100;
private static SECOND_IN_MILLISECOND: number = 10 * Countdown.COUNT_IN_MILLISECOND;
private static MINUTE_IN_MILLISECOND: number = 60 * Countdown.SECOND_IN_MILLISECOND;
private static HOUR_IN_MILLISECOND: number = 60 * Countdown.MINUTE_IN_MILLISECOND;
private static DAY_IN_MILLISECOND: number = 24 * Countdown.HOUR_IN_MILLISECOND;
private endTime: number; // 结束时间
private remainTime: number = 0; // 剩余时间
private status: CountdownStatus = CountdownStatus.stopped;
private step: number;
/**
* endTime:结束时间
* step:多少秒跳一次,默认值 1000ms(1e3)
*/
constructor(endTime: number, step: number = 1e3) {
super();
this.endTime = endTime;
this.step = step;
this.start();
}
public start() {
this.emit(CountdownEventName.START);
this.status = CountdownStatus.running;
this.countdown();
}
public stop() {
this.emit(CountdownEventName.STOP);
this.status = CountdownStatus.stopped;
}
// 倒计时 - 使用了递归
private countdown() {
if (this.status !== CountdownStatus.running) {
return;
}
// 边界条件,使用 Math.max 是为了防止出现负数的情况
// 正常情况下,endTime > now
// 如果 endTime < now 了,this.endTime - Date.now() 就是负数,所以和 0 取最大值
this.remainTime = Math.max(this.endTime - Date.now(), 0);
this.emit(CountdownEventName.RUNNING, this.parseRemainTime(this.remainTime), this.remainTime);
if (this.remainTime > 0) {
setTimeout(() => this.countdown(), this.step);
} else {
this.stop();
}
}
// 时间戳的换算逻辑
private parseRemainTime(remainTime: number): RemainTimeData {
let time = remainTime;
const days = Math.floor(time / Countdown.DAY_IN_MILLISECOND);
time = time % Countdown.DAY_IN_MILLISECOND;
const hours = Math.floor(time / Countdown.HOUR_IN_MILLISECOND);
time = time % Countdown.HOUR_IN_MILLISECOND;
const minutes = Math.floor(time / Countdown.MINUTE_IN_MILLISECOND);
time = time % Countdown.MINUTE_IN_MILLISECOND;
const seconds = Math.floor(time / Countdown.SECOND_IN_MILLISECOND);
time = time % Countdown.SECOND_IN_MILLISECOND;
const count = Math.floor(time / Countdown.COUNT_IN_MILLISECOND);
return {
days,
hours,
minutes,
seconds,
count,
};
}
}
使用方式:
import { Countdown, CountdownEventName, fillZero } from '../lib/countdown';
const countdown = new Countdown(Date.now() + 60 * 60 * 1000, 10);
countdown.on(CountdownEventName.RUNNING, remainTimeData => {
const { hours, minutes, seconds, count } = remainTimeData;
this.timeDisplay = [hours, minutes, seconds, count].map(fillZero).join(':');
});
7、写一个缓存的装饰器
/**
* 缓存的装饰器
*/
const cacheMap = new Map();
export function EnableCache(target: any, name: string, descriptor: PropertyDescriptor) {
const val = descriptor.value;
descriptor.value = async function (...args: any) {
const cacheKey = name + JSON.stringify(args);
if (!cacheMap.get(cacheKey)) {
const cacheValue = Promise.resolve(val.apply(this, args)).catch(_ => cacheMap.set(cacheKey, null));
cacheMap.set(cacheKey, cacheValue);
}
console.log('cacheMap =========== ', cacheMap);
return cacheMap.get(cacheKey);
};
return descriptor;
}
8、其他装饰器
/**
* 方法执行之前执行的方法
*/
export function before(beforeFn: any) {
return function (target: any, name: any, descriptor: any) {
const oldValue = descriptor.value;
descriptor.value = function () {
beforeFn.apply(this, arguments);
return oldValue.apply(this, arguments);
};
return descriptor;
};
}
/**
* 方法执行之后执行的方法
*/
export function after(afterFn: any) {
return function (target: any, name: any, descriptor: any) {
const oldValue = descriptor.value;
descriptor.value = function () {
const ret = oldValue.apply(this, arguments);
afterFn.apply(this, arguments);
return ret;
};
return descriptor;
};
}
/**
* 方法执行耗时
*/
export function measure(target: any, name: any, descriptor: any) {
const oldValue = descriptor.value;
descriptor.value = async function () {
const start = Date.now();
console.log('start ========= ', start);
const ret = await oldValue.apply(this, arguments);
console.log(`${name}执行耗时 ${Date.now() - start}ms`);
return ret;
};
return descriptor;
}
四、tsconfig.json 配置项
{
"compilerOptions": {
/* 基本选项 */
"target": "es5", // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
"module": "commonjs", // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
"lib": [], // 指定要包含在编译中的库文件
"allowJs": true, // 允许编译 javascript 文件
"checkJs": true, // 报告 javascript 文件中的错误
"jsx": "preserve", // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
"declaration": true, // 生成相应的 '.d.ts' 文件
"sourceMap": true, // 生成相应的 '.map' 文件
"outFile": "./", // 将输出文件合并为一个文件
"outDir": "./", // 指定输出目录
"rootDir": "./", // 用来控制输出目录结构 --outDir.
"removeComments": true, // 删除编译后的所有的注释
"noEmit": true, // 不生成输出文件
"importHelpers": true, // 从 tslib 导入辅助工具函数
"isolatedModules": true, // 将每个文件做为单独的模块 (与 'ts.transpileModule' 类似).
/* 严格的类型检查选项 */
"strict": true, // 启用所有严格类型检查选项
"noImplicitAny": true, // 在表达式和声明上有隐含的 any类型时报错
"strictNullChecks": true, // 启用严格的 null 检查
"noImplicitThis": true, // 当 this 表达式值为 any 类型的时候,生成一个错误
"alwaysStrict": true, // 以严格模式检查每个模块,并在每个文件里加入 'use strict'
/* 额外的检查 */
"noUnusedLocals": true, // 有未使用的变量时,抛出错误
"noUnusedParameters": true, // 有未使用的参数时,抛出错误
"noImplicitReturns": true, // 并不是所有函数里的代码都有返回值时,抛出错误
"noFallthroughCasesInSwitch": true, // 报告 switch 语句的 fallthrough 错误。(即,不允许 switch 的 case 语句贯穿)
/* 模块解析选项 */
"moduleResolution": "node", // 选择模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
"baseUrl": "./", // 用于解析非相对模块名称的基目录
"paths": {}, // 模块名到基于 baseUrl 的路径映射的列表
"rootDirs": [], // 根文件夹列表,其组合内容表示项目运行时的结构内容
"typeRoots": [], // 包含类型声明的文件列表
"types": [], // 需要包含的类型声明文件名列表
"allowSyntheticDefaultImports": true, // 允许从没有设置默认导出的模块中默认导入。
/* Source Map Options */
"sourceRoot": "./", // 指定调试器应该找到 TypeScript 文件而不是源文件的位置
"mapRoot": "./", // 指定调试器应该找到映射文件而不是生成文件的位置
"inlineSourceMap": true, // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件
"inlineSources": true, // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性
/* 其他选项 */
"experimentalDecorators": true, // 启用装饰器
"emitDecoratorMetadata": true // 为装饰器提供元数据的支持
}
}