现有Vue项目接入ts

2,166 阅读2分钟

1. 安装

yarn add -D typescript ts-loader vue-class-component vue-property-decorator

2. 修改 vue.config.js

const path = require('path');

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

module.exports = {
  // ...
  chainWebpack: config => {
    // ...
    // add ts-loader
    config.module
    .rule('ts')
    .test(/\.tsx?$/)
    .exclude
      .add(resolve('node_modules'))
      .end()
    .use('cache-loader')
      .loader('cache-loader')
      .options({
          cacheDirectory: resolve('node_modules/.cache/ts-loader')
      })
      .end()
    .use('babel-loader')
      .loader('babel-loader')
      .end()
    .use('ts-loader')
      .loader('ts-loader')
      .options({
          transpileOnly: false, // 关闭类型检查,即只进行转译(类型检查交给webpack插件(fork-ts-checker-webpack-plugin)在另一个进程中进行,这就是所谓的多进程方案,如果设置transpileOnly为false, 则编译和类型检查全部由ts-loader来做, 这就是单进程方案.显然多进程方案速度更快)
          appendTsSuffixTo: ['\\.vue$'],
          happyPackMode: false
      })
      .end()
  }
}

3. 新增 3 个文件

  1. tsconfig.json

    // 使用官方的,其他配置可以自定义
    {
      "compilerOptions": {
        // 与 Vue 的浏览器支持保持一致
        "target": "es5",
        // 这可以对 `this` 上的数据 property 进行更严格的推断
        "strict": true,
        // 如果使用 webpack 2+ 或 rollup,可以利用 tree-shake:
        "module": "es2015",
        "moduleResolution": "node"
      }
    }
    

    丰富配置的版本:

    {
        "externalTranspiler": {
            "name": "babel",
            "options": {}
        },
        "buildOnSave": false,
        "compileOnSave": false,
        "indentSize": 2,
        "tabSize": 2,
        "compilerOptions": {
            "suppressOutputPathCheck": true,
            "baseUrl": ".",
            "noEmit": false,
            "declaration": false,
            "moduleResolution": "node",
            "module": "commonjs",
            "target": "es6",
            "allowNonTsExtensions": true,
            "experimentalDecorators": true,
            "emitDecoratorMetadata": true,
            "jsx": "react",
            "removeComments": false,
            "noLib": false,
            "preserveConstEnums": false,
            "suppressImplicitAnyIndexErrors": true
        },
        "filesGlob": [
            "app/**/*.ts"
        ],
        "files": [
            "app/routing.ts"
        ],
        "atom": {
            "rewriteTsconfig": false
        }
    }
    
  2. shims-vue.d.ts

    由 shims-vue.d.ts 引发的思考 :

    Ambient Declarations(通称:外部模块定义) ,主要为项目内所有的 vue 文件做模块声明,毕竟 ts 默认只识别 .d.ts、.ts、.tsx 后缀的文件;(即使补充了 Vue 得模块声明,IDE 还是没法识别 .vue 结尾的文件,这就是为什么引入 vue 文件时必须添加后缀的原因,不添加编译也不会报错)

    declare module '*.vue' {
      import Vue from 'vue';
      export default Vue;
    }
    
  3. shims-tsx.d.ts

    由 shims-vue.d.ts 引发的思考 :

    后者为 JSX 语法的全局命名空间,这是因为基于值的元素会简单的在它所在的作用域里按标识符查找(此处使用的是**无状态函数组件 (SFC)**的方法来定义),当在 tsconfig 内开启了 jsx 语法支持后,其会自动识别对应的 .tsx 结尾的文件,可参考官网 jsx

    import Vue, { VNode } from 'vue';
    
    declare global {
      namespace JSX {
        // tslint:disable no-empty-interface
        interface Element extends VNode { }
        // tslint:disable no-empty-interface
        interface ElementClass extends Vue { }
        interface IntrinsicElements {
          [elem: string]: any
        }
      }
    }
    

4. 额外

一些必要的工具库,可以考虑加入,如 eslint :

module.exports = {
  // ...
  chainWebpack: config => {
    // ...
    // eslint 自动修复 (修改已经存在的loader)
      config.module
      .rule('eslint')
      .test(/\.(vue|(j|t)sx?)$/)
      .pre() // eslint是pre处理的
      .use('eslint-loader')
        .loader('eslint-loader')
        .tap(options => { // 修改已经存在loader的配置
            options.fix = true
            return options
        })
        .end()
  }
}

参考

TypeScript 支持

无缝改造vue项目,支持typescript

由 shims-vue.d.ts 引发的思考