Vue - js 向 ts 迁移

4,774 阅读5分钟

介绍

当前 typescript 在前端的使用已经越来越热,伴随前端工程越来越庞大,javascript 的弱语言的特点逐渐显露出了一些缺点。 但是现在的浏览器并不支持 TS 的语法,所以还是需要将 TS 最终编译成 JS 才能正常在浏览器中打开。

Vue 项目迁移 ts

安装依赖

Vue官方提供了一个库Vue-class-component,用于让我们使用Ts的类声明方式来编写vue组件代码。但是查看若干文章,都在使用 Vue-property-decorator 来使用,其中 vue-element-admin的 TS 版本也使用了这个在Vue-class-component的基础上提供了装饰器来编写,那么在迁移的过程我们便也不犹豫的使用了。 我的项目是基于vue-cli@4.X创建的,还需要在项目中引入支持 TS 的编译的依赖:

yarn add @vue/cli-plugin-typescript typescript --dev

配置 tsconfig.json 和 vue.config.js

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "baseUrl": ".",
    "types": [
      "node",
      "jest",
      "webpack-env"
    ],
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

这里边进行启动查看一下效果,发现启动报错:已经在src 目录下寻找 main.ts 文件了,而我想做的是一步步的迁移,main.ts 文件是最后才替换的。

* ./src/main.ts in multi (webpack)-dev-server/client?http://192.168.43.86:8080/sockjs-node (webpack)/hot/dev-server.js ./src/main.ts, multi (webpack)/hot/dev-server.js (webpack)-dev-server/client?http://192.168.43.86:8080/sockjs-node ./src/main.ts
Type checking in progress...
No type errors found
Version: typescript 3.8.3
Time: 4321ms

进而先移除掉 @vue/cli-plugin-typescript 组件,而添加只是编译 ts 的loader:

yarn add ts-loader --dev

增加配置文件 vue.config.js

'use strict'
const path = require('path')

function resolve(dir) {
  return path.join(__dirname, dir)
}

const name = 'hello-world' // page title

// If your port is set to 80,
// use administrator privileges to execute the command line.
// For example, Mac: sudo npm run
// You can change the port by the following method:
// port = 9527 npm run dev OR npm run dev --port = 9527
const port = 3000 // dev port

module.exports = {
  publicPath: '/',
  outputDir: 'dist',
  assetsDir: 'static',
  lintOnSave: process.env.NODE_ENV === 'development',
  productionSourceMap: false,
  devServer: {
    port: port,
    open: true,
    overlay: {
      warnings: false,
      errors: true
    }
  },
  configureWebpack: {
    // provide the app's title in webpack's name field, so that
    // it can be accessed in index.html to inject the correct title.
    name: name,
    resolve: {
      alias: {
        '@': resolve('src')
      },
      extensions: [".ts", ".tsx", ".js", ".json"]
    },
    module: {
      rules: [
        {
          test: /\.tsx?$/,
          loader: 'ts-loader',
          exclude: /node_modules/,
          options: {
            appendTsSuffixTo: [/\.vue$/],
          }
        }
      ]
    }
  }
}

添加之后可以正常启动。

文件之间的相互引用

vue 文件中引用 ts 文件

新建 ts 文件:

export const hello = '你好,这是在增加 ts-loader 之后增加的第一个 ts 文件'

class Person {
    private name: string;
    constructor (name:string) {
        this.name = name
    }
    sayHi () {
        return 'hi, ' + this.name
    }
}
export const sayHello = (name: string) => {
    let person = new Person(name)
    return person.sayHi()
}

将该文件在 hello.vue 中引用:

<template>
    <div>
        {{helloConst}}----{{greeting}}
    </div>
</template>
<script>
import { hello, sayHello } from './const'
export default {
    name: 'hello',
    data () {
        return {
            helloConst: hello
        }
    },
    computed: {
        greeting () {
            return sayHello('张三')
        }
    }
}
</script>

重新启动后报错:

 error  in D:\workspace\vue\demo\hello-world\hello-world\tsconfig.json

[tsl] ERROR
      TS2688: Cannot find type definition file for 'webpack-env'.

安装 webpack-env

yarn add @types/webpack-env

安装之后可以正常启动并渲染内容。

ts 文件中引用 ts 文件

Cat.ts

class Cat {
    static miao() {
        return 'miaomiao--可爱喵'
    }
}

export default Cat

const.ts

import Cat from './Cat'

export const hello = '你好,这是在增加 ts-loader 之后增加的第一个 ts 文件'

class Person {
    name: string;
    constructor (name:string) {
        this.name = name
    }
    sayHi () {
        return 'hi, ' + this.name
    }
}
export const sayHello = (name: string) => {
    let person = new Person(name)
    return person.sayHi()
}

export const miao = () => {
    return Cat.miao()
}

hello.vue

<template>
    <div>
        {{helloConst}}----{{greeting}}
    </div>
</template>
<script>
import { hello, sayHello, miao} from './const'
export default {
    name: 'hello',
    data () {
        return {
            helloConst: hello
        }
    },
    computed: {
        greeting () {
            return sayHello('张三') + '====' + miao()
        }
    }
}
</script>

根据以上两步之后,在 vue 中引用 ts 文件和 ts 文件中引用 ts 文件已经是可以的了。然后就剩下要在 ts 文件中引用 vue 文件了。

ts 文件引用 vue 文件

本来想直接使用 router 下的 index.js 改为 ts 文件来实验这个内容。可是修改之后直接报错:

Module build failed (from ./node_modules/eslint-loader/index.js):
Error: ENOENT: no such file or directory, open 'D:\workspace\vue\demo\hello-world\hello-world\src\router\index.js'

 @ ./src/main.js 8:0-30 15:10-16
 @ multi (webpack)-dev-server/client?http://192.168.43.86:3000/sockjs-node (webpack)/hot/dev-server.js ./src/main.js

根据报错信息的显示,看来在main.js 文件中是无法引用 ts 文件的。

是js 文件中均无法引用 ts 文件吗???

在 hello 文件夹下新建:index.ts:

export { default as hello } from './hello.vue'

报错:

This relative module was not found:

* ./const in ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/views/hello/hello.vue?vue&type=script&lang=js&

Visual Studio 提示:

can not find module './hello.vue'

看来在 ts 文件中还无法引用 vue 组件,ts文件中是无法识别vue文件的,所以需要在项目根目录新建 shims-vue.d.ts 文件,添加以下代码,来让ts识别vue文件。

declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}

添加之后Visual Studio 的错误提示消失,在 ts 文件已经认识 vue 文件了。 但是编译的报错依然存在。 呃呃,经过查找是路径发生了变化,真是恶心了,现在 hello.vue 文件引用的 const.ts 的路径不对。修改后可以正常启动。

但是现在hello.vue 中需要渲染出来的内容却没有展示出来,可恶。 将新增的index.ts 修改为:

// export { default as hello } from './hello.vue' 
// 引用的文件的写法是 import { hello } from "./hello/index", 不能在 router 的 component 下直接使用
import hello from './hello.vue'
export default hello

之后可以正常展示。这里真的是犯了一个很弱的错误,在 router 的 index.js 文件中,

export const constantRoutes = [
    {
        path: '/',
        component: Layout,
        redirect: '/hello',
        children: [
            {
                path: '/hello',
                component: () => import ('@/views/hello') // hello 里只能是倒出的一个组件
            }
        ]
    }
]

至此,文件之间的相互引用已经全部 ok 了。

改写 Vue 文件

将原来的vue文件改写成使用 vue-property-decorator 编写的方式。

安装依赖

yarn add vue-property-decorator

新建文件 about.vue

<template>
    <div>
        {{msg}}
    </div>
</template>
<script lang="ts">

import { Component, Prop, Vue } from 'vue-property-decorator';

@Component
export default class about extends Vue {
  @Prop({default: 'about......'}) private msg!: string;
}
</script>

页面可以正常显示信息,但是一只在报编译错误:

Module Warning (from ./node_modules/eslint-loader/index.js):

D:\workspace\vue\demo\hello-world\hello-world\src\views\about.vue
  11:0  error  Parsing error: Using the export keyword between a decorator and a class is not allowed. Please use `export @dec class` instead.

  2 | import { Component, Prop, Vue } from 'vue-property-decorator';
  3 | @Component
> 4 | export default class about extends Vue {
    | ^
  5 |   @Prop({default: 'about......'}) private msg!: string;
  6 | }

增加 .eslintrc.js 文件后,报错消失。 这个文件中需要确定在 package.json 的 dev 中存在依赖:"@typescript-eslint/parser","@typescript-eslint/eslint-plugin"

module.exports = {

    parser:  '@typescript-eslint/parser', //定义ESLint的解析器
    extends: ['plugin:@typescript-eslint/recommended'],//定义文件继承的子规范
    plugins: ['@typescript-eslint'],//定义了该eslint文件所依赖的插件
    env:{                          //指定代码的运行环境
        browser: true,
        node: true,
    }                               
}