在 Vue 中使用 Typescript
前言
恕我直言,用 typescript 写 vue 真的很难受,vue 对 ts 的支持一般,如非万不得已还是别在 vue 里边用吧,不过听说 vue3 会增强对 ts 的支持,正式登场之前还是期待一下吧嘻嘻。
本篇不会过多讲述 ts 语法,着重记录下 在 vue 中使用 ts 的方法以及踩坑经过。
如果是使用 vue cli2 搭建的项目,要注意 webpack 版本可能与 ts-loader 版本不匹配,可以降低 ts-loader 版本到 3.0+ 或者 将 webpack升级到 4.0+ (本篇所用版本 webpack@3.6.0 + ts-loader@3.5.0)
主要步骤
1. 先要让 vue 项目可以识别 .ts 文件。安装关键依赖 npm i typescript ts-loader -d
2. 在 webpack.base.config.js 中添加 ts-loader
//resolve.extensions 里面加上.ts 后缀 之后引入.ts的时候可以不写后缀 { test: /\.tsx?$/, loader: 'ts-loader', exclude: /node_modules/, options: { appendtssuffixto: [/\.vue$/], //关键 } }
3. 在根目录建tsconfig.json文件 下面的配置仅供参考
{ "include": [ "src/**/*" ], "exclude": [ "node_modules" ], "compileroptions": { "allowsyntheticdefaultimports": true, "experimentaldecorators": true, "allowjs": true, "module": "esnext", "target": "es5", "moduleresolution": "node", "isolatedmodules": true, "lib": [ "dom", "es5", "es2015.promise" ], "sourcemap": true, "pretty": true } }
4. 接着需要确保在 vue 中可以正常使用 ts 。安装 vue-class-component(为vue组件提供修饰器) vue-property-decorator(更多的结合vue特性的修饰器) [ tslint tslint-loader tslint-config-standard(可选 约束.ts/.tsx代码格式)],这样就可以在 vue 文件中使用诸如 @component、@prop等装饰器了。注意:.vue 文件中的 script 标签要加上 lang="ts"。关于 装饰器的使用可以参看下这位大哥的文章:
<template> <div class="count-down" v-html="countdown(enddate)"> </div> </template> <script lang="ts"> import { component, prop, vue, emit } from "vue-property-decorator" import moment from 'moment' @component export default class countdown extends vue { @prop() enddate!: string // 变量后加!是非空断言 now: any = moment() mounted() { setinterval((): void =>{ this.now = moment() },1000) } destroyed() { } get countdown(): object{ return function(enddate: any): string { let m1: any = this.now let m2: any = moment(enddate) let du: any = moment.duration(m2 - m1, 'ms') let hours: number = du.get('hours') let mins: number = du.get('minutes') let ss: number = du.get('seconds') if(hours <= 0 && mins <= 0 && ss <= 0) { // this.$emit('timeout') this.timeout() return "今日已结束" }else { return `${this.prefixinteger(hours,2)} <span style="font-size: 16px;">小时</span> ${this.prefixinteger(mins,2)} <span style="font-size: 16px;">分钟</span><span style="color: #f56c6c;"> ${this.prefixinteger(ss,2)} </span><span style="font-size: 16px;">秒</span>` } } } @emit() timeout(){} //数字前补 0 // num传入的数字,n需要的字符长度 prefixinteger(num: number, n: number): string { return (array(n).join('0') + num).slice(-n) } } </script> <style lang="less" scoped> //... </style>
5. 将main.js 变成 main.ts 并且在 webpack.base.conf.js 修改入口为main.ts,这一步至关重要。
6. 在 src 目录下新建文件 shims-vue.d.ts ,告诉 typescript *.vue 后缀的文件可以交给 vue 模块来处理,注意 在代码中导入 *.vue 文件的时候,需要写上 .vue 后缀,这里可以参考官网说明:增强类型以配合插件使用
declare module "*.vue" { import vue from "vue"; export default vue; }
踩坑记录
1. 报错如下,报错原因一是没有将入口文件改成 ts,二是 webpack.base.conf.js 中引入 ts-loader 错误,没有加上 options: { appendtssuffixto: [/\.vue$/], }
module build failed: error: could not find source file: 'xxx/src/app.vue'.
2. 全局属性报错 如 vue.prototype.$msg = xxx,需要将全局属性在 .d.ts 文件中声明
import vue from "vue"; import { axiosinstance } from "axios"; import { elmessage } from "element-ui/types/message"; declare module "*.vue" { export default vue; } declare module 'vue/types/vue' { interface vue { $http: axiosinstance, $message: elmessage } }
3. 使用上面这种声明全局属性的方式又会带来新的问题,报错 import app from './app.vue'处,找不到 app.vue 这个模块,虽然不影响编译,但是这红色的波浪线就像老鼠屎,看着那叫一个难受呀。解决方法:将 shims-vue.d.ts 文件一分为二,将全局属性声明和 vue 的声明分离;在 shims-vue.d.ts 文件同级目录下新建 vue.d.ts(名字不一定叫 vue,如 xxx.d.ts 也可以);关键是要将以下代码放在单独的 .d.ts 文件中
declare module '*.vue' { import vue from 'vue' export default vue }
上一篇: JS笔记之第一天
下一篇: Python编码风格指南