步骤

  • 使用 yarn add 安装 @vue/cli-service 对应版本的 @vue/cli-plugin-typescript
    • 例如:"@vue/cli-service": "~4.5.0" 使用yarn add -D @vue/cli-plugin-typescript@^4安装
  • 使用 vue invoke typescript 运行插件
  • 插件提供的配置项
    • Use class-style component syntax? 是否使用类组件
      • 类组件是通过 typescript 提供的装饰器实现了通过写一个类来写 vue 组件的方法,对 typescript 有更好的支持。
      • 但是官方配套的库并不能完美解决 typescript 的支持,需要 vue-tsx-support 提供额外支持
      • 新项目建议不选择,直接使用 composition API,虽然它也需要 vue-tsx-support 提供支持,但是这种代码组织方式更解耦
    • Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? 是否在 typescript 编译后使用 babel 进行编译
      • typescript 具备转换 ts 到指定某 es 版本的能力,但是不具备 babel 提供的其他转换代码的能力。
      • 例如:typescript 虽然能够把 jsx 转换为 javascript,但是转换的结果不能满足 vue 的要求,依然需要 babel 进行二次转换
      • 建议开启:jsx 是 vue 使用 ts 比较成熟的方案
    • Convert all .js files to .ts? 是否转换所有的 js 为 ts 文件
      • 建议选择否。对于旧项目而言,这种操作会导致大量的 ts 文件类型报错
    • Allow .js files to be compiled?
      • 是否允许接收 js 文件作为入口文件
    • Skip type checking of all declaration files (recommended for apps)?
      • 跳过所有类型文件 .d.ts 的检查,因为类型文件通常是外部库提供的,检查这些类型文件将会降低编译速度
  • 运行插件后续钩子
    • 如果项目中已经安装了 eslint 插件,由于增加了对 typescript 的格式检查支持,eslint 的钩子会被调用
      • 建议在格式化之前暂存文件,然后恢复被格式化的文件

相关问题

最佳实践

  • vue组件需要类型支持的特性
    • props
    • emit
    • slot
    • scopeSlot
    • ref 获取组件的方法或属性
    • provide/inject
  • vue 对 ts 的支持并不完善,建议放弃 .vue 文件方式,改用 jsx 书写 template
    • .vue 文件在引用组件时需要通过插件 Volar 来识别组件的类型,插件容易面临支持不完善和编辑器卡顿的问题
  • 使用 jsx 书写,需要配合 class component 或 compositionAPI 来实现,个人建议采用 compositionAPI,它在代码组织上更为灵活
  • vue 提供的 vue.extend 和 defineComponent 方法并不能正确返回一个 class 类型供外部使用,需要 wonderful-panda/vue-tsx-support提供完善的类型支持
    • 提供了原生元素的类型支持(input等)
    • 支持 props、emit、scopeSlot、ref 类型
    • 支持对现有组件进行类型包装,即给现有组件增加类型,使其能够被 ts 正确识别
    • 支持 class component 和 compositionAPI
    • 提供 router-link and router-view 类型支持
    • <MyComponent props={{ foo: "foo" }} />;
    • 事件处理程序包装器,其工作方式类似于模板中可用的一些事件修饰符,例如:<div onKeydown={m.enter(this.onEnter)} />;
  • tsx 先由 ts 转换为 js,然后由 babel 转换为 vue 渲染函数,ts 对部分 jsx 语法不支持(见后文)

别名

  • 使用 tsconfig-paths-webpack-plugin,把 tsconfig.json 中配置的别名同步到 webpack 中
// vue.config.js
{
  chainWebpack: (config) => {
    config.resolve
      .plugin("tsconfig-paths")
      .use(require("tsconfig-paths-webpack-plugin"));
  },
}

jsx

  • 3.0 和 4.0 的 jsx 是由 base 的插件提供的,由 @vue/babel-preset-jsx 实现
    • <p>hello { this.message }</p>
    • <input type="email" placeholder={this.placeholderText} />
    • <input { ...{ attrs: {type: 'email'}} } />
    • <header slot="header">header</header>
    • <MyComponent scopedSlots={ {header: () => <header>header</header>} } />
    • <p domPropsInnerHTML={html} />
  • @vue/babel-preset-jsx

compositionAPI

相关文件

3.0

  • package.json
 "devDependencies": {
    "@vue/cli-plugin-babel": "^3.12.0",
    "@vue/cli-service": "^3.12.0",
    "vue-template-compiler": "^2.6.10",
    "sass": "^1.19.0",
    "sass-loader": "^8.0.0",
    
    "@vue/cli-plugin-eslint": "^3.12.0",
    "@vue/eslint-config-prettier": "^5.0.0",
    "eslint": "^5.16.0",
    "eslint-plugin-prettier": "^3.1.0",
    "eslint-plugin-vue": "^5.0.0",
    "lint-staged": "^8.1.5",
    "prettier": "^1.18.2",
    "babel-eslint": "^10.0.1",
    
    "@vue/eslint-config-typescript": "^4.0.0",
    "@vue/cli-plugin-typescript": "^3.12.0",
    "typescript": "^3.4.3",
  },
  • tsconfig.json
{
  "compilerOptions": {
    "target": "esnext", // 指定ECMAScript目标版本
    "module": "esnext", // 指定生成哪个模块系统代码
    "strict": true, // 启用所有严格检查选项
    "jsx": "preserve", // 在.tsx文件里支持JSX
    "importHelpers": true, // 从tslib导入辅助工具函数
    "moduleResolution": "node", // 决定如何处理模块,解析引用时查找模块的规则
    "esModuleInterop": true, // 修复 CommonJS/AMD/UMD模块 导入语句转换问题
    "allowSyntheticDefaultImports": true, // 允许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查。
    "sourceMap": true, // 生成相应的.map文件
    "baseUrl": ".", // 解析非相对模块名的基准目录
    "types": [ // 要包含的类型声明文件名列表
      "webpack-env"
    ],
    "paths": { // 别名,基于baseUrl的路径映射的列表
      "@/*": [
        "src/*"
      ]
    },
    "lib": [ // 编译过程中需要引入的库文件的列表
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  // 使用"include"引入的文件可以使用"exclude"属性过滤。 然而,通过"files"属性明确指定的文件却总是会被包含在内,不管"exclude"如何设置
  // 任何被"files"或"include"指定的文件所引用的文件也会被包含进来
  "include": [ 
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}
  • .eslintrc.js
module.exports = {
  root: true,
  env: {
    node: true
  },
  extends: ["plugin:vue/essential", "@vue/prettier", "@vue/typescript"], // 和仅仅 eslint 的区别
  rules: {
    "no-console": process.env.NODE_ENV === "production" ? "error" : "off",
    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off"
  },
  parserOptions: {
    parser: "@typescript-eslint/parser" // 和仅仅 eslint 的区别
  }
};

4.0

  • package.json
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.19",
    "@vue/cli-service": "~4.5.19",
    "vue-template-compiler": "^2.6.11",
    "sass": "^1.26.5",
    "sass-loader": "^8.0.2",
    "@vue/cli-plugin-router": "~4.5.19",
    "@vue/cli-plugin-vuex": "~4.5.19",

    "@vue/cli-plugin-eslint": "~4.5.19",
    "@vue/eslint-config-prettier": "^6.0.0",
    "eslint": "^6.7.2",
    "eslint-plugin-prettier": "^3.3.1",
    "eslint-plugin-vue": "^6.2.2",
    "lint-staged": "^9.5.0",
    "prettier": "^2.2.1",
    
    "@vue/eslint-config-typescript": "^7.0.0",
    "@vue/cli-plugin-typescript": "~4.5.19",
    "typescript": "~4.1.5",
    "@typescript-eslint/eslint-plugin": "^4.18.0",
    "@typescript-eslint/parser": "^4.18.0"
  },
  • tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "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",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}
  • .eslintrc.js
module.exports = {
  root: true,
  env: {
    node: true,
  },
  extends: [
    "plugin:vue/essential",
    "eslint:recommended",
    "@vue/typescript/recommended", // 和仅仅 eslint 的区别,注意顺序
    "@vue/prettier",
    "@vue/prettier/@typescript-eslint", // 和仅仅 eslint 的区别,注意顺序
  ],
  parserOptions: {
    ecmaVersion: 2020, // // 和仅仅 eslint 的区别
  },
  rules: {
    "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
    "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
  },
};
posted on 2022-07-19 10:36  噬蛇之牙  阅读(2111)  评论(0编辑  收藏  举报