vue_cli3+
的 js
版本支持 ts
生成vue
项目的vue_cli
版本为 4.0.5
javascript
应用场景:html
ts
ts
, 不敢彻底入坑yarn add typescript ts-loader --dev // 编译用 yarn add vue-property-decorator // 写vue组件时用 yarn add fork-ts-checker-webpack-plugin --dev // typescript 类型检查的webpack插件 yarn add @types/webpack-env // 包含webpack的类型定义(在tsconfig.json中定义types用,目前没有测试出有什么影响) yarn add @typescript-eslint/parser --dev // eslint中的parse依赖r
vue.config.js
修改 webpack
的 loader
vue.config.js
必须是 js
文件vue
const path = require("path"); function resolve(dir) { return path.join(__dirname, dir); } const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin') module.exports = { // lintOnSave: process.env.NODE_ENV === "development", lintOnSave: true, configureWebpack: { resolve: { extensions: ['.tsx','.ts', '.mjs', '.js', '.jsx', '.vue', '.json', '.wasm'] } }, chainWebpack: config => { // 处理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: true, // 关闭类型检查,即只进行转译(类型检查交给webpack插件(fork-ts-checker-webpack-plugin)在另外一个进程中进行,这就是所谓的多进程方案,若是设置transpileOnly为false, 则编译和类型检查所有由ts-loader来作, 这就是单进程方案.显然多进程方案速度更快) appendTsSuffixTo: ['\\.vue$'], happyPackMode: false }) .end() // 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() // 使用webpack 插件进行typescript 的类型检查 fork-ts-checker-webpack-plugin config .plugin('fork-ts-checker') .use(ForkTsCheckerWebpackPlugin, [{ vue: true, tslint: false, formatter: 'codeframe', checkSyntacticErrors: false, // 由于fork-ts-checker-webpack-plugin是在单独的进程跑的,因此它的错误或警告信息是异步回传给到webpack进程的, 这时编译报错信息只在终端显示,不会在预览的浏览器界面显示报错信息。 // 将async设置为false后,就要求webpack等待fork-ts-checker-webpack-plugin进程返回信息, 这样会在页面显示编译报错信息。不过这样作也可能会拖慢整个webpack的转译等待时间。 // async: false }]) } }
tsconfig.json
文件配置编译 ts
文件规则.java
vue-cli
选择 typescript
版本时的 tsconfig.json
(自定义新增了 "noImplicitAny": false
, "strictPropertyInitialization": false
这个配置是要求定义类的属性时必须初始化赋值,在"strict": true
时自动设置为 true
,这很是不合理,由于咱们在 vue
中属性的值常常在 created/mounted
赋值){ "compilerOptions": { "target": "esnext", "module": "esnext", "strict": true, "strictPropertyInitialization": false "jsx": "preserve", "noImplicitAny": false, "importHelpers": true, "moduleResolution": "node", "experimentalDecorators": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "sourceMap": true, "baseUrl": ".", "types": [ "webpack-env", "jest" ], "paths": { "@/*": [ "src/*" ] }, "lib": [ "esnext", "dom", "dom.iterable", "scripthost" ] }, "include": [ "src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "tests/**/*.ts", "tests/**/*.tsx" ], "exclude": [ "node_modules" ] }
paths
和 types
{ "compilerOptions": { "target": "esnext", // 编译目标语法, 能够写es5, 可是咱们项目的ts-loader前通过了babel-loader "module": "esnext", // "strict": true, "strictPropertyInitialization": false, // strict为true时,默认为true "jsx": "preserve", "noImplicitAny": false, // false表示运行隐式的any类型,也就是容许不设置任何类型, 这个设置运行js文件直接改为ts文件 "importHelpers": true, "moduleResolution": "node", // 和nodejs同样的node_modules机制 "experimentalDecorators": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "sourceMap": true, "baseUrl": ".", "paths": { // 配合baseUrl, ts文件中import 模块路径的解析规则 "@/*": [ "src/*", "src/types/*" ], "*":[ "node_modules/*", "src/types/*" ] }, "lib": [ "esnext", "dom", "dom.iterable", "scripthost" ] }, "include": [ "src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "tests/**/*.ts", "tests/**/*.tsx" ], "exclude": [ "node_modules" ] }
以上两个版本主要是 paths
和 types
不一样, 你须要了解他们的做用,并在工做中设置合适的值.
第二种的其余版本运行把全部的 .d.ts
文件放到 src/types
文件夹内.node
src
文件下新建一个 ts
文件在 src
下新建 shims-vue.d.ts
文件, 不然会报错以下:webpack
ERROR TS18003: No inputs were found in config file 'tsconfig.json'. Specified 'include' paths were '["src/**/*.ts","src/**/*.tsx","src/**/*.vue","tests/**/*.ts","tests/**/*.tsx"]' and 'exclude' paths were '["node_modules"]'.
vueCli3
的 typescript
版本有 shims-vue.d.ts
和 shims-tsx.d.ts
这两个文件,咱们不妨把他们放到 src
下.git
// shim-vue.d.ts declare module '*.vue' { import Vue from 'vue' export default Vue }
// shims-tsx.d.ts 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 } } }
以上文件的原理,详见 typescript
的 module-augmentation
(模块补充: 能够经过路径在文件中增补类型定义)es6
eslint
规则解决使用 ts
内置类型时保报错 ,例如 Partial
.
安装(第一步已经安装)github
yarn add @typescript-eslint/parser --dev
配置 .eslintrc.js
文件的 parser
项为 @typescript-eslint/parser
web
vue-cli(js版)
生成的 .eslintrc.js
中简单修改module.exports = { root: true, env: { node: true, browser: true, es6: true }, 'extends': [ 'plugin:vue/essential', 'eslint:recommended', '@vue/prettier' ], parserOptions: { // parser: 'babel-eslint', parser: '@typescript-eslint/parser', // 解析ts文件, 例如识别ts文件的内置类型 ecmaFeatures: { legacyDecorators: true } }, rules: { 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' }, overrides: [ { files: [ '**/__tests__/*.{j,t}s?(x)', '**/tests/unit/**/*.spec.{j,t}s?(x)' ], env: { jest: true } } ] }
在上述 .eslintrc.js
的配置中, 默认是使用双引号和分号结尾的, 当我在 rules
中修改时,会和 prettier
的配置冲突, 所以在根目录下新建 .prettierrc.js
文件,书写
module.exports = { "printWidth": 80, // 每行代码长度(默认80) "tabWidth": 2, // 每一个tab至关于多少个空格(默认2) "useTabs": false, // 是否使用tab进行缩进(默认false) "singleQuote": true, // 使用单引号(默认false) "semi": false, // 声明结尾使用分号(默认true) "trailingComma": "all", // 多行使用拖尾逗号(默认none) "bracketSpacing": true, // 对象字面量的大括号间使用空格(默认true) "jsxBracketSameLine": false, // 多行JSX中的>放置在最后一行的结尾,而不是另起一行(默认false) "arrowParens": "avoid" // 只有一个参数的箭头函数的参数是否带圆括号(默认avoid) };
module.exports = { root: true, parserOptions: { // +++++++++++ parser: '@typescript-eslint/parser', sourceType: 'module', ecmaFeatures: { legacyDecorators: true } }, env: { browser: true, node: true, es6: true, }, // plugin:包名/配置名称 extends: ['plugin:vue/recommended', 'eslint:recommended'], // add your custom rules here //it is base on https://github.com/vuejs/eslint-config-vue rules: { "vue/max-attributes-per-line": [2, { "singleline": 10, "multiline": { "max": 1, "allowFirstLine": false } }], "vue/singleline-html-element-content-newline": "off", "vue/multiline-html-element-content-newline":"off", "vue/name-property-casing": ["error", "PascalCase"], "vue/no-v-html": "off", 'accessor-pairs': 2, 'arrow-spacing': [2, { 'before': true, 'after': true }], 'block-spacing': [2, 'always'], 'brace-style': [2, '1tbs', { 'allowSingleLine': true }], 'camelcase': [0, { 'properties': 'always' }], 'comma-dangle': [2, 'never'], 'comma-spacing': [2, { 'before': false, 'after': true }], 'comma-style': [2, 'last'], 'constructor-super': 2, 'curly': [2, 'multi-line'], 'dot-location': [2, 'property'], 'eol-last': 2, 'eqeqeq': ["error", "always", {"null": "ignore"}], 'generator-star-spacing': [2, { 'before': true, 'after': true }], 'handle-callback-err': [2, '^(err|error)$'], 'indent': [2, 2, { 'SwitchCase': 1 }], 'jsx-quotes': [2, 'prefer-single'], 'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }], 'keyword-spacing': [2, { 'before': true, 'after': true }], 'new-cap': [2, { 'newIsCap': true, 'capIsNew': false }], 'new-parens': 2, 'no-array-constructor': 2, 'no-caller': 2, 'no-console': 'off', 'no-class-assign': 2, 'no-cond-assign': 2, 'no-const-assign': 2, 'no-control-regex': 0, 'no-delete-var': 2, 'no-dupe-args': 2, 'no-dupe-class-members': 2, 'no-dupe-keys': 2, 'no-duplicate-case': 2, 'no-empty-character-class': 2, 'no-empty-pattern': 2, 'no-eval': 2, 'no-ex-assign': 2, 'no-extend-native': 2, 'no-extra-bind': 2, 'no-extra-boolean-cast': 2, 'no-extra-parens': [2, 'functions'], 'no-fallthrough': 2, 'no-floating-decimal': 2, 'no-func-assign': 2, 'no-implied-eval': 2, 'no-inner-declarations': [2, 'functions'], 'no-invalid-regexp': 2, 'no-irregular-whitespace': 2, 'no-iterator': 2, 'no-label-var': 2, 'no-labels': [2, { 'allowLoop': false, 'allowSwitch': false }], 'no-lone-blocks': 2, 'no-mixed-spaces-and-tabs': 2, 'no-multi-spaces': 2, 'no-multi-str': 2, 'no-multiple-empty-lines': [2, { 'max': 1 }], 'no-native-reassign': 2, 'no-negated-in-lhs': 2, 'no-new-object': 2, 'no-new-require': 2, 'no-new-symbol': 2, 'no-new-wrappers': 2, 'no-obj-calls': 2, 'no-octal': 2, 'no-octal-escape': 2, 'no-path-concat': 2, 'no-proto': 2, 'no-redeclare': 2, 'no-regex-spaces': 2, 'no-return-assign': [2, 'except-parens'], 'no-self-assign': 2, 'no-self-compare': 2, 'no-sequences': 2, 'no-shadow-restricted-names': 2, 'no-spaced-func': 2, 'no-sparse-arrays': 2, 'no-this-before-super': 2, 'no-throw-literal': 2, 'no-trailing-spaces': 2, 'no-undef': 2, 'no-undef-init': 2, 'no-unexpected-multiline': 2, 'no-unmodified-loop-condition': 2, 'no-unneeded-ternary': [2, { 'defaultAssignment': false }], 'no-unreachable': 2, 'no-unsafe-finally': 2, 'no-unused-vars': [2, { 'vars': 'all', 'args': 'none' }], 'no-useless-call': 2, 'no-useless-computed-key': 2, 'no-useless-constructor': 2, 'no-useless-escape': 0, 'no-whitespace-before-property': 2, 'no-with': 2, 'one-var': [2, { 'initialized': 'never' }], 'operator-linebreak': [2, 'after', { 'overrides': { '?': 'before', ':': 'before' } }], 'padded-blocks': [2, 'never'], 'quotes': [2, 'single', { 'avoidEscape': true, 'allowTemplateLiterals': true }], 'semi': [2, 'never'], 'semi-spacing': [2, { 'before': false, 'after': true }], 'space-before-blocks': [2, 'always'], 'space-before-function-paren': [2, 'never'], 'space-in-parens': [2, 'never'], 'space-infix-ops': 2, 'space-unary-ops': [2, { 'words': true, 'nonwords': false }], 'spaced-comment': [2, 'always', { 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] }], 'template-curly-spacing': [2, 'never'], 'use-isnan': 2, 'valid-typeof': 2, 'wrap-iife': [2, 'any'], 'yield-star-spacing': [2, 'both'], 'yoda': [2, 'never'], 'prefer-const': 2, 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, 'object-curly-spacing': [2, 'always', { objectsInObjects: false }], 'array-bracket-spacing': [2, 'never'] } }
若是你写了以下代码, 会报一个错误:
<template> <div>{{message}}</div> </template> <script lang="ts"> import { Vue, Component } from "vue-property-decorator" @Component export default class TestTs extends Vue { message: string = "hello ts!"; mounted() { setTimeout(()=>{ this.message = "hello typeScript!" },2000) } } </script>
错误以下:
error Parsing error: Using the export keyword between a decorator and a class is not allowed. Please use `export @dec class` instead.
解决方法: 修改eslintrc.js
parserOptions: { ecmaFeatures: { legacyDecorators: true } },
vue-property-decorator
的使用