为啥 vue 在第一次编译时无法识别 TS 语法,但在第二次编译时可以正常工作?

Posted

技术标签:

【中文标题】为啥 vue 在第一次编译时无法识别 TS 语法,但在第二次编译时可以正常工作?【英文标题】:Why does vue fail to recognize TS syntax on the first compilation, but works okay on the second one?为什么 vue 在第一次编译时无法识别 TS 语法,但在第二次编译时可以正常工作? 【发布时间】:2020-06-11 20:51:03 【问题描述】:

我遇到了一个奇怪的错误,即 vue cli 在第一次执行时无法识别 TypeScript 语法,但在第二次执行时工作正常。

如果删除node_modules/.cache再执行命令:

yarn build

您在第一次调用时收到此错误:

 ERROR  Failed to compile with 1 errors                                                                                           3:54:55 PM
 error  in ./src/components/ATSText/TextDate.vue

Module Error (from ./node_modules/eslint-loader/index.js):
error: Parsing error: Unexpected token : at src\components\ATSText\TextDate.vue:69:11:
  67 |   computed: 
  68 |     dateFormatted: 
> 69 |       get(): string 
     |           ^
  70 |         return this.formatDate(this.value);
  71 |       ,
  72 |       set(val: string) 


1 error found.

 @ ./src/utils/schemaConverter.ts 13:47-92
 @ ./node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/ts-loader??ref--14-3!./node_modules/vuetify-loader/lib/loader.js??ref--20-0!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/views/Schema.vue?vue&type=script&lang=ts&
 @ ./src/views/Schema.vue?vue&type=script&lang=ts&
 @ ./src/views/Schema.vue
 @ ./src/router/index.ts
 @ ./src/main.ts
 @ multi ./src/main.ts

 ERROR  Build failed with errors.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

如果再次执行相同的命令,编译会正常:

 DONE  Build complete. The dist directory is ready to be deployed.
 INFO  Check out deployment instructions at https://cli.vuejs.org/guide/deployment.html

Done in 28.14s.

如果删除缓存并重复该过程,则循环重复。

作为参考,这些是相关的源文件:

TextDate.vue:

<template>
  <v-menu
    ref="menu"
    v-model="menu"
    offset-y
    transition="scale-transition"
    max-
    min-
    :close-on-content-click="false"
  >
    <template v-slot:activator=" on ">
      <v-text-field
        v-model="dateFormatted"
        v-mask="'##/##/####'"
        v-on="on"
        dense
        outlined
        hide-details
        prepend-inner-icon="event"
        :label="label"
        :tabindex="tabindex"
        :value="value"
        @blur="onblur"
        @input="oninput"
      />
    </template>
    <v-date-picker 
      v-model="date" 
      no-title 
      @input="menu = false"
    />
  </v-menu> 
</template>

<script lang="ts">
import Vue from 'vue'
export default Vue.extend(
  props: 
    label: String,
    hideDetails: 
      type: [String, Boolean],
      default: false
    ,
    noResize: 
      type: Boolean,
      default: true
    ,
    value: String,
    hint: String,
    tabindex: [String, Number]
  ,
  data() 
    return 
      menu: undefined,
      date: undefined
    
  ,
  watch: 
    date(val) 
      if (val)
        this.dateFormatted = this.formatDate(val);
    ,
    value(val) 
      this.date = val
    
  ,
  computed: 
    dateFormatted: 
      get(): string  
        return this.formatDate(this.value); 
      ,
      set(val: string)  
        this.$emit('input', this.parseDate(val)); 
      
    ,
  ,
  methods: 
    oninput(val) 
      this.date = this.parseDate(val);      
      this.$emit('input', this.date);
    ,
    onblur() 
      if (!this.date) 
        this.date = this.parseDateForce(this.dateFormatted)
    ,
    formatDate (date) 
      if (!date) return null
      const [year, month, day] = date.split('-')
      return `$day/$month/$year`
    ,
    parseDate (date) 
      if (!date || (date.length < 10))
        return null;
      const [day, month, year] = date.split('/')

      const result = `$year-$month.padStart(2, '0')-$day.padStart(2, '0')`;
      const nDate = new Date(result);
      if (!(nDate.getTime() === nDate.getTime()))
        return null;
      return result;
    ,
    parseDateForce (date) 
      if (!date || (date.length < 8))
        return null;
      const [day, month, year] = date.split('/')
      let result
      if (year >= 70) 
        result = `$year.padStart(4, '1900')-$month.padStart(2, '0')-$day.padStart(2, '0')`;
      else
        result = `$year.padStart(4, '2000')-$month.padStart(2, '0')-$day.padStart(2, '0')`;
      const nDate = new Date(result);
      if (!(nDate.getTime() === nDate.getTime()))
        return null;
      return result;
    ,
  ,
)
</script>

package.json


  "name": "framework-ats",
  "version": "0.1.0",
  "private": true,
  "scripts": 
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "electron:build": "vue-cli-service electron:build",
    "electron:serve": "vue-cli-service electron:serve",
    "postinstall": "electron-builder install-app-deps",
    "postuninstall": "electron-builder install-app-deps"
  ,
  "main": "background.ts",
  "dependencies": 
    "@rauschma/stringio": "^1.4.0",
    "@types/lodash": "^4.14.149",
    "ajv-i18n": "^3.5.0",
    "core-js": "^3.4.4",
    "lodash": "^4.17.15",
    "node-firebird": "^0.8.9",
    "typed-rest-client": "^1.7.1",
    "typescript-ioc": "^1.2.6",
    "v-money": "^0.8.1",
    "vue": "^2.6.10",
    "vue-class-component": "^7.2.2",
    "vue-form-json-schema": "^2.5.0",
    "vue-property-decorator": "^8.3.0",
    "vue-router": "^3.1.5",
    "vue-the-mask": "^0.11.1",
    "vuetify": "^2.1.0"
  ,
  "devDependencies": 
    "@vue/cli-plugin-babel": "^4.1.0",
    "@vue/cli-plugin-eslint": "^4.1.0",
    "@vue/cli-plugin-router": "^4.1.0",
    "@vue/cli-plugin-typescript": "^4.2.2",
    "@vue/cli-service": "^4.1.0",
    "@vue/eslint-config-typescript": "^4.0.0",
    "babel-eslint": "^10.0.3",
    "electron": "^6.0.0",
    "eslint": "^5.16.0",
    "eslint-plugin-vue": "^5.0.0",
    "material-design-icons-iconfont": "^5.0.1",
    "sass": "^1.19.0",
    "sass-loader": "^8.0.0",
    "typescript": "~3.7.5",
    "vue-cli-plugin-electron-builder": "^1.4.4",
    "vue-cli-plugin-vuetify": "^2.0.4",
    "vue-template-compiler": "^2.6.10",
    "vuetify-loader": "^1.3.0"
  

tsconfig.json:


  "compilerOptions": 
    "target": "es6",
    "module": "commonjs",
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "allowJs": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "baseUrl": ".",
    "typeRoots": [
      "./node_modules/@types",
      "./node_modules/vuetify/types"
    ],
    "types": [
      "webpack-env",
      "vuetify"
    ],
    "paths": 
      "@/*": [
        "src/*"
      ]
    ,
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  ,
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]

vue.config.ts:

module.exports = 
  "devServer": 
    "disableHostCheck": true
  ,
  "transpileDependencies": [
    "vuetify"
  ]

【问题讨论】:

【参考方案1】:

eslintrc 中使用@typescript-eslint/parserparserOptions.parser 可以解决问题。

https://github.com/vuejs/vue-cli/issues/5227#issuecomment-788639361

【讨论】:

【参考方案2】:

更正实际上很简单,但很难找到: https://github.com/vuejs/vue-cli/issues/5227

为了解决问题,需要修改.eslintrc.js,将"parser": "babel-eslint"添加到parserOptions中:

module.exports = 
    "env": 
        "browser": true,
        "es6": true,
        "node": true
    ,
    "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/eslint-recommended",
        "plugin:@typescript-eslint/recommended",
        "plugin:vue/essential"
    ],
    "globals": 
        "Atomics": "readonly",
        "SharedArrayBuffer": "readonly"
    ,
    "parserOptions": 
        "parser": "babel-eslint",
        "ecmaVersion": 6,
        "sourceType": "module",
        "ecmaFeatures": 
            "modules": true
        
    ,
    "plugins": [
        "vue"
    ],
    "rules": 
        "no-console": "off",
        "@typescript-eslint/indent": "off"
    
;

【讨论】:

以上是关于为啥 vue 在第一次编译时无法识别 TS 语法,但在第二次编译时可以正常工作?的主要内容,如果未能解决你的问题,请参考以下文章

【vue3.x+vite】.ts文件识别不了.vue 文件

Vue.config.js 无法识别 require('webpack')

前端vue中ts无法识别引入的vue文件,提示找不到xxx.vue模块的解决引入新建页面或者通过router引入时报错

前端vue中ts无法识别引入的vue文件,提示找不到xxx.vue模块的解决引入新建页面或者通过router引入时报错

前端vue中ts无法识别引入的vue文件,提示找不到xxx.vue模块的解决引入新建页面或者通过router引入时报错

如何在vue中使用ts