本文是vue项目的js升级ts教程,也可以作为vue项目的ts使用教程。
项目背景:
vue ts基础:
npm i -D typescript @vue/cli-plugin-typescript
ts校验相关:
npm i -D @typescript-eslint/eslint-plugin @typescript-eslint/parser @vue/eslint-config-typescript eslint-plugin-import eslint-plugin-node eslint-plugin-promise eslint-plugin-standard
vue ts辅助:
npm i -D vue-class-component vue-property-decorator
shims-tsx.d.ts
import Vue, {
VNode } from 'vue'
declare global {
namespace JSX {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface Element extends VNode {
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface ElementClass extends Vue {
}
interface IntrinsicElements {
[elem: string]: any;
}
}
interface Window {
__wxjs_environment: any;
WebViewJavascriptBridge: any;
_czc: any;
}
}
shims-vue.d.ts
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": ["src/*"]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
],
"exclude": [
"node_modules",
"dist",
"public",
"build"
]
}
vue-property-decorator插件使用文档:传送门
要在.vue文件里使用ts,首先要在script标签上添加属性lang=“ts”
例如一个基础的App.vue
<template>
<div id="app">
<router-view/>
div>
template>
<script lang="ts">
import {
Vue, Component } from 'vue-property-decorator'
@Component
export default class App extends Vue {
}
script>
<style lang="less">
style>
直接用等于号赋值
// ========== js版 ==========
data () {
return {
a: 1,
b: false
}
}
// ========== ts版 ==========
a = 1
b = false
用get的形式
// ========== js版 ==========
computed: {
aa () {
return this.a
}
}
// ========== ts版 ==========
get aa () {
return this.a
}
保持不变,只是不同函数之间不需要用逗号分隔
不再需要methods属性包裹,里面的方法直接提出来,和声明钩子函数平级(注意方法名别和钩子函数重名了),方法之间也不需要逗号分隔。
// ========== js版 ==========
created () {
},
methods: {
getData () {
},
setData () {
}
}
// ========== ts版 ==========
created () {
}
getData () {
}
setData () {
}
全都写在开头的@Component里
// ========== ts版 ==========
@Component({
components: {
},
directives: {
},
filters: {
}
})
通过@Prop定义
// ========== js版 ==========
props: {
title: {
type: String,
default: ''
},
list: {
type: Array,
default () {
return []
}
}
// ========== ts版 ==========
@Prop({
default: '' }) readonly title!: string
@Prop({
default: () => [] }) readonly list!: any[]
通过@Watch定义
// ========== js版 ==========
watch: {
// 普通监听
list (val, oldVal) {
console.log(val, oldVal)
},
// 深度监听
res: {
handler () {
console.log(val, oldVal)
},
deep: true,
immediate: false
}
}
// ========== ts版 ==========
@Watch('list')
onWatchList (val, oldVal) {
// 方法名没要求,但是要放在相应的@Watch下面,一一对应
console.log(val, oldVal)
}
@Watch('res', {
deep: true, immediate: false })
onHandleRes (val, oldVal) {
console.log(val, oldVal)
}
通过@PropSync定义
// ========== js版 ==========
props: {
// 父组件通过isShow.sync传递
isShow: {
type: boolean,
default: false
}
},
computed: {
isShowModal: {
get () {
return this.isShow
},
set (val) {
this.$emit('isShow:update', val)
}
}
}
// ========== ts版 ==========
@PropSync('isShow', {
default: false }) isShowModal!: boolean
直接上双混入:
// mixins1.js
export default {
created () {
console.log(1)
}
}
// mixins2.js
export default {
created () {
console.log(2)
}
}
// index.vue
<template>
<div id="index"></div>
</template>
<script>
import mixins1 from './mixins1'
import mixins2 from './mixins2'
export default {
mixins: [mixins1, mixins2]
}
</script>
// mixins1.ts
import {
Component, Vue } from 'vue-property-decorator'
@Component
export default class mixins1 extends Vue {
created () {
console.log(1)
}
}
// mixins2.ts
import {
Component, Vue } from 'vue-property-decorator'
@Component
export default class mixins2 extends Vue {
created () {
console.log(2)
}
}
// index.vue
<template>
<div id="index"></div>
</template>
<script lang="ts">
import {
Component, Prop, Mixins } from 'vue-property-decorator'
import mixins1 from './mixins1'
import mixins2 from './mixins2'
@Component
export default class Index extends Mixins(mixins1, mixins2) {
}
</script>
推荐使用插件vuex-class 官方文档
npm i vuex-class -S
以带命名空间namespace的为例:
import {
Component, Vue } from 'vue-property-decorator'
import {
namespace } from 'vuex-class'
const userModule = namespace('user')
@Component
export default class Index extends Vue {
@userModule.Getter isLogin!: boolean // 使用this.isLogin指代vuex的user模块里getters的同名isLogin
}
需要在shims-vue.d.ts文件里声明。
declare module 'js-md5' {
import md5 from 'js-md5'
export default md5
}
常见于给vue实例添加的自定义属性上,比如 $toast $http $loading
declare module 'vue/types/vue' {
interface Vue {
$loading: {
show: (text?: string) => void; hide: () => void }; // 具体类型以你自己的为准,或者直接用any
}
}
报错信息:找不到模块…或其相应的类型声明。
可能的原因有很多,按照以下方法依次排查处理:
"paths": {
"@/*": ["src/*"]
},
declare module '@/utils/api' {
import api from '@/utils/api'
export default api
}
ts里引入.ts文件时路径不要加后缀名,引入.vue文件时路径需要带上.vue后缀名
import apis from '@/utils/api'
import ruleModal from '@/components/rule.vue'
那就试试vscode工作区的配置:
vue 2.x的版本需要引入第三方插件使用class类的形式才能较好的配合使用ts,写起来还是比较繁琐的,而已经发布了正式版的vue 3.0对ts的支持比较完全,而且可以使用function形式使用ts,类似于react的hooks,function形式更加简单方便,如果是新开的项目还是推荐直接上vue 3.0吧。
module.exports = {
root: true,
env: {
browser: true,
node: true
},
extends: [
'plugin:vue/essential',
'@vue/standard',
'@vue/typescript/recommended'
],
parserOptions: {
ecmaVersion: 2020
},
globals: {
_czc: true
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'camelcase': 'off',
'comma-dangle': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/camelcase': 'off',
'@typescript-eslint/no-empty-function': 'off',
'lines-between-class-members': 'off',
'@typescript-eslint/no-this-alias': [
"error",
{
"allowDestructuring": true,
"allowedNames": ["that", "self"]
}
]
},
}