• @vue/cli typescript插件使用指南


    步骤

    • 使用 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 进行二次转换
        • 建议开启
      • Convert all .js files to .ts?
        • 是否转换所有的 js 为 ts 文件,建议选择否。对于旧项目而言,这种操作会导致大量的 ts 文件类型报错
      • Allow .js files to be compiled?
        • 是否允许编译 js,让 ts 编译 js 文件
      • Skip type checking of all declaration files (recommended for apps)?
        • 跳过所有类型文件的检查,因为类型文件通常是外部库提供的,检查这些类型文件将会降低编译速度
    • 运行插件后续钩子
      • typescript 插件会根据当前项目插件安装情况修改文件
        • 当前项目同时安装了 eslint 插件的话,会修改 .eslintrc.js 文件
        • 会修改 main.js 为 main.ts
        • 会增加 shims-tsx.d.ts 和 shims-vue.d.ts 等文件,详情见下文的文件解析
      • 如果项目中已经安装了 eslint 插件,由于增加了对 typescript 的格式检查支持,eslint 的钩子会被调用
        • 建议在格式化之前暂存文件,然后恢复被格式化的文件

    其他配套操作

    别名

    • 使用 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 比较不一样
    module.exports = {
      presets: [
        '@vue/app'
      ]
    }
    
    • 4.0、5.0 由 @vue/cli-plugin-babel 提供支持
      • 默认使用 Babel 7 + babel-loader + @vue/babel-preset-app
      • @vue/babel-preset-app 由以下库对 Vue JSX 语法提供支持
        • @babel/plugin-syntax-jsx 支持 babel 解析 jsx 语法
        • @vue/babel-preset-jsx 提供了 jsx 支持的语法,但由于 Babel 7的 bug,并非所有功能都支持
          • 配置
            • compositionAPI 在 setup 中返回渲染函数需要单独打开,下面代码中提供了配置方法
          • 异常
            • vOn: 暂时未能使用
            • vModel 当自定义组件具有 model 属性时会出现错误,例如:el-form
          • 组成库
            • @vue/babel-sugar-composition-api-inject-h、@vue/babel-sugar-composition-api-render-instance 支持 compositionApi 在 setup 方法中返回渲染函数
              • compositionApi 函数式组件const Test = (props, { refs, emit, ... }) => { return () => <h1>Hello World!</h1>; }
            • @vue/babel-sugar-functional-vue 支持函数式组件及以函数的方式写简易组件
              • 函数式组件export const A = ({ props, listeners, children, data, ... }) => <div onClick={listeners.click}>{props.msg}</div>
              • 以函数的方式写简易组件,变量名不同export const b = ({ props, listeners }) => <div onClick={listeners.click}>{props.msg}</div>
            • @vue/babel-sugar-inject-h 给 jsx 函数自动注入 h 函数
            • @vue/babel-sugar-v-model 支持 vModel 语法
            • @vue/babel-sugar-v-on 支持 vOn 语法
            • @vue/babel-plugin-transform-vue-jsx 支持其他的一些语法,包括domPropsInnerHTML等
            • @vue/babel-helper-vue-jsx-merge-props
    module.exports = {
      presets: [
        [
          "@vue/cli-plugin-babel/preset",
          {
            jsx: {
              compositionAPI: true,
            },
          },
        ],
      ],
    };
    
    

    composition-api

    • 安装并引用 composition-api,一种 vue 官方提供的,更好代码组织方式,支持 ts,支持 vue2。
    • 部分文档
      • getCurrentInstance 获取当前组件实例,用来代替 this

    ts支持现状

    • 内置元素
      • .vue 文件
        • .vue 文件中 vscode 1.66.2 和 vetur 0.35 支持内置元素属性的输入提醒,但是输入错误没有报错
      • jsx 模式
        • jsx 中需要使用 vue-tsx-support 外部库支持
    • 自定义元素
      • prop
        • .vue
          • .vue 文件 vetur 0.35 支持提醒,但不支持类型检查
          • vetur 0.35 提供 vetur.experimental.templateInterpolationService 测试功能,用于为 template 中的绑定之获取 js 特性和类型。需要配合<script lang='ts'>
          • vetur 0.35 提供 vetur.validation.interpolation 测试功能,配合 vetur.experimental.templateInterpolationService 用于检查传入组件的 prop 类型是否正确
          • 以上两种支持获取的类型并不准确,且实测中 vetur.experimental.templateInterpolationService 未能获得对应变量的类型
        • jsx
          • class
            • vue-tsx-support 支持
          • compositionAPI
            • compositionAPI原生支持
      • scope-slot
        • .vue
          • .vue 文件 vetur 0.35 不支持
        • jsx
          • class
            • vue-tsx-support 支持
          • compositionAPI
            • vue-tsx-support 支持
      • slot
        • .vue
          • .vue 文件 vetur 0.35 不支持
        • jsx
          • vue-tsx-support 不支持
      • emit
        • .vue
          • .vue 文件 vetur 0.35 不支持
        • jsx
          • class
            • vue-tsx-support 支持
          • compositionAPI
            • compositionAPI 原生支持?
    • 外部组件库
      • .vue
        • .vue 文件 vetur 0.35 提供了常用库的类型提醒,也支持通过配置 JSON 进行扩展
      • jsx
        • 外部组件库提供了 .d.ts 文件,但是不兼容 vue-tsx-support,需要手动转换
    • 推荐方案
      • vetur 对类型的支持始终受限于插件的完成度,建议使用 jsx 代替 template,回归原生,对于类型支持更完善

    其他疑难问题

    • vue invoke eslint 和 vue invoke typescript 执行顺序的先后,会影响 .eslint.js 中的配置。
      • 不清楚具体会有什么影响,建议建立一个对应版本新的项目,选择 eslint 和 typescript 插件,拷贝生成的 .eslint.js 文件
      • 以下为 4.0 配置
    module.exports = {
      root: true,
      env: {
        node: true,
      },
      extends: [
        "plugin:vue/essential",
        "eslint:recommended",
        "@vue/typescript/recommended",
        "@vue/prettier",
        "@vue/prettier/@typescript-eslint",
      ],
      parserOptions: {
        ecmaVersion: 2020,
      },
      rules: {
        "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
        "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
      },
    };
    
    • .vue文件中的data或methods下的方法报 Missing return type on function 错误
      • 这是由于 .vue 的 script 标签 export default 一个 {},该对象内的方法 eslint 检查时判断需要 return 的类型定义
      • 只需要改为 export default Vue.extend({})

    实操记录

    环境

    • "@vue/cli-service": "~4.5.0"
    • "@vue/cli-plugin-typescript": "4"

    修改的文件,文件解析来源于 TypeScript 3.1

    • .eslintrc.js

      • parserOptions
        • parser: "@typescript-eslint/parser"
          • 支持解析 typescript 的解析器
      • extends
        • 新增 "@vue/typescript"
    • tsconfig.json

      • "compilerOptions"
        • "target": "esnext",
          • 转换的目标 es 版本
        • "module": "esnext",
          • 转换的目标模块关系: "None", "CommonJS", "AMD", "System", "UMD", "ES6"或 "ES2015"
        • "strict": true,
          • 启用所有严格类型检查选项,相当于启用
          • noImplicitAny 禁止 any 类型
          • noImplicitThis 禁止 this 为 any 类型
          • alwaysStrict 以严格模式解析并为每个源文件生成 "use strict"语句
          • strictNullChecks 禁止 null、undefined 赋值给其他类型(有个例外 void)
          • strictFunctionTypes 进行严格的函数类型检查,禁止函数双向协变赋值
            • 是否协变是指,复合类型Comp<T>和基础类型 T,兼容关系的说明
            • 协变是指复合类型的兼容关系和基础类型的兼容关系一致
              • 例如:T1={a:string} Comp1<T>={a:string}[]
              • T2={a:string,b:number} Comp2<T>={a:string,b:unmber}[]
              • T2 可以赋值给 T1,Comp2 也可以赋值给 Comp1,他们的兼容性一致,被成为协变
            • 逆协变是指函数的参数存在逆协变
              • 例如:T1={a:string} Comp1<T>=(arg:{a:string})=>void
              • T2={a:string,b:number} Comp2<T>=(arg:{a:string,b:number})=>void
              • T2 可以赋值给 T1,但是 Comp2<T> 不能够赋值给 Comp1<T>,刚好和上例中的协变相反
              • Comp1<T> 在被调用时只要求 a 参数,而 Comp2<T> 在调用时需要 a、b 两个参数,把 Comp2<T> 赋值给 Comp1<T>,当 Comp1<T> 被调用时调用者根据 Comp1<T> 的类型传递参数 {a:'1'},这将不能满足函数 Comp2<T> 的需求
            • 双向协变:上例中 Comp2<T> 被允许赋值 Comp1<T> 的话,即 strictFunctionTypes:false,这容易导致错误
            • 不变表示 Comp<T> 类型与 T 类型双向都不兼容
          • strictPropertyInitialization 确保类的非undefined属性已经在构造函数里初始化
        • "jsx": "preserve"
          • 在 .tsx文件里支持JSX
          • 具有三种JSX模式:preserve,react和react-native
          • 这些模式只在代码生成阶段起作用,类型检查并不受影响
          • preserve模式下生成代码中会保留JSX以供后续的转换操作使用,输出文件会带有.jsx扩展名
          • react模式会生成React.createElement,输出文件的扩展名为.js
          • react-native相当于preserve,它也保留了所有的JSX,但是输出文件的扩展名是.js
        • "importHelpers": true,
          • 从 tslib 导入辅助工具函数,应该是用来提供常用函数,减少编译后文件体积大小的
        • "moduleResolution": "node",
          • 决定如何处理模块 module === "AMD" or "System" or "ES6" ? "Classic" : "Node"
          • 决定的是模块的解析方式,根据那种规则寻找 import 路径对应的文件
        • "allowJs": true,
          • 允许编译 javascript 文件
        • "skipLibCheck": true,
          • 忽略所有的声明文件( *.d.ts)的类型检查
          • 当程序包含大型声明文件时,编译器会花费大量时间对已知不包含错误的声明进行类型检查,而跳过声明文件类型检查可能会显着缩短编译时间。
        • "esModuleInterop": true,
          • 支持使用import d from 'cjs'的方式引入 commonjs 包
        • "allowSyntheticDefaultImports": true,
          • 允许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查。
        • "sourceMap": true,
          • 生成相应的 .map文件
        • "baseUrl": ".",
          • 解析非相对模块名的基准目录
        • "types": []
          • 要包含的类型声明文件名列表。
          • "webpack-env"
        • "paths":
          • 模块名到基于 baseUrl的路径映射的列表
          • "@/*": []
            • "src/*"
        • "lib": []
          • 编译过程中需要引入的库文件的列表,应该是类似于 polyfill 的存在
          • "esnext",
          • "dom",
          • "dom.iterable",
          • "scripthost"
      • "include": []
        • "include"和"exclude"属性指定一个文件glob匹配模式列表。"files"指定一个包含相对或绝对文件路径的列表,优先级高于前两者,编译时取他们的并集。
        • "src/**/*.ts",
        • "src/**/*.tsx",
        • "src/**/*.vue",
        • "tests/**/*.ts",
        • "tests/**/*.tsx"
      • "exclude": []
        • "node_modules"
    • shims-tsx.d.ts

      • 该文件用来定义和jsx相关的类型,但是官方给出的定义相当敷衍,建议使用vue-tsx-support库代替
      • declare global
        • namespace JSX
          • JSX 中固有元素<div>总是以一个小写字母开头,基于值的元素<MyComponent>总是以一个大写字母开头。
          • 有两种方式可以定义基于值的元素,无状态函数组件 (SFC)和类组件
          • interface Element extends VNode {}
            • 无状态组件的返回类型限定
            • 无状态组件的第一个参数是 props 对象。 TypeScript会强制它的返回值可以赋值给 JSX.Element。即可以通过定义 JSX.Element 来自定义检查无状态函数组件的返回类型
            • 这是 typescript 的要求,vue 是不支持这种写法的,需要 vuejs/jsx-vue2 进行转换
            • 例如:export default ({ props }) => <p>hello {props.message}</p>
          • interface ElementClass extends Vue {}
            • 类组件实例的类型限定
            • 类组件的实例类型必须赋值给 JSX.ElementClass 或抛出一个错误,即可以通过 JSX.ElementClass 来自定义元素实例类型的检查方法
          • interface IntrinsicElements
            • 固有元素用来查找元素和属性。例如:{input:{readonly?:boolean}}
            • [elem: string]: any;
              • 使固有元素可以是任意字符串,且可以具有任意属性
              • 这是不严谨的,导致固有元素不能出现正确的属性提醒
          • 其他与 jsx 相关
            • interface ElementAttributesProperty{}
              • 基于值的元素,用该属性来定义哪个属性作为元素属性使用
              • 如果未指定JSX.ElementAttributesProperty,那么将使用类元素构造函数或SFC调用的第一个参数的类型
            • 如果一个属性名不是个合法的JS标识符(像data-*属性),并且它没出现在元素属性类型里时不会当做一个错误。
            • interface IntrinsicAttributes{}
              • 该接口用来指定额外的属性,这些额外的属性通常不会被组件的props或arguments使用
            • type IntrinsicClassAttributes
              • 该接口也用来指定额外的属性,这里的泛型参数表示类实例类型
            • type LibraryManagedAttributes<C, P>
              • 提供了一个元素属性运算获取的方法,C 为基于值的元素自身类型,P接收的应该是函数式组件的泛型
              • 这是 vue-tsx-support 的实现方式
            • ElementAttributesProperty{}
              • 从TypeScript 2.3开始引入了children类型检查,该选项用于决定children名
              • 用于检查插槽是否插入正确的元素
            • 编译选项使用的工厂函数是可以通过jsxFactory配置
              • 即编译时以上这些配置项可以被整体替换,也就是本来去 global.JSX 寻找的方法 改为到 global.jsxFactoryName.JSX 下寻找
              • jsxFactory 在 tsconfig.json 中配置,vue-tsx-support 目前采用该方式整体替换
    • shims-vue.d.ts

      • 为 .vue 文件添加模块声明
      • .vue 文件不太适合 ts 的全面支持,不推荐使用,但可以保留该文件的声明,不会有影响。
    declare module "*.vue" {
      import Vue from "vue";
      export default Vue;
    }
    
    • main.js 会被修改文件名后缀为 main.ts,这是必须的,typescript 要求入口文件为 ts 类型
    • HelloWorld.vue 这是一个范例文件,建议删除
    • Home.vue 这是一个范例文件,建议删除

    新增的依赖

    • "@typescript-eslint/eslint-plugin": "^4.18.0"
    • "@typescript-eslint/parser": "^4.18.0"
    • "@vue/eslint-config-typescript": "^7.0.0"
    • "typescript": "~4.1.5"
  • 相关阅读:
    iOS高级教程:处理1000张图片的内存优化
    [转]改变UITextField placeHolder颜色、字体
    pushViewController自定义动画http://blog.csdn.net/ralbatr/article/details/22039233
    获取UIWebView的内容高度
    iOS: 计算 UIWebView 的内容高度
    iOS UIWebView 获取内容实际高度,关闭滚动效果
    UIImage 裁剪图片和等比列缩放图片
    Jqueryui+easyui+easywidgets做的后台界面
    多线程中使用HttpContext.Current为null的解决办法
    jquery图片上传前预览剪裁
  • 原文地址:https://www.cnblogs.com/qq3279338858/p/16185611.html
Copyright © 2020-2023  润新知