【笔记】Vite+Vue 3+TS 配置记录

3,475 阅读5分钟

* 本文正在参加「金石计划 . 瓜分6万现金大奖」

项目初始化

通过 npm init vite@latest 或者 yarn/pnpm create vite 创建项目

得到如下的 package.json.eslintrc.cjs 文件

// 文件:package.json

"scripts": {
    "dev": "vite",
    "build": "vue-tsc --noEmit && vite build",
    "preview": "vite preview --port 5050",
    "test:unit": "vitest --environment jsdom",
    "test:e2e": "start-server-and-test preview http://127.0.0.1:5050/ 'cypress open'",
    "test:e2e:ci": "start-server-and-test preview http://127.0.0.1:5050/ 'cypress run'",
    "typecheck": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
  },
  "dependencies": {
    "pinia": "^2.0.13",
    "vue": "^3.2.33",
    "vue-router": "^4.0.14"
  },
  "devDependencies": {
    "@rushstack/eslint-patch": "^1.1.0",
    "@types/jsdom": "^16.2.14",
    "@types/node": "^16.11.27",
    "@vitejs/plugin-vue": "^2.3.1",
    "@vitejs/plugin-vue-jsx": "^1.3.10",
    "@vue/eslint-config-prettier": "^7.0.0",
    "@vue/eslint-config-typescript": "^10.0.0",
    "@vue/test-utils": "^2.0.0-rc.20",
    "@vue/tsconfig": "^0.1.3",
    "cypress": "^9.5.4",
    "eslint": "^8.5.0",
    "eslint-plugin-cypress": "^2.12.1",
    "eslint-plugin-vue": "^8.2.0",
    "jsdom": "^19.0.0",
    "prettier": "^2.5.1",
    "start-server-and-test": "^1.14.0",
    "typescript": "~4.6.3",
    "vite": "^2.9.5",
    "vitest": "^0.9.3",
    "vue-tsc": "^0.34.7"
  }
// 文件:.eslintrc.cjs

/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')

module.exports = defineConfig({
  env: {
    browser: true,
    'vue/setup-compiler-macros': true
  },
  extends: [
    'plugin:vue/vue3-essential',
    'eslint:recommended',
    '@vue/eslint-config-typescript/recommended',
    '@vue/eslint-config-prettier'
  ],
  rules: {
    'node/no-extraneous-require': 'off',
    'node/no-missing-import': 'off'
  },
  overrides: [
    {
      files: ['cypress/integration/**.spec.{js,ts,jsx,tsx}'],
      extends: ['plugin:cypress/recommended']
    }
  ]
})

按正常来说,此时编写项目代码应该不会有太多问题。

由于我个人倾向于使用 SFC 方式进行编写,那么 vue 的一些 API 使用就需要自己手动引入了。

// 文件:componentA.vue
import { ref, onMounted, computed, provide, useSlots, ...  } from 'vue'

如果再来有使用到 vue-routerpinia 这些生态包的话那么就有可能进需要手动引入更多。

// 文件:componentA.vue
import { ref, onMounted, computed, provide, useSlots, ...  } from 'vue'
import { useRouter, useRoute, ... } from 'vue-router'
import { storeToRefs, mapActions, mapGetters, mapStores, ...  } from 'pinia'

这样的操作对于少量的组件倒是还行 CV 大法加加减减完事。但如果是要在一个有规划的项目里这样的东西多了,就会显得比较繁琐杂乱。不利于规范的建立。


自动导入

如何能够解决这样的场景那就成为了一个痛点。

好在 @Anthony Fu 开源了一个支持了 vite 的插件 unplugin-auto-import ,这个插件能够按需自动导入所需的包。

unplugin-auto-import 加入到项目之后,修改 vite.config.ts 文件,新增如下内容。

import ...

+ import AutoImport from 'unplugin-auto-import/vite'

...

export default defineConfig({
  ...,
  plugins: [
    ...,
+   AutoImport({imports: ['vue', 'vue-router', 'pinia']})
  ]
})

修改完成后,如果不是使用了 TypeScript 的话,那么此时就可以将组件文件里关于 vuevue-routerpinia 相关的 import 都可以删除了。

- import { ref, onMounted, computed, provide, useSlots, ...  } from 'vue'
- import { useRouter, useRoute, ... } from 'vue-router'
- import { storeToRefs, mapActions, mapGetters, mapStores, ...  } from 'pinia'

+ 

但由于我这里的项目是使用了 TypeScript 所以这时候删除的话,VsCode 还是会提示 eslint 的错误:ref is not defined.

image.png

根据 unplugin-auto-import 文档 说明,插件也是支持了 TypeScript 的 d.ts 类型描述文件的生成,vite.config.ts 里改动如下。

import ...

import AutoImport from 'unplugin-auto-import/vite'

...

export default defineConfig({
  ...,
  plugins: [
    ...,
-   AutoImport({ imports: ['vue', 'vue-router', 'pinia'] }),
+   AutoImport({
+     imports: ['vue', 'vue-router', 'pinia'],
+     dts: 'types/auto-imports.d.ts',
+     eslintrc: {
+       enabled: false,
+       filepath: 'types/.eslintrc-auto-import.json',
+       globalsPropValue: true,
+     },
+   }),
  ],
})

参数解释看这里:configuration

新增的这些参数主要还是用于支持 TypeScript,我这里是习惯于将 *.d.ts 这类文件统一放置在 types 文件夹下。

将文件生成后,那么 types 文件夹下会将出现 auto-imports.d.ts.eslintrc-auto-import.json 两个文件。这两个文件将需要分别配置到 tsconfig.json.eslintrc.cjs 中。

关系如下:

  • tsconfig.json <- auto-imports.d.ts
  • .eslintrc.cjs <- .eslintrc-auto-import.json

修改 tsconfig.json

{
  "include": [
    ...,
+   "types/auto-imports.d.ts"
  ]
}

修改 .eslintrc.cjs

module.exports = defineConfig({
  extends: [
    ...,
+   './types/.eslintrc-auto-import.json'
  ]
})

修改完成后就能得到正确的类型提示了。

image.png


自定义类型支持

使用 TypeScript 的项目中难免会有一些自定义的类型,虽然 与文件同名.d.ts 能够解决当前文件的类型定义,但总是会有一些公共的类型需要定义,例如请求返回的自定义数据体。

假设自定义一个类型文件:types/request.d.ts

// 文件:request.d.ts

// 假设返回数据结构如下
type ResultData = {
  code: number
  msg: string
  data: unknown
}

这时候在非当前文件中使用该类型定义的时候,则会提示:ResultData is not defined.

非 *.ts 文件是由 eslint 提示错误。

image.png

*.ts 文件是由 TS 自身提示错误。

image.png

如有多个文件中都使用了请求函数,那么就需要编写多次或者引入类型文件使用该类型,这样并不利于管理也过于散乱,也有可能修改一处,其它处都需要修改。

所以我就想着是否可以通过配置的方式实现全局使用该类型呢?

其实是可以的。

解决 *.ts 使用自定义类型报错提示

解决 *.ts 文件的报错就比较简单了,types/request.d.ts 文件修改如下。

// 文件:types/request.d.ts
- type ResultData = {
+ declare type ResultData = {
  code: number
  msg: string
  data: unknown
}

tsconfig.json 文件修改如下。

{
  "include": [
    ...,
+   "types/request.d.ts"
  ]
}

修改完成后即可得到正确的类型提示,也不需要再次定义或者引入额外的类型文件。

image.png

解决非 *.ts 使用自定义类型报错提示

*.ts 文件的类型使用报错解决了,但这只是针对于 *.ts 的,而非 *.ts 的文件使用该类型还是会有 eslint 的错误提示。

新增一个 types/.eslintrc-request.json 文件用于 eslint 错误提示的解决。

{
  "globals": {
    "ResultData": true
  }
}

.eslintrc.cjs 文件修改如下。

module.exports = defineConfig({
  extends: [
    ...,
+   './types/.eslintrc-request.json'
  ]
})

文件添加完成后也可得到了正确的类型提示了。

文件:componentA.vue
image.png


组件命名

关于给组件命名的问题,Vue 3 所提供的方式有限,且也不够优雅,写起来终是感觉有点奇怪。

对于这个问题,我去看了一些开源库的应用最终目标指向为 unplugin-vue-define-options三咲智子开源的插件。

正常来说仅需要在 vite.config.ts 新增如下内容即可实现。

import ...

+ import DefineOptions from 'unplugin-vue-define-options/vite'

...

export default defineConfig({
  ...,
  plugins: [
    ...,
+   DefineOptions()
  ]
})

TypeScript 项目则需要修改 tsconfig.json 文件,新增类型支持。

{
  ...,
  "compilerOptions": {
+   "types": ["unplugin-vue-define-options/macros-global"]
  }
}

使用 defineOptions 函数即可定义。

// 文件:componentA.vue
defineOptions({
  name: 'Dialog'
})

pnpm-workspace package 分项目的问题

但由于我这使用了 pnpm-workspace,那么 A项目 的 UI 组件由 B项目 目提供。

目录结构如下:

- packages
  - A
  - B

其关系是使用 "workspace:*" 方式绑定,而 B项目 除必要包外都由 A项目 引入。

那么这个时候在 B项目 中使用 defineOptions 就是会出现报错的:defineOptions is not defined

image.png

A项目tsconfig.json 配置 B项目 别名路径即可。


版权声明:
本文版权属于作者 林小帅,未经授权不得转载及二次修改。
转载或合作请在下方留言及联系方式。