目录
脚手架搭建
d.ts文件声明
vue-property-decorator 装饰器使用
vuex-class 装饰器使用
typescript按需转换ES6 Api
webpack 完整配置可以参考 typescript-vue-eslint-starter [ https://github.com/vok123/typescript-vue-eslint-starter ]
欢迎提建议,如果觉得有用的给个star哈~
↓↓↓↓↓↓高能时刻↓↓↓↓↓↓
开局一只蔡徐坤, 涨薪全靠vue + ts [https://www.bilibili.com/video/av50374517/?spm_id_from=333.788.videocard.4]
脚手架搭建
1. 安装依赖
- 开发依赖
npm i -D @typescript-eslint/eslint-plugin @typescript-eslint/experimental-utils @typescript-eslint/parser @typescript-eslint/typescript-estree eslint eslint-config-standard eslint-plugin-standard eslint-plugin-import eslint-plugin-promise eslint-loader eslint-plugin-node eslint-plugin-vue typescript ts-loader
- 项目依赖
npm i -S vue-class-component vue-property-decorator vuex-class
2. Webpack loader配置 (ts-loader/eslint-loader)
完整webpack配置 [https://github.com/vok123/typescript-vue-eslint-starter/blob/master/build/webpack.config.js]
module: {
rules: [
{
test: /\.ts(x)?$/,
loader: 'ts-loader',
exclude: /node_modules/,
options: {
appendTsSuffixTo: [/\.vue$/],
transpileOnly: true,
happyPackMode: false
}
},
{
test: /\.(js|vue|ts|tsx|jsx)$/,
enforce: 'pre',
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
fix: false,
extensions: ['.js', '.jsx', '.vue', '.ts', '.tsx'],
cache: false,
emitWarning: true,
emitError: false
}
}
];
}
3. 根目录添加eslint配置文件 .eslintrc.js
module.exports = {
plugins: ['vue', '@typescript-eslint'],
parserOptions: {
parser: '@typescript-eslint/parser',
env: { es6: true },
sourceType: 'module'
},
root: true,
env: {
browser: true,
node: true,
serviceworker: true
},
extends: ['plugin:vue/base', 'plugin:@typescript-eslint/recommended', 'plugin:vue/essential', 'standard'],
rules: {
// 设置默认eslint规则
'one-var': 0,
'arrow-parens': 0,
'generator-star-spacing': 0,
'no-debugger': 0,
'no-console': 0,
semi: [2, 'always'],
'no-extra-semi': 2,
'space-before-function-paren': 0,
eqeqeq: 0,
'spaced-comment': 0,
'no-useless-escape': 0,
'no-tabs': 0,
'no-mixed-spaces-and-tabs': 0,
'new-cap': 0,
camelcase: 0,
'no-new': 0,
indent: 'off',
semi: 'off',
// 设置typescript-eslint规则
// https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin/docs/rules
'@typescript-eslint/semi': ['error'],
'@typescript-eslint/indent': ['error', 2],
'@typescript-eslint/explicit-function-return-type': 0
}
};
4. 根目录添加 tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"allowJs": true,
"baseUrl": ".",
"types": ["webpack-env", "node"],
"paths": {
"@/*": ["src/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": ["node_modules"]
}
d.ts文件声明
src/@types/shims-vue.d.ts
- 声明.vue文件
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
- 声明组件实例属性或者方法 ( 类似 this.$route ) 来源[https://github.com/vuejs/vue-router/blob/dev/types/vue.d.ts]
declare module "vue/types/vue" {
interface Vue {
$message: (msg: string): void;
$balala: string;
}
}
// 在组件中使用
this.$balala
this.$message('Hello world');
- 声明new Vue自定义属性或者方法 (类似 new Vue({ router: router })) 来源[https://github.com/vuejs/vue-router/blob/dev/types/vue.d.ts]
declare module "vue/types/options" {
interface ComponentOptions<V extends Vue> {
i18n: any
}
}
// 使用
new Vue({
i18n, ...
});
- 声明第三方npm包
// 声明 element-ui
declare module 'element-ui'
// 声明 axios
declare module 'axios' {
import Axios from 'axios/index';
export default Axios;
}
vue-property-decorator 装饰器使用
- 常用装饰器及方法
装饰器 | 用途 | 描述 |
---|---|---|
Component | 声明class组件 | 只要是个组件都必须加该装饰器 |
Prop | 声明props | 对应普通组件声明中的props属性 |
Watch | 声明监听器 | 对应普通组件声明中的watch属性 |
Mixins | 混入继承 | 对应普通组件声明中的mixins属性 |
Emit | 子组件向父组件值传递 | 对应普通this.$emit() |
Inject | 接收祖先组件传递的值 | 对应普通组件声明中的inject属性 |
Provide | 祖先组件向其所有子孙后代注入一个依赖 | 对应普通组件声明中的provide属性 |
-
用法
- vue 钩子写法
// javascript <script> export default { beforeCreate() {}, created() {}, beforeMount() {}, mounted() {}, beforeUpdate() {}, updated() {}, activated() {}, deactivated() {}, beforeDestroy() {}, destroyed() {}, errorCaptured() {} } </script> // --------typescript-------- <script lang="ts"> import { Component, Vue } from 'vue-property-decorator'; @Component export default class App extends Vue { beforeCreate() {} created() {} beforeMount() {} mounted() {} beforeUpdate() {} updated() {} activated() {} deactivated() {} beforeDestroy() {} destroyed() {} errorCaptured() {} } </script>
- Component
// javascript import helloWorld from './helloWorld.vue'; export default { components: { helloWorld } } // --------typescript-------- import helloWorld from './helloWorld.vue'; import { Component, Vue } from 'vue-property-decorator'; @Component({ components: { helloWorld } }) export default class App extends Vue {}
- Prop
// javascript export default { props: { msg: { type: String, default: 'Hello world', required: true, validator: (val) => (val.length > 2) } } } // --------typescript-------- import { Component, Vue, Prop } from 'vue-property-decorator'; @Component export default class HelloWorld extends Vue { @Prop({ type: String, default: 'Hello world', required: true, validator: (val) => (val.length > 2) }) msg!: string }
- Watch
// javascript export default { data() { return { value: '' }; }, watch: { value: { handler() { console.log(this.value); }, deep: true, immediate: true } } } // --------typescript-------- import { Component, Vue, Watch } from 'vue-property-decorator'; @Component export default class App extends Vue { value: string = '' @Watch('value', { deep: true, immediate: true }) valueWatch() { console.log(this.value); } }
- Mixins
// javascript // -info.js export default { methods: { mixinsShow() { console.log('徐蔡坤'); } } } // -hello-world.vue import mixinsInfo from './info.js'; export default { mixins: [mixinsInfo], mounted() { this.mixinsShow(); // 徐蔡坤 } } // --------typescript-------- // -info.ts import { Component, Vue } from 'vue-property-decorator'; @Component export default class MixinsInfo extends Vue { mixinsShow() { console.log('徐蔡坤'); } } // -hello-world.vue import { Component, Vue, Mixins } from 'vue-property-decorator'; import mixinsInfo from './info.ts'; @Component export default class HelloWorld extends Mixins(mixinsInfo) { mounted() { this.mixinsShow(); // 徐蔡坤 } }
- Emit
// javascript // -children.vue <template> <button @click="$emit('submit', '唱, 跳')">提交</button> </template> // -parent.vue <template> <children @submit="submitHandle"/> </template> <script lang="ts"> import children from './children.vue'; export default { components: { children }, methods: { submitHandle(msg) { console.log(msg); // 唱, 跳 } } } </script> // --------typescript-------- // -children.vue <template> <button @click="submit">提交</button> </template> <script lang="ts"> import { Component, Vue, Emit } from 'vue-property-decorator'; @Component export default class Children extends Vue { @Emit() submit() { return '唱, 跳'; } } </script> // -parent.vue <template> <children @submit="submitHandle"/> </template> <script lang="ts"> import children from './children.vue'; import { Component, Vue } from 'vue-property-decorator'; @Component({ components: { children } }) export default class Parent extends Vue { submitHandle(msg: string) { console.log(msg); // 唱, 跳 } } </script>
- Provide/Inject
// javascript // -children.vue <script> export default { inject: ['root'], mounted() { console.log(this.root.name); // rap, 篮球 } } </script> // -parent.vue <template> <children /> </template> <script> import children from './children.vue'; export default { components: { children }, data() { return { name: 'rap, 篮球' }; }, provide() { return { root: this }; } } </script> // --------typescript-------- // -children.vue <script lang="ts"> import { Component, Vue, Inject } from 'vue-property-decorator'; @Component export default class Children extends Vue { @Inject() root!: any mounted() { console.log(this.root.name); // rap, 篮球 } } </script> // -parent.vue <template> <children /> </template> <script lang="ts"> import children from './children.vue'; import { Component, Vue, Provide } from 'vue-property-decorator'; @Component({ components: { children } }) export default class Parent extends Vue { name: string = 'rap, 篮球' @Provide() root = this.getParent() getParent() { return this; } } </script>
- 计算属性
// javascript export default { data() { return { hobby: '唱, 跳, rap, 篮球' }; }, computed: { msg() { return '我也会' + this.hobby; } }, mounted() { console.log(this.msg); // 我也会唱, 跳, rap, 篮球 } } // --------typescript-------- // -hello-world.vue import { Component, Vue } from 'vue-property-decorator'; @Component export default class HelloWorld extends Vue { hobby: string = '唱, 跳, rap, 篮球' get msg() { return '我也会' + this.hobby; } mounted() { console.log(this.msg); // 我也会唱, 跳, rap, 篮球 } }
vuex-class 装饰器使用
装饰器 | 用途 |
---|---|
State | 获取vuex state |
Getter | 获取vuex getter |
Mutation | 获取vuex mutation |
Action | 获取vuex actions |
-
vuex typescript 基础定义
// -store/store.ts import Vue from 'vue'; import Vuex, { StoreOptions } from 'vuex'; import user from './modules/user'; Vue.use(Vuex); interface RootState { version: string; } const store: StoreOptions<RootState> = { strict: true, state: { version: '1.0.0' }, modules: { user } }; export default new Vuex.Store<RootState>(store); // -store/modules/user.ts import { Module } from 'vuex'; export interface UserInfo { uId: string; name: string; age: number; } interface UserState { userInfo: UserInfo; } const user: Module<UserState, any> = { namespaced: true, state: { userInfo: { uId: '', name: '', age: 0 } }, getters: { isLogin(state) { return !!state.userInfo.uId; } }, mutations: { updateUserInfo(state, userInfo: UserInfo): void { Object.assign(state.userInfo, userInfo); } }, actions: { async getUserInfo({ commit }): Promise<void> { let { userInfo } = await getUserInfo(); commit('updateUserInfo', userInfo); } } }; export default user;
- vuex-class 装饰器
<script lang="ts"> import { Component, Vue } from 'vue-property-decorator'; import { State, Getter, Action } from 'vuex-class'; import { UserInfo } from './store/modules/user'; @Component export default class App extends Vue { @State('version') version!: string @State('userInfo', { namespace: 'user' }) userInfo!: UserInfo @Getter('isLogin', { namespace: 'user' }) isLogin!: boolean @Action('getUserInfo', { namespace: 'user' }) getUserInfo!: Function mounted() { this.getUserInfo(); console.log(this.version); // 1.0.0 } } </script>
- 在非vue组件的.ts中获取store数据
// test.ts import store from './store/store'; if (store.getters['user/isLogin'] === false) { console.log('未登录'); }
typescript按需转换ES6 Api
默认情况下typescript在转换语法为es3或者es5时并不会转换ES6 Api, 例如(Object.values, Array.fill ...)
在这种情况下我们可以使用 (ts-polyfill) [https://github.com/ryanelian/ts-polyfill] 定向指定需要转换的api