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
的使用