vite项目中的常见问题

开始在个人项目中全量使用vite了,使用了一段时间,感觉开发体验确实很好。本文主要整理了vite项目中的一些常见问题及解决办法。

<!--more-->

1. 自动引入

减少重复的import模块引入代码

1.1. 全局API

在很多文件中都需要引入一些框架API,如Vue的refonMounted等代码,写多了就感觉很重复,可以试试unplugin-auto-import

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

const plugins  = [
    AutoImport({
      // 定义需要自动引入的框架
      imports: ['vue', 'vue-router'], 
      // 处理eslint
      eslintrc: {
        enabled: true,
      },
    }),
]

该插件会自动生成一个auto-imports.d.ts,然后添加到tsconfig.jsoninclude

接着就可以直接写代码,无需再重复引入了

const a = ref(0)

项目如果使用了eslint,可能会提示某些API未引入的错误,这时候可以开启eslintrc.enabled配置项.

这个配置项打开后,会在项目目录生成.eslintrc-auto-import.json文件(实际上就是把自动引入的API声明为globals全局变量)

最后,在.eslintrc.js文件中exteneds配置项把这个文件包括进来就可以了

module.exports = {
  extends: [
    'plugin:vue/vue3-essential',
    'airbnb-base',
    'plugin:@typescript-eslint/recommended',
    './.eslintrc-auto-import.json', // 这一行
  ],
}

1.2. 组件库

引入组件库的时候,出于性能考虑,往往需要实现按需引入相关组件,主流的UI库也提供了相关的插件如babel-plugin-component(ElementUI)等来实现,在vite中可以通过unplugin-vue-components来实现


import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default defineConfig({
  // ...
  plugins: [
    // ...
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
})

之后就再无需手动引入组件的情况下就可以直接使用组件了

<template>
  <el-button>click</el-button>
</template>
<script lang="ts" setup>
// 无需每个组件都显示引入该UI组件了 
// import {ElButton} from 'element-plus'
</script></script>

resolvers内置了十来种主流UI库的按需加载解析器,如果有未包含在内的组件库,也可以很简单的实现一个

Components({
  resolvers: [
    // example of importing Vant
    (componentName) => {
      // where `componentName` is always CapitalCase
      if (componentName.startsWith('Van'))
        return { name: componentName.slice(3), from: 'vant' }
    },
  ],
})

2. MFC多组件文件

Vue默认的组件形式是SFC (Single File Component),一个文件只能对应一个组件,对于某些UI组件创建单个SFC显得有点繁琐。

因此可以考虑下面这种通过tsx实现的MFC组件,即一个文件里面有多个组件

首先需要安装@vitejs/plugin-vue-jsx支持tsx

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'

export default defineConfig({
  plugins: [vue(),vueJsx()],
})

然后就可以编写tsx文件了

// 使用css module
import styles from './comp.module.scss'

type Demo1Props = {
  msg: string
}
export const Demo1 = defineComponent({
  props: ['msg'],
  setup(props: Demo1Props) {
    return () => (<div class={styles.demo1}>demo1 {props.msg}</div>)
  },
})

type Demo2Props = {
  initValue: number
}
export const Demo2 = defineComponent({
  props: ['initValue'],
  setup(props: Demo2Props) {
    const count = ref(props.initValue)
    const add = () => {
      count.value++
    }
    return () => (<div onClick={add}>demo2 {count.value}</div>)
  },
})

在其他的SFC组件中,可以直接引入这个文件里的小组件,维护起来要方便很多,缺点就是无法使用SFC的setup 语法糖等功能,有舍有得。

3. css原子类

原子类大法,在某些场景下还是有点香(但不是万能的~

目前主流的原子类方案都支持了vite,以windicss为例

安装

npm i -D vite-plugin-windicss windicss

vite.config.ts中引入插件

import WindiCSS from 'vite-plugin-windicss'

export default {
  plugins: [
    WindiCSS(),
  ],
}

在入口文件main.ts引入虚拟模块

import 'virtual:windi.css'

然后就可以愉快地编码了。

4. vitest单元测试

vitest是vite项目首选的单元测试框架,当然也可以在非vite项目的环境下直接使用,相信我,体验了之后你就会喜欢上单元测试了。

跟所有测试框架类似,默认约定x.test.ts的文件为测试文件

// util.test.ts
import {describe, it, expect} from "vitest"
import {add} from './util'

describe("util test", () => {
  it("100% pass", () => {
    expect(1 === 1)
  })

  it("add", () => {
    expect(add(1, 2)=== 3)
  })
})

然后运行vitetest即可,更通用的方案是修改package.json的test命令

"scripts": {
    "test": "vitest"
}

除了常规的测试文件用例之外,vitest还支持源码内联测试、类型测试等高级功能,快去试一下~

5. CDN上传构建产物

对于构建之后的资源发布到CDN,有两种处理方式

  • 构建结束后,通过oss sdk直接将文件上传到CDN
  • nginx转发资源路径,通过CDN配置文件回源地址

第一种方式更加灵活,更容易在项目中落地。

社区提供了类似于vite-plugin-ali-oss之类的插件,不过在使用中发现一些不尽人意的地方,因此这里不介绍插件的使用方式,而是大概的实现思路

相关的实现也比较简单

  • 插件注册closebundle的钩子,
  • 这个函数调用时可认为构建已结束,读取输出目录下的所有文件
  • 遍历文件夹和文件,调用oss sdk(比如常见的阿里云、七牛等)上传文件

由于sdk上传文件时需要秘钥等,如果担心直接明文写在vite.config.ts中不太方便权限管理,也可以放在CI中通过环境变量的形式注入。

6. 小结

本文主要整理了vite项目中一些常见的功能,后面会继续补充。