这是一份保姆级Vue3+Vite实战教程

36,067 阅读37分钟

写在前面

很长时间没有更新文章了,由于这次签约专栏不能和之前的专栏重复,外加最近又有很多小伙伴问我 Vue3 在实战中的注意事项和技巧,我想了想,决定写一个 Vue3 实战专栏,我理解的项目实战,就是大家在看到这篇文章的时候,我刚创建好项目,然后目前码字的我就是在一边写项目一边写文章。

在刚刚开始接触前端的时候就一直想写一个工具大全的网站,一直没闲下来写,正好凑着这个机会搞一下,那这个过程中可以随时会遇到一些问题,我们就以项目开发中最平常的角度去解决这些问题,一个项目从想法到落地,从产生问题再到解决问题,这一切会有太多的不可预知,也正贴合我们实际工作中开发项目。

那我们来介绍下要做的这个项目。

日常工作生活中,总会用到一些小工具,每次想要使用一些工具时,标准流程是百度、谷歌搜索 免费XXX网站 ,然后在搜索列表中一个一个的点击查看是否免费,遇到好用的可能会收藏一下网站,但是下次再使用此类工具的时候,大家什么样子我不知道,但是我自己通常不会去收藏夹找这个工具网站,而是接着百度、谷歌开启循环,随之而来的就是收藏的工具网站越来越多,五花八门。

那都已经做这么久程序员了,为什么不自己开发一个工具类的聚合网站呢?需求自己决定,想用啥写啥,于是,这个我自己都不知道要未来都会写些什么的工具聚合网站就成了这个专栏的实战项目。

那我们会做一些什么工具呢?

如上所说,我自己都不知道这个项目要未来都会写些什么工具,当然我们还是有些目标的,因为有很多常用工具,像图片处理中压缩、格式转换、合成、裁切、水印、隐式水印?像文件处理中格式转换、预览、编辑?像颜色处理中格式转换、对比色、互补色、中差色、临近色生成?像编码转换、加密解密、正则可视化、在线 Markdown?甚至你看完这篇文章在评论区留言我想要一个 XXX 工具,然后我看到了,可能就将是下一篇要写的。你也可以自己写工具 PR 到项目,采用的话我也会参考实现去出文章教程。

那我们可以在这个项目中学到什么呢?

首先,很多同学还没用过 Vue3 以及 Vite,这部分同学可以学习一下 Vue3 生态在实战中一些使用。

其次,已经在使用 Vue3 的同学,也可以学习一下 Vue3 生态在实战中的一些技巧。

当然,之后我们写各种各样的工具,一定会触及到很多知识点,你以为写个颜色工具只会讲代码怎么实现?不,预想中应该是要把 RGBHEXRGBAHSLHSLAHSVHSB对比色互补色中差色临近色等等等等这些色彩相关理论说一下的,直接卷到设计岗,从此和 UI 小姐姐们说话挺直腰板,告别程序员审美。你以为写个正则可视化只是简单写个工具?不,预想中写工具只是幌子,应该是一片大家其乐融融一块学正则的画面,边学边用可 Copy 的正则可视化工具才是我们的目标。

既然是工具网站为了方便使用我们肯定是要写浏览器插件的,后续也能学到一个基础的浏览器插件开发的过程。

最后,希望我不会太监(断更),那这么随意的项目做下去很大可能是一块见证代码屎山的形成,但这重要吗?不重要,重要的是能从中汲取经验、思路和开发技巧。

此文主要介绍项目初始化的一些准备工作,那你以为这篇文章只是简单搭建一个项目?没错,它确实是,但也会涉及到方方面面,鉴于实战类的文章新同学可能很多,所以我尽可能的写的详细。

所以,你被唬住了吗?据不知名人士(正是在下)透露,点个赞再看有助于全文完整阅读。

创建项目

创建一个 Vue3+Vite 项目

首先我们要创建一个 Vue3+Vite 项目,目前 Vue 官方创建项目时默认就是 Vite 构建了,所以直接按照官网来就可以,如下:

确保你安装了最新版本的 Node.js,然后在命令行中运行以下命令

npm init vue@latest

# or

pnpm create vue@latest

PS: 我这边使用的是 pnpm ,它的优缺点我就不说了,大家自行百度吧,不过后面所有的命令中我都会写 npmpnpm 两种,大家想用啥都无所谓的,工具而已,但是 pnpm 真的很香。

这一指令将会安装并执行 create-vue,它是 Vue 官方的项目脚手架工具。你将会看到一些诸如 TypeScript 和测试支持之类的可选功能提示,我们的选择如下:

✔ Project name(项目名): toolsdog
✔ Add TypeScript(添加TS)? : No
✔ Add JSX Support(添加JSX支持)? : No
✔ Add Vue Router for Single Page Application development(添加Vue-router)? : Yes
✔ Add Pinia for state management(添加状态管理Pinia)? : Yes
✔ Add Vitest for Unit testing(为单元测试添加Vitest)? : No
✔ Add Cypress for both Unit and End-to-End testing(为单元测试与端到端测试添加Cypress)? : No
✔ Add ESLint for code quality(为代码质量添加ESLint)? : Yes
✔ Add Prettier for code formatting(为代码格式添加Prettier)? : Yes

Scaffolding project in ./tooldog...
Done.

OK,解释一下

没有使用 TS 只是单纯的不太喜欢 TS,虽然有很多优点,但是个人认为它是一种束缚,大家想用的话选个 Yes 也可以,我们这里选了 No

JSX 如果要使用的话,不如直接使用 React,所以我们选了 No

做项目 VueRouter 必选,不解释。

状态管理也是必选,目前官方都不推荐 Vuex 而是 Pinia 了,后续大家用起来也是逃不过真香定律的。

Unit(单元测试)和 E2E(端到端测试),做组件库之类的是需要搞的,但一般对做项目来说,会很少留给我们写 UnitE2E 的时间,所以我们先不选了,后续有时间写再单独安装(没有这个可能 🤥)。

最后的 ESLintPrettier,一个保证质量,一个保证风格,必选,我们都选择 Yes 就好了。

这里都很基础,没有什么特殊要说的,不过我留意到很多新手前端甚至很多有些经验的前端都搞不懂 ESLintPrettier 的区别,所以这里我们单独提一下。

2022年了,搞清楚 ESLint 和 Prettier 吧

先看 ESLint,它是一个 Lint 工具,Lint 工具专注于检测并修复错误。

ESLint 不仅能够检测代码中的错误,而且在许多情况下,可以为我们自动纠正错误。我们可以通过安装 ESlint 的相关插件,在各种 IDE 编译器下进行应用,通过实时检测代码格式问题或者是语法问题,可以使得程序员在编码时犯更少的错误,当然也可以通过命令行或者插件去自动修复问题。说白了,ESLint 这种 Lint 工具它是一个基本的静态代码分析器,使用 Lint 工具不会使我们的代码免受任何业务逻辑缺陷的影响,反而它会确保我们的代码在语法上准确并遵循一些最佳实践。

再来看 Prettier,它就是一个代码格式化工具,代码格式化工具并不会校验代码中的语法错误,而是在代码易读性上面下功夫。

Prettier 翻译过来是漂亮,其实意思就很明显了,就是让你的代码更漂亮,核心就是格式化代码,确保我们的代码遵守一些一致的格式化规则,比如像制表符宽度、单双引号、尾随逗号、行尾分号等等。这样的格式化规则可以确保我们的代码即使由不同的开发人员开发,或者是在不同的编辑器中也能保持一致,以此来保证整个项目中代码风格的一致性。

所以,为了满足程序的标准化,减少由于手动排版代码带来的时间消费,我们通常会将 ESLint 和 Prettier 一起应用,同时处理代码语法错误和代码风格格式化。它们两个的核心是并不冲突的,可能有人会问,我配置的 ESLint 和 Prettier 怎么经常冲突?

举个例子,我们在 ESLint 配置了一个代码规则,禁止代码尾部使用分号,但是如果我们项目中还用了 Prettier 做代码格式化,并且 Prettier 中配置了代码末尾自动加分号的规则,那就凉了,只要你在代码末尾使用了分号,ESLint 检测到就会给你警告(具体是报错还是警告基于你的 Lint 配置),那一般 ESLint 会默认自动给我们修复这个简单警告去掉代码后面分号,但是这个时候 Prettier 在格式化代码的时候检测到没有分号就会再给我们加上分号。。。

所以,即使有冲突,冲突的也是我们自己的配置,而不是ESLint 、 Prettier 本身有冲突,这点我们在配置的时候注意一下就好了。

如果看到这里还有人觉得这两个东西功能重复,用一个就好了。。。那你可能需要再看一次上文内容并且好好思考下这两个东西的核心是什么,他们只是在配置规则上少量重复,有一点点交集罢了。

毕竟 Prettier 不能为你检测因为摸鱼而产生的代码上的一些小问题,ESLint 也不能让你整个团队的代码风格保持一致,不是吗?

把项目跑起来

跑一下项目

上面我们初始化好了项目,那接下来先来启起来项目:

# 移动到项目根目录
cd toolsdog
# 安装依赖
npm install
# 启动项目
npm run dev

#or

cd toolsdog
pnpm install
pnpm dev

如果你的项目跑起来了,打开浏览器,应该就可以看到下面界面:

当然,还有一些同学是启不起来的,估计很多的报错是这个:

Error: Cannot find module 'node:url'

这是因为我们创建的项目下载的包里用了 node 较高版本的语法,比如

// vite.config.js 中引入 node url 模块时使用了 'node:url'
// 详见:https://nodejs.org/dist/latest-v16.x/docs/api/url.html#url
import { fileURLToPath, URL } from 'node:url'

Vue 官方文档上明确说了开始项目之前,请确保安装了最新版本的 NodeJS,我们的 node 版本要在 v16+ ,所以出现这种问题你的电脑 node 版本可能有些老了,赶快 node -v 查看一下自己电脑的 node 版本吧!

PS: 对于经验不多的同学,可能认为换个版本太麻烦,所以,下面还是简单介绍下切换 node 版本包括切换 npm 镜像源,这是很基础的常识,新同学要敲黑板了。

nvm管理&切换Node版本

切换不同版本的 node 对前端来说是很常见的事情(比如那个可恶的 node-sass,就不支持 node16+ 。。。),如果每次都删除重装,那也太麻烦了,我们可以直接使用 nvm 工具去管理多个版本的 node,想使用哪个版本,安装好直接切换使用即可。

Windows 同学安装 nvm

使用 win 的同学需注意,安装 nvm 之前需要先卸载掉 node,所以安装前可以先查看下当前本机 node 版本,下载好 nvm 后再通过 nvm 安装上这个版本 node,防止突然换了其他版本 node 后对当前电脑上的项目造成一些影响。

使用 win 的同学安装起来可以很简单,打开 nvm-windows,找到最新版本。

如上图

nvm-noinstall.zip # 绿色免安装版,使用时需进行配置
nvm-setup.zip     # 全自动安装版,推荐使用

新手同学建议直接下载一键自动安装版就行,想挑战一下自己,也可以下载绿色免安装版本,不过需要自己配置一些环境变量(希望不会有这种大聪明,有简单的方式就用简单的嘛)。

下载下来 nvm-setup.zip 解压后,直接双击一路 next 安装即可,需要注意安装时选择安装地址,只要你选的安装地址目录没有中文和空格,一般不会出现问题的。

安装好后,打开 cmd 输入 nvm 回车显示 nvm 帮助命令提示即安装成功!

Mac 同学安装 nvm

Mac 同学如果装了 brew,那直接使用它安装即可

brew install nvm

如果没有 brew,使用官方 sh 脚本安装也可以,查看官方最新版脚本

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash

当然,无论你用那种安装,安装好了之后都得去配置环境变量。

写环境变量前我们需要创建一下 .nvm 文件

mkdir ~/.nvm

可能我们安装 nvm 时自动创建了 .nvm 文件,但有些不会自动创建,mkdir 一下更安全,如果已经存在了,就会报该文件已存在,无伤大雅。

然后是写入环境变量。

brew 安装方式需要写入的环境变量:

export NVM_DIR="$HOME/.nvm"
  [ -s "/usr/local/opt/nvm/nvm.sh" ] && . "/usr/local/opt/nvm/nvm.sh"  # This loads nvm
  [ -s "/usr/local/opt/nvm/etc/bash_completion.d/nvm" ] && . "/usr/local/opt/nvm/etc/bash_completion.d/nvm"  # This loads nvm bash_completion

脚本安装方式需要写入的环境变量:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

我们需要把上述对应的环境变量配置写入配置文件中,mac 的配置文件有 2 个,分别是 .bash_profile & .zshrc ,那它们有什么区别呢?

.bash_profile.zshrc 文件均在 根目录下。

当我们修改 .bash_profile 配置文件,运行 source ~/.bash_profile 重新加载配置文件后,此配置文件只在当前窗口生效,电脑一重启就无了。

而我们修改 .zshrc 配置文件 ,运行 source ~/.zshrc 重新加载配置文件后,这个配置是永久生效的,因为我们的电脑每次重启时都会自动去执行一次 source ~/.zshrc 命令。

一般 mac 用户都会在 ~/.zshrc 文件末尾添加一句 source ~/.bash_profile 命令,这其实就是为了确保 .bash_profile 中的修改也可以随着.zshrc 配置文件的运行而永久生效。

所以,你如果也在 ~/.zshrc 文件末尾写了执行 ~/.bash_profile 的命令,那你在写环境变量时,写到任意一个配置文件里都可以,否则的话就老老实实在 ~/.zshrc 文件写入环境变量吧!!!

我们直接在终端通过 vim 去编辑配置文件

vim ~/.zshrc

不同的安装方式去 copy 上面对应的环境变量配置,写入到配置文件就行了。

写入成功后,按 esc 键,然后输入 :wq 保存并退出 vim 修改,此为 vim 命令,不懂的同学自行 Google

最后重新加载配置文件

source ~/.zshrc

注意需要重启终端哦,重启后终端输入 nvm 回车显示 nvm 帮助命令提示即安装成功!

nvm 常用命令

nvm ls-remote # 查看node所有版本
nvm install node # 安装最新node可用版本
nvm version/nvm current # 查看当前nvm使用node版本
nvm list available # 查看可安装node版本
nvm list/nvm ls # 查看已安装版本
nvm install <version> # 安装指定node版本
nvm uninstall <version> # 卸载指定node版本
nvm use <version> # 切换使用指定版本node
nvm use [version] [arch] # 切换指定node版本和位数
nvm reinstall-packages <version> # 在当前版本node环境下,重新全局安装指定版本号的npm包

nvm on # 打开nodejs控制
nvm off # 关闭nodejs控制
nvm alias <name> <version> # 给不同的版本号添加别名
nvm unalias <name> # 删除已定义别名
nvm proxy # 查看设置与代理
nvm node_mirror [url] # 设置setting.txt中的node_mirror,如果不设置的默认是 https://nodejs.org/dist/
nvm npm_mirror [url] # 设置setting.txt中的npm_mirror,如果不设置的话默认的是: https://github.com/npm/npm/archive/.
nvm root [path] # 设置和查看root路径

如上, nvm 安装完成后我们查看下 node 版本,然后安装上想要使用的 node 版本就可以了,那针对我们这次 Vue3+Vite 项目,需要使用到 node16+ 版本,那么我们可以直接选择安装 16+ 大版本中最新最稳定的小版本,即 v16.17.0 (Latest LTS: Gallium)

之前还有同学问我 LatestLTSGallium 这些都是啥意思,这。。。好吧,给新同学们解释下。。

软件工程中,我们对一个项目的整个生命周期是没有一套常规定义的,所以不同的项目用的标识是有区别的,到如今,大致项目用的其实也差不多了,比较常用的几个我简单列了,如下:

Dev # 开发版,频繁出新功能,另外还修复了一些Bug和不稳定因素
Alpha # 软件或系统的内部测试版本,会有不少Bug,仅内部人员使用浏览器
Beta # 软件或系统的测试版本,这一版本一般是在Alpha版本后,会有不少新功能,同时也有很多Bug
Gamma # 软件或系统接近于成熟的版本,只须要作一些小的改进就能发行测试
RC # 发行候选版本,和Beta版最大的差别在于Beta阶段会一直加入新的功能,但是到了RC版本,几乎不会加入新功能,主要在于除错。RC版本是最终发放给用户的最接近正式版的版本,发行后改正bug就是正式版,正式版之前的最后一个测试版。
Release # 正式发布版本,最终交付用户使用的一个版本,该版本也称为标准版,也可用符号 ® 表示
GA # 也代表正式发布版本,这个版本也是正式版本,国外大多都是用GA来说明Release版本
Stable # 稳定版,在开源软件中,都有stable版,就是开源软件的最终发行版,此版本一般基于Beta版,已知Bug都被修复,一般情况下更新较慢
LTS # 长期支持版,这一版本会持续进行支持,最早用在 Ubuntu

所以,LTS 就是长期支持版,在 node 中它代表此版本会长期进行支持,很稳定,放心使用的意思。当然,这么多标识是没有必要死记硬背的,记住个大概就行,有看到不懂就万能 Coogle 嘛。

Latest 就是字面理解的最新版本的意思。

最后的 Gallium 其实就是 node 发版对应的一个代号,这个就比较随意了,就是个情怀,比如大家耳熟能详的 Vue ,扒一扒发版记录,它的每次发版都有代号:

Vue3.0 # One Piece:海贼王
Vue2.7 # Naruto:火影忍者
Vue2.6 # Macross:超时空要塞
Vue2.5 # Level E:灵异E接触
Vue2.4 # Kill la Kill:斩服少女
Vue2.3 # JoJo's Bizarre Adventure:JoJo的奇妙冒险
Vue2.2 # Initial D:头文字D 
Vue2.1 # Hunter X Hunter:全职猎人
Vue2.0 # Ghost in the Shell:攻壳机动队
Vue1.0 # Evangelion:新世纪福音战士
Vue0.12 # Dragon Ball:龙珠
Vue0.11 # Cowboy Bebop:星际牛仔
Vue0.10 # Blade Runner:银翼杀手
Vue0.9  # Animatrix:黑客帝国动画版

所以,你了解了吗?如果你运行项目报了上述错误,那么在安装并使用上高版本 node 之后,重启编辑器再次运行项目,应该就可以看到启动成功了!

nrm管理&切换镜像源

除了管理 node 版本外,npm 镜像源的管理在开发中也是比较常用的,那假如你没有翻出墙的话,可能安装某些依赖会很慢甚至失败,你可以尝试切换一下 npm 镜像源,或者你的公司有 npm 私服,你也可以设置一个公司私服的源地址,使用 nrm 能够很好的管理和切换这些源

全局安装

npm install -g nrm

当你安装完成后,可以使用 nrm ls 命令查看当前可用的源

如上,这是我电脑 nrm 管理的镜像源列表,最后一个是我公司内网的所以打码了,除了最后一个都是安装 nrm 自带的,你可以使用 nrm use <registry> 命令很方便的去切换源,npm 默认就是列表中名为 npm 的源,如果你下载不下来一些工具,可以切换为 taobao 源,当然安装完成后最好切回来,因为除了 npm 源,其他源在下载某些包时,都可能会出现一些莫名的问题。

常用命令

nrm -h /nrm -help  # 查看 nrm 帮助(相关命令、信息)
nrm -V             # 查看当前 nrm 版本
nrm ls             # 查看当前 nrm 中可用的镜像源地址
nrm current        # 查看当前使用镜像源

nrm use <registry> # 切换为某个镜像源 registry-镜像源名
nrm add <registry> <url> # 添加一个镜像源 registry-镜像源名 url-镜像源地址
nrm del <registry>  # 删除一个镜像源
nrm test <registry> # 测试该镜像源下载响应时间

PS: 当然还有像 yrm 等其他管理源的工具,但工具不在多,而是够用就行。

让项目更健壮

了解项目中每一个文件的作用

到此我们已经初步创建并启动了项目,其实很多人只关注代码开发相关的文件,并不会去纠结项目中和核心开发无关配置文件的作用,这是不对的,我们应该对自己的项目做到极致掌控,了解项目中每一个文件每一行代码对项目的作用,接下来就来一起看看我们创建的项目中所有文件的作用吧!

在初始化创建项目时,默认创建了很多子文件(一些组件、样式文件等等),我们先把不需要的项目无关文件删干净,需要我们处理的无用文件都在 src 文件夹下:

  • 删除 src/views 下所有文件
  • 删除 src/stores 下所有文件
  • 删除 src/components 下所有文件
  • 删除 src/assets 下所有文件

清除干净之后,我们在 src/views 文件夹下新建一个 HomePage.vue 文件,随便写点东西:

<script setup></script>

<template>
  <div>hello isboyjc, This is toolsdog home page!</div>
</template>

<style scoped></style>

然后修改一下 router/index.js 路由文件,把之前删掉页面的路由干掉,加上 HomePage 页面的路由

import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'HomePage',
      component: () => import('@/views/HomePage.vue')
    }
  ]
})

export default router

再来修改一下项目根组件 src/App.vue 的内容,把无用的删了,只留下面内容即可

<script setup>
import { RouterView } from 'vue-router'
</script>

<template>
  <RouterView />
</template>

<style scoped></style>

最后,项目入口文件里,有一行 css 样式的引入,引入的样式文件我们已经删了,所以,把这行代码删除掉

import { createApp } from 'vue'
import { createPinia } from 'pinia'

import App from './App.vue'
import router from './router'

// 删除掉,css文件已经删除过了
// import './assets/main.css'

const app = createApp(App)

app.use(createPinia())
app.use(router)

app.mount('#app')

ok,处理好之后我们就会得到一个很干净的项目了,如果你的步骤和我一致,目前的文件目录应该是下面这样的,我们简单介绍下它们的作用。

toolsdog
├─.vscode                # vscode配置文件
|    └─extensions.json   # 项目推荐插件列表(可把项目中用到的vscode插件ID写进去,跑项目时没有安装这些插件会推荐安装)
├─public/                # 公共资源目录
├─src/                   # 核心开发目录
|  ├─App.vue             # 项目根组件
|  ├─main.js             # 项目入口文件
|  ├─views/              # 项目视图目录
|  | └─Home/index.vue
|  ├─stores/             # 统一状态管理目录-pinia
|  ├─router/             # 项目路由目录
|  | └─index.js
|  ├─components/         # 项目公共组件目录
|  ├─assets/             # 项目静态资源目录
├─.eslintrc.cjs          # eslint 配置文件
├─.gitignore             # git忽略文件
├─.prettierrc.json       # prettier 配置文件
├─README.md              # 项目说明文件
├─index.html             # html入口文件
├─package.json           # 项目配置和包管理文件
├─vite.config.js         # vite 配置文件

再次启动项目,如果没有问题的话,打开浏览器你的页面目前就是下面这样子

接下来我们逐步的加一些目录、依赖和配置让我们的项目更健壮、更好用!

安装组件库

一般我们开发为了省事儿都会用一个开源组件库,我们这里当然少不了,至于用什么,这里我们用字节的 arco.design ,那至于为什么用这个,因为 ElementPlus 用腻了,尝试下新鲜的,我也是头一次用,组件库嘛,用什么都无所谓。

安装 ArcoVue

npm install --save-dev @arco-design/web-vue

# or 

pnpm add -D @arco-design/web-vue

配置按需加载

因为懒,所以我们组件使用 unplugin-vue-components 和 unplugin-auto-import 这两款 vite 插件来开启按需加载及自动导入,插件会自动解析模板中的使用到的组件,并导入组件和对应的样式文件。

其实说白了,这两个插件一个是自动帮我们引入一些组件和指令(只做 HTML 中使用的常规组件例如各种 .vue 组件的引入以及指令的自动引入),另一个是自动帮我们做一些 API 组件的自动引入(像直接在 script 中引入的必须用 API 调用的 Message 组件以及后面我们还会用它做 Vue 的一些 API 自动引入等等)

先安装

npm i unplugin-vue-components -D
npm i -D unplugin-auto-import

# or

pnpm add -D unplugin-vue-components
pnpm add -D unplugin-auto-import

然后我们在 vite.config.js 文件中配置使用一下插件

import { fileURLToPath, URL } from 'node:url'

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

import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ArcoResolver } from 'unplugin-vue-components/resolvers'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    AutoImport({
      resolvers: [ArcoResolver()]
    }),
    Components({
      resolvers: [
        ArcoResolver({
          sideEffect: true
        })
      ]
    })
  ]

  // ...
})

可以看到上面我们在 unplugin-vue-components/resolvers 中导出了一个 ArcoResolver ,它是什么呢?

其实,它是插件内置的解析器,像常用的组件库(element、antd 等)自动引入的一些配置都被内置了 👉🏻 查看内置支持的组件库解析器,我们只需要导出对应 UI库 的解析器用就行了,如果你使用的组件库没有被内置,你完全可以自己写一个,如果是通用组件库的话,你甚至可以直接 PR 到插件 GitHub,混一个开源项目的 Contributors 玩,并不复杂,因为后面写项目的时候应该会简单写到几个解析函数,不多解释了。

OK,现在组件库和自动引入都做好了,先试一试,我们在 home 页面分别用 ArcoVue 的普通按钮 AButton 组件和全局提示 AMessage 组件试一试。

<script setup>
const handleClickMini = () => {
  AMessage.info('hello isboyjc, click mini AButton!')
};
</script>
<template>
  <div>hello isboyjc, This is toolsdog home page!</div>
  <a-space>
    <a-button type="primary" size="mini" @click="handleClickMini">Mini</a-button>
    <a-button type="primary" size="small">Small</a-button>
    <a-button type="primary">Medium</a-button>
    <a-button type="primary" size="large">Large</a-button>
  </a-space>
</template>
<style scoped lang="scss"></style>

可以看到,我们不需要自己去引入就可以随时随地的使用组件库中的组件了!

配置项目内组件 & API 的自动引入

我们在使用 Vue 的过程中,每个 script 以及 js 文件中或多或少需要引入一些像 ref、reactiveVueAPI,包括 VueRouter、Pinia 等都要引入一些 API,还有我们自己写的组件也都需要我们手动去引入使用。

那既然配置了组件库自动引入,我们接下来也配置API、以及页面组件的自动引入。

还是在 vite.config.js 文件中,依旧还是上面那 2 个插件,我们来写一下配置。

import { fileURLToPath, URL } from 'node:url'

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

import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ArcoResolver } from 'unplugin-vue-components/resolvers'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    AutoImport({
      // 需要去解析的文件
      include: [
        /\.[tj]sx?$/, // .ts, .tsx, .js, .jsx
        /\.vue$/,
        /\.vue\?vue/, // .vue
        /\.md$/ // .md
      ],
      // imports 指定自动引入的包位置(名)
      imports: ['vue', 'pinia', 'vue-router'],
      // 生成相应的自动导入json文件。
      eslintrc: {
        // 启用
        enabled: true,
        // 生成自动导入json文件位置
        filepath: './.eslintrc-auto-import.json',
        // 全局属性值
        globalsPropValue: true
      },
      resolvers: [ArcoResolver()]
    }),
    Components({
      // imports 指定组件所在目录,默认为 src/components
      dirs: ['src/components/', 'src/view/'],
      // 需要去解析的文件
      include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
      resolvers: [
        ArcoResolver({
          sideEffect: true
        })
      ]
    })
  ]

  // ...
})

如上,在 API 自动引入插件 AutoImport 中我们写了指定要去解析的文件 include 配置,然后在 import 选项中指定了自动引入的包名,并且所有自动引入的 API 在被自动引入时会添加记录到根目录的 ./.eslintrc-auto-import.json 文件中,方便我们查看都自动引入了哪些东西,后面我们使用这几个包的 API ,就不需要手动引入了,插件会帮我们在文件解析时自动引入。

同样的,在组件自动引入插件 Components 中,我们配置了指定要去解析的文件 include 配置,然后在 import 选项中指定了自动引入的组件目录,以后只要是在这几个目录下写的组件,我们在使用时都必须要手动去引入了

ok,我们来试一下。

我们在 src/components 文件夹下新建一个 HelloWorld.vue 文件,写上下面内容。

<script setup>
const name = ref('isboyjc')
</script>
<template>
  <div>hello {{ name }}, this is helloworld components</div>
</template>

<style scoped></style>

然后,直接在 src/views/HomePage.vue 文件中使用 HelloWorld 组件,不要引入,如下:

<script setup>
const handleClickMini = () => {
  AMessage.info('hello isboyjc, click mini AButton!')
}
</script>
<template>
  <div>hello isboyjc, This is toolsdog home page!</div>
  <a-space>
    <a-button type="primary" size="mini" @click="handleClickMini">
      Mini
    </a-button>
    <a-button type="primary" size="small">Small</a-button>
    <a-button type="primary">Medium</a-button>
    <a-button type="primary" size="large">Large</a-button>
  </a-space>
	
  <!-- 这里 -->
  <HelloWorld />
</template>
<style scoped lang="scss"></style>

上面我们在创建的 HelloWorld 组件中使用了 Vueref API,并没有引入它,而后在 HomePage 页面中使用该组件也没有引入,我们来跑一下项目。

nice!后面我们使用 VueVueRouterPiniaArcoVue 包括自建组件等等都不需要手动引入了,当然,后续你的项目中有用到其他地方你依然可以在插件中去配置!

安装 VueUse

VueUse 大家没用过的话可以先把它理解为一个基于 Vue 的工具库,Vue2Vue3 都可以用,有很多实用的方法、组件包括指令,超级方便,后续我们会用到其中的一些方法,所以先装上

安装

npm i @vueuse/core

// or

pnpm add @vueuse/core

配置自动引入

VueUse 不止有方法,还有组件和指令,所以我们还是需要上面两个自动引入的插件去处理,那由于作者是一个人,解析器都内置在自动引入插件中了,所以我们直接导出用就可以了。

我们配置 VueUse 的组件和指令自动引入需要两个解析器,还是在 vite.config.js 配置文件中引入,如下:

// ArcoVue、VueUse 组件和指令的自动引入解析器
import {
  ArcoResolver,
  VueUseComponentsResolver,
  VueUseDirectiveResolver
} from 'unplugin-vue-components/resolvers'

使用的话,只需要在配置文件 plugins 模块中之前写过的 Components 插件中使用一下这两个解析器就好了:

plugins: [
  Components({
    // ...
    VueUseComponentsResolver(),
    VueUseDirectiveResolver()
  })
]

API 方法的自动引入就很简单了,还是配置文件中只需要在之前用过的 AutoImport 插件中添加一个 VueUse 包名配置就行了:

plugins: [
  AutoImport({
    // 新增 '@vueuse/core'
    imports: ['vue', 'pinia', 'vue-router', '@vueuse/core'],
    resolvers: [ArcoResolver()]
  }),
]

这样我们就可以在项目中随时随地的使用 VueUse 了!建议大家有时间可以去看看 VueUse 的源码实现,也并不复杂,它有很多最佳实践,可以给我们使用 Vue3 提供很大的帮助!

配置 ESLint 和 Prettier

那上面我们配置了自动引入,但是大家会发现,由于之前我们给项目安装了 ESLintPrettier ,虽然还没有进行配置,但是默认配置会给那些自动引入的 API 报红,就比如下面这样

还有这个

作为一个强迫症患者,这是不能存在的,所以我们配置下 ESLintPrettier ,配置之前我们先看看初始化的配置是什么样子

根目录下的 .eslintrc.cjsESLint 配置,当前默认如下

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

module.exports = {
  root: true,
  'extends': [
    'plugin:vue/vue3-essential',
    'eslint:recommended',
    '@vue/eslint-config-prettier'
  ],
  parserOptions: {
    ecmaVersion: 'latest'
  }
}

根目录下的 .prettierrc.jsonPrettier 配置,当前默认如下

{}

那么如何让我们自动引入的那些 API 不报红呢?

那报红这种检测肯定是 ESLint 干的, 还记得我们自动引入配置的那个导出文件吗?我们所有自动引入的 API 都生成了记录在这个文件,你只需要将它写入 ESLint 配置的 extends 中让 Lint 工具识别下就好了,如下

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

module.exports = {
  root: true,
  'extends': [
    // 这里
    './.eslintrc-auto-import.json',
    'plugin:vue/vue3-essential',
    'eslint:recommended',
    '@vue/eslint-config-prettier'
  ],
  parserOptions: {
    ecmaVersion: 'latest'
  }
}

注意,extends 这个继承配置的是一个数组,最终会将所有规则项进行合并,出现冲突的时候,后面的会覆盖前面的,我们在初始化项目安装时默认给加上去了 3 个,我们看看这三个是什么?

  • plugin:vue/vue3-essential
    • ESLint Vue3 插件扩展
  • eslint:recommended
    • ESLint 官方扩展
  • @vue/eslint-config-prettier
    • Prettier NPM 扩展

我们把 Prettier 扩展放到最后面,原因是 Prettier 会格式化代码,是为了保证最终代码格式统一。

ok,保存在看看,是不是不报红了。

那么我们还需要配置什么?

可能很多同学没有用过 Vue3 ,这里直接给大家说,由于我们接下来要使用 Vue3CompositionAPI,那 Vue3 有几个可以直接在 <script setup> 中可用的全局 API,比如 definePropsdefineEmitsdefineExpose,如果你使用 TS,还会用到 withDefaults

那我们的 ESLint 默认是识别不了这些全局 API 的,此时需要向 ESlint 规则中添加需要辨认的全局变量。

ESLint 配置中的 globals 属性就是让项目在 lint 执行期间访问额外的全局变量,简单说就是开发者自定义的全局变量,我们依次加上这些属性就可以了。

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

module.exports = {
  root: true,
  'extends': [
    './.eslintrc-auto-import.json',
    'plugin:vue/vue3-essential',
    'eslint:recommended',
    '@vue/eslint-config-prettier'
  ],
  // 这里
  globals: {
    defineEmits: "readonly",
    defineProps: "readonly",
    defineExpose: "readonly",
    withDefaults: "readonly",
  },
  parserOptions: {
    ecmaVersion: 'latest'
  }
}

如上,添加全局属性时,readonly 代表只读,writable 代表可写,可写就是可以手动覆盖这个全局变量的意思,我们当然是不允许覆盖了,所以全部都设置成了 readonly

我们可以看到在 .eslintrc.cjs 文件中第一行有个 /* eslint-env node */ 注释,它是用来指定文件为 node 环境的。

作为一个比较严格的强迫症,这种莫名其妙的注释是不允许出现的,OK,删掉它,所以就报红了:

这就是因为我们没有去指定这个文件用 node 规则解析,其实我们不需要去在开头写这么一行注释指定文件环境,我们给 ESLint 指定一下常用环境,即 env 属性配置,让 ESLint 自己去匹配,我们不写这个配置的话默认它只支持浏览器 browser 的规则解析,写上环境配置,我们最终的配置文件如下:

require("@rushstack/eslint-patch/modern-module-resolution");

module.exports = {
  // 这里
  env: {
    // 浏览器环境
    browser: true,
    // Node环境
    node: true,
    // 启用除了modules以外的所有 ECMAScript 6 特性
    es2021: true,
  },
  root: true,
  extends: [
    "./.eslintrc-auto-import.json",
    "plugin:vue/vue3-essential",
    "eslint:recommended",
    "@vue/eslint-config-prettier",
  ],
  globals: {
    defineEmits: "readonly",
    defineProps: "readonly",
    defineExpose: "readonly",
    withDefaults: "readonly",
  },
  parserOptions: {
    ecmaVersion: "latest",
  },
  rules: {
    semi: ["warn", "never"], // 禁止尾部使用分号
    "no-debugger": "warn", // 禁止出现 debugger
  },
};

如上所示,我们在环境这块配置了三个

  • browser ── 浏览器环境
  • node ── Node 环境
  • es6 ── 启用除了 modules 以外的所有 ECMAScript 6 特性

都用的到,直接都开启就好。

可能我们也都发现了,我们新增了一个 rules 属性,如单词字面意思,就是规则的配置,可以配置启用一些规则及其各自的错误级别,那由于每个人的喜好不同,所以我没有过多配置,只配置了 2 个。

rules 的规则配置有三种:

  • off 或 0 关闭对该规则的校验
  • warn 或 1 启用规则,不满足时抛出警告,不会退出编译进程
  • error 或 2 启用规则,不满足时抛出错误,会退出编译进程

注意,如果某项规则,有额外的选项,可以通过数组进行传递,数组的第一位必须是错误级别,就比如我们配置的 semi 规则中的 never 就是额外配置项。

那我们就配置到这里,后续大家按照自己的喜好在 rules 中配置一些规则就可以了。

PS: 亲测安装了 VSCodeBabel JavaScript 插件后, JS 代码末尾如果不加分号,会导致后续代码高亮、注释不正常,在 VSCode 中禁用这个插件就好了,高亮错误展示如下:

配置完了 ESLint ,我们再来看Prettier,我这边配置了几个常用的,如下

{
  "semi": false,
  "singleQuote": true,
  "printWidth": 80,
  "trailingComma": "none",
  "arrowParens": "avoid",
  "tabWidth": 2
}
  • semi 代码结尾是否加分号
  • singleQuote 是否使用单引号
  • printWidth 超过多少字符强制换行
  • trailingComma 代码末尾不需要逗号
  • arrowParens 单个参数的箭头函数不加括号 x => x
  • tabWidth 使用 n 个空格缩进

Prettier 配置就比较简单,按照文档和喜好在 .prettierrc.json 文件中配置就可以,注意配置的时候一定要和 ESLintrules 比较一下,这里是会发生冲突的地方,检测和格式化规则一定要一致。

配置 SVGIcon

项目中我们多多少少会用到一些图标,而目前 SVG 图标是主流,所以我们配置下 SVG 图标。

我们这里还用到 antFu 大佬写的 unplugin-icons 插件,它基于 iconify 图标库支持按需访问上万种图标,当然,我们不使用图标库也是可以的,也可以配置自定义一些 SVG 图标。

同时,因为此插件和上面自动引入的两个插件都是 antFu 大佬写的,所以,我们也可以直接为引入的 Icon 配置自动引入对应图标组件,很方便。

安装插件

npm i -D unplugin-icons

# or

pnpm add unplugin-icons -D

配置插件

配置之前,我们先在 src/assets 文件夹下创建一个 svg 文件夹,然后在其下面新建两个文件夹,一个叫 home 一个叫 user,在下面随便放 2 个 svg 文件,我们下面会给大家简单演示怎么配置和使用自定义的 SVG 图标集合。

这块详细配置相关的就不给大家讲了,因为文章 Vue3!ElementPlus!更加优雅的使用Icon 中已经详细说过了,大家可以先去看看这便文章再来看这块,我这边再写的话比较浪费时间。

整个的配置如下所示:

import { fileURLToPath, URL } from 'node:url'

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

// API自动引入插件
import AutoImport from "unplugin-auto-import/vite"
// 组件自动引入插件
import Components from "unplugin-vue-components/vite"
import {
  ArcoResolver,
  VueUseComponentsResolver,
  VueUseDirectiveResolver
} from 'unplugin-vue-components/resolvers'
// icon 插件
import Icons from "unplugin-icons/vite"
// icon 自动引入解析器
import IconsResolver from "unplugin-icons/resolver"
// icon 加载 loader
import { FileSystemIconLoader } from "unplugin-icons/loaders"

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    AutoImport({
      include: [
        /\.[tj]sx?$/,
        /\.vue$/,
        /\.vue\?vue/,
        /\.md$/,
      ],
      imports: ["vue", "pinia", "vue-router"],
      eslintrc: {
        enabled: true,
        filepath: "./.eslintrc-auto-import.json",
        globalsPropValue: true,
      },
      resolvers: [ArcoResolver()],
    }),
    Components({
      dirs: ["src/components/", "src/view/""@vueuse/core"],
      include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
      resolvers: [
        ArcoResolver({
          sideEffect: true,
        }),
        VueUseComponentsResolver(),
        VueUseDirectiveResolver(),
	// icon组件自动引入解析器使用
        IconsResolver({
          // icon自动引入的组件前缀 - 为了统一组件icon组件名称格式
          prefix: "icon",
          // 自定义的icon模块集合
          customCollections: ["user", "home"],
        }),
      ],
    }),
    // Icon 插件配置
    Icons({
      compiler: "vue3",
      customCollections: {
        // user图标集,给svg文件设置 fill="currentColor" 属性,使图标的颜色具有适应性
        user: FileSystemIconLoader("src/assets/svg/user", (svg) =>
          svg.replace(/^<svg /, '<svg fill="currentColor" ')
        ),
        // home 模块图标集
        home: FileSystemIconLoader("src/assets/svg/home", (svg) =>
          svg.replace(/^<svg /, '<svg fill="currentColor" ')
        ),
      },
      autoInstall: true,
    }),
  ]
})

使用图标库中 Icon

打开 iconify 的官方图标网站 icones

随便挑几个复制一下名字

然后我们在 HomePage 页面中直接去按规则写组件就行(我们上面配置了自动引入的 icon 组件前缀必须写 <icon- ),那我们使用起来如下:

<!-- ep:alarm-clock  改成  ep-alarm-clock -->
<icon-ep-alarm-clock class=""/>

如上,我们 copy 名字就可以直接使用,项目运行时写完保存,项目就可以自动为我们下载这个集合的图标并识别解析这个格式的 icon 组件

使用自定义 Icon

那如何使用我们自定义的图标集,比如上面我们配置中自定义的 home、user 集合,我们在 home 文件夹下放了一个 copy.svg 还有一个 download.svg 文件,使用如下

<!-- 相当于 home:xxx,xxx就是home文件夹下的图标文件名 -->
<icon-home-copy class=""/>
<icon-home-download class=""/>

同样,自定义的图标我们也写在 在 HomePage 页面中。

最后我们来看下目前 HomePage 页面代码

<script setup>
const handleClickMini = () => {
  AMessage.info("hello isboyjc, click mini AButton!")
}
</script>
<template>
  <div>hello isboyjc, This is toolsdog home page!</div>
  <a-space>
    <a-button type="primary" size="mini" @click="handleClickMini">
      Mini
    </a-button>
    <a-button type="primary" size="small">Small</a-button>
    <a-button type="primary">Medium</a-button>
    <a-button type="primary" size="large">Large</a-button>
  </a-space>

  <HelloWorld />

  <!-- Icon -->
  <div>
    <icon-ep-alarm-clock class="" />
    <icon-home-copy class="" />
    <icon-home-download class="" />
  </div>
</template>
<style scoped></style>

最终效果:

安装 VSCode 插件 Iconify IntelliSense

这是 iconify 图标库的 VSCode 插件,VSCode 中搜索插件:

Iconify IntelliSense

// or 搜索插件ID

antfu.iconify

安装之后,我们在使用图标库内图标时,直接可以在 VSCode 中预览到图标,超级方便,如下

图标样式的话你可以直接在 icon 组件中添加,OK,图标也好了!

Styles 公共样式管理、初始化样式

接下来我们简单做一下 CSS 公共样式的处理,我们在项目 src 目录下新增一个 styles 文件夹,此文件夹我们后期可以放一些公共的样式文件。

大家都知道,HTML 标签是有默认样式的,一般我们在写项目时都会直接清除掉这个默认样式,也就是做个重置。

那相较于 Eric Merer 原版的清楚样式文件,Normalize.css 它在默认的 HTML 元素样式上提供了跨浏览器的高度一致性,是一种现代的、为 HTML5 准备的优质替代方案,所以我们直接使用它就好了。

下载 Normalize.cssStyles 文件夹下,当然你也可以直接 npm 安装它,不过我比较喜欢直接下载下来这个文件。

下载下来之后直接在 main.js 最上面引入一下就行了,如下

import { createApp } from "vue"
import { createPinia } from "pinia"
// 这里
import "@/styles/normalize.css"

import App from "./App.vue"
import router from "./router"

const app = createApp(App)

app.use(createPinia())
app.use(router)

app.mount("#app")

其他的公共 css 文件我们用到的时候也可以这样引入一下就可以了。

配置 UnoCSS

CSS 这块,我们的原则是能简单就简单,所以我们基于 ACSS 即原子化的 CSS 框架来做。

不了解 原子化 的同学建议先看下这篇文章 「前端工程四部曲」模块化的前世今生(下)

 Tailwind CSS 大家应该都知道, WindiCSS 呢,算是他的一个超集,本来我想用 WindiCSS 的,但是 WindiCSS 作者们都不咋维护了,然后 UnoCSS 又这么便捷,配置文件都不需要写,直接引入 Vite 插件和对应的预设就可以了,So,上 UnoCSS。。。

UnoCSS,官方说它是一个按需原子 CSS 引擎,它默认提供了流行实用程序优先框架 Tailwind CSSWindi CSSBootstrapTachyon 等的通用超集,如果你习惯这些框架,依旧可以按照熟悉的方式写,无缝衔接。

不用不知道,用了是真香啊,当然,肯定是有人不喜欢原子化 CSS 的,不喜欢不用,毕竟就是写 CSS 的方式不一样而已,我这边也不会做很多配置,你甚至可以用原生 CSS

安装

npm install --save-dev unocss @unocss/preset-uno @unocss/preset-attributify @unocss/transformer-directives

# or

pnpm i -D unocss @unocss/preset-uno @unocss/preset-attributify @unocss/transformer-directives

如上,我们装了 4 个包

配置

还是在 Vite 插件配置中,也就是 vite.config.js 文件中配置

import { fileURLToPath, URL } from 'node:url'

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

// ...

// Unocss 插件
import Unocss from 'unocss/vite'
// Unocss 默认预设
import presetUno from '@unocss/preset-uno'
// Unocss 属性模式预设
import presetAttributify from '@unocss/preset-attributify'
// Unocss 指令转换插件
import transformerDirective from '@unocss/transformer-directives'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    // ...

    // 新增一个 Unocss 插件配置
    Unocss({
      // 预设
      presets: [presetUno(), presetAttributify()],
      // 指令转换插件
      transformers: [transformerDirective()],
      // 自定义规则
      rules: []
    }),
  ]

  // ...
})

OK,就这么简单。

使用

在使用之前我们先在入口文件 main.js 中一下 UnoCSScss 文件:

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import '@/styles/normalize.css' 

// 导入Unocss样式 
import 'uno.css' 

// ...

基础使用如下:

<button class="bg-blue-400 hover:bg-blue-500 text-sm text-white font-mono font-light py-2 px-4 rounded border-2 border-blue-200 dark:bg-blue-500 dark:hover:bg-blue-600">
  Button
</button>

如果你对这些原子化样式不太熟悉的话,直接打开样式查询地址 uno.antfu.me/

输入对应 CSS 样式去搜就可以了,如下:

当然,我是不建议你这么做的,因为个人觉得不太好用,感觉好像不太完善,既然我们用的预设支持 Tailwind / WindiCSS ,所以,我建议大家直接去看看这两个文档,了解一个大概,按照这两个东西的写法来就可以,有啥不会的去这两个的文档里搜

那推荐最好是直接去看 WindiCSS 使用文档,因为作者是一个人嘛,如下,直接搜对应 CSS 看怎样使用就可以了

然后在项目中直接使用就行了,真丫机智!!!

我们上面安装了 @unocss/preset-attributify 属性预设,所以我们也可以使用属性模式,可以将实用程序分为多个属性,这样写:

<button 
  bg="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600"
  text="sm white"
  font="mono light"
  p="y-2 x-4"
  border="2 rounded blue-200"
>
  Button
</button>

肯定有人觉得原子化 CSS 全写在 HTML 中,太多的话,看上去眼花缭乱很不爽,那所以我们还安装了指令转换器插件 @unocss/transformer-directives 就是为了解决这个问题了。

它允许我们使用 @apply指令在 style 中写原子化 CSS

<button class="btn-style">
  Button
</button>

<style>
.btn-style{
  @apply bg-blue-400 text-sm text-white font-mono font-light py-2 px-4 rounded border-2 border-blue-200;
  @apply hover:bg-blue-500;
  @apply dark:bg-blue-500 dark:hover:bg-blue-600;
}
</style>

安装 VSCode 插件 UnoCSS

我们安装一下 UnoCSS 官方的 VSCode 插件,VSCode 扩展中搜索:

UnoCSS

// or 搜索ID 

antfu.unocss

安装之后,就可以在编辑器中看到我们写的原子化对应的 CSS 样式了,如下:

当然 UnoCSS 还有很多功能,这里不过多描述了,有兴趣文档看一看,后面写项目的过程中也会逐步的去介绍。

如果我们是刚开始使用,肯定会不习惯,但是,只要坚持几天,直接真香警告!毕竟,一切都是为了简单,一切都是为了省事儿,一切都为了少写代码!!!

Utils、Hooks、API 管理

我们在项目 src 目录下添加一个 utils 文件夹,此文件夹用于存放我们项目中用到的一些公共方法文件。

同样的,我们在项目 src 目录下添加一个 hooks 文件夹,此文件夹用于存放我们项目中用到的一些 hooks 文件,因为我们用 Vue3CompsitionAPI,后面用多了自然会有很多 hooks 文件,针对一些公用的,我们统一管理在此文件夹下。

平常我们做项目,一般和请求相关的文件都统一放在一个文件夹下,所以我们在项目 src 目录下添加一个 api 文件夹,用于存放和请求相关的文件,因为项目性质,所以我们应该暂时用不到请求后端接口,那这边 api 文件的一些配置以及 axios 的封装甚至 API Mock 配置这里都先不展开说了,回头我会写好这块代码提交到 GitHub ,后面有需要的话会写一篇 axios 封装相关的文章来单独介绍这块。

其他 Vite 配置

先来说环境的配置,先放个官方文档压压惊

OK,我们在 env 目录下新建下面 3 个文件

  • .env 所有模式下都会加载
  • .env.development 只在开发模式下加载
  • .env.production 只在生产模式下加载

.env 文件在所有模式下都会加载,所以这里我们可以写一些所有环境都通用的环境变量,如下:

# 所有环境都会加载

# 项目标识代码
VITE_APP_CODE="TOOLSDOG"

# 项目名
VITE_APP_NAME="工具狗"

# 项目描述
VITE_APP_DESCRIPTION="你用的到的工具,这里都有!"

注意,我们在 Vite 中配置的环境变量默认只有以 VITE_ 开头的配置,才会暴露给客户端,我们才能在项目中获取到。

开发模式 .env.development 配置

# 开发环境加载

# 环境标识
VITE_APP_ENV="development"

# 公共基础路径
VITE_BASE="/"

# 代理URL路径
VITE_BASE_URL ="/api"

# 模拟数据接口路径
VITE_BASE_MOCK_URL ="/mock-api"

# 服务端接口路径
VITE_BASE_SERVER_URL = "..."

# 打包是否使用Mock
VITE_APP_PRODMOCK=false

那生产环境除了环境标识 VITE_APP_ENV 和开发模式标识不同,其他配置项应尽量保持一致,只是配置项的内容不同而已,不一一的展示了。

接下来修改下 package.json 脚本命令如下

{
    "scripts": {
    "serve": "vite --mode development",
    "build": "vite build --mode production",
    "preview": "vite preview --port 8081",
    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
  }
}

上面脚本中我们给启动命令搞成了

npm run serve

// or

pnpm serve

serve 脚本命令配置中,我们还传了一个 mode,其实这个 mode 就是对应我们的环境文件 .env.[mode]

开发环境默认 mode 就是 development,生产环境默认 mode 就是 development,所以脚本命令这里我不传 mode 也可以,但是如果大家把开发环境文件由 .env.development 改成 .env.dev,那脚本中 mode 就得传 —-mode devbuild 时也是一样的道理,如果有其他环境,那脚本命令传入对应的 mode 就可以了。

如果想要在 vite.config.js 文件中获取对应运行 mode 环境变量的配置,我们可以使用 viteloadEnv API

VitedefineConfig 方法也可以接收一个返回配置对象的回调函数,回调函数的参数里我们可以拿到运行脚本命令时传入的 mode 值,从而使用 loadEnv 方法去在 Vite 配置文件中获取对应 mode 下的环境变量,如下:

// export default defineConfig({}) 修改

export default defineConfig(({mode}) => {
	return {}
})

那其他一些基础配置就不一一说明了,大家可以直接看 Vite 官方文档

目前我们的配置如下:

/*
 * @LastEditors: isboyjc
 * @Description: Vite 项目配置
 * @Date: 2022-09-17 14:35:02
 * @LastEditTime: 2022-09-23 01:56:21
 * @Author: isboyjc
 */
import { fileURLToPath, URL } from 'node:url'

import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'

// API自动引入插件
import AutoImport from 'unplugin-auto-import/vite'
// 组件自动引入插件
import Components from 'unplugin-vue-components/vite'
// ArcoVue、VueUse 组件和指令自动引入解析器
import {
  ArcoResolver,
  VueUseComponentsResolver,
  VueUseDirectiveResolver
} from 'unplugin-vue-components/resolvers'
// icon 插件
import Icons from 'unplugin-icons/vite'
// icon 自动引入解析器
import IconsResolver from 'unplugin-icons/resolver'
// icon 加载 loader
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
// Unocss 插件
import Unocss from 'unocss/vite'
// Unocss 默认预设
import presetUno from '@unocss/preset-uno'
// Unocss 属性模式预设
import presetAttributify from '@unocss/preset-attributify'
// Unocss 指令插件
import transformerDirective from '@unocss/transformer-directives'

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
  const viteEnv = loadEnv(mode, './')

  return {
    base: viteEnv.VITE_BASE,
    server: {
      host: '0.0.0.0',
      port: '8080',
      open: true,
      // 端口占用直接退出
      strictPort: true
      // 本地服务 CORS 是否开启
      // cors: true,
      // proxy: {
      //   [viteEnv.VITE_BASE_URL]: {
      //     target: viteEnv.VITE_BASE_SERVER_URL,
      //     // 允许跨域
      //     changeOrigin: true,
      //     rewrite: path => path.replace(viteEnv.VITE_BASE_URL, '/')
      //   }
      // }
    },
    build: {
      outDir: 'dist',
      assetsDir: 'static/assets',
      // sourcemap: true,
      // 规定触发警告的 chunk 大小,消除打包大小超过500kb警告
      chunkSizeWarningLimit: 2000,
      // 静态资源打包到dist下的不同目录
      rollupOptions: {
        output: {
          chunkFileNames: 'static/js/[name]-[hash].js',
          entryFileNames: 'static/js/[name]-[hash].js',
          assetFileNames: 'static/[ext]/[name]-[hash].[ext]'
        }
      }
    },
    resolve: {
      alias: {
        '@': fileURLToPath(new URL('./src', import.meta.url))
      }
    },
    plugins: [
      vue(),
      // 使用Unocss
      Unocss({
        // 预设
        presets: [presetUno(), presetAttributify()],
        // 指令转换插件
        transformers: [transformerDirective()],
        // 自定义规则
        rules: []
      }),
      AutoImport({
        include: [
          /\.[tj]sx?$/, // .ts, .tsx, .js, .jsx
          /\.vue$/,
          /\.vue\?vue/, // .vue
          /\.md$/ // .md
        ],
        imports: ['vue', 'pinia', 'vue-router', '@vueuse/core'],
        // 生成相应的自动导入json文件。
        // eslint globals Docs - https://eslint.org/docs/user-guide/configuring/language-options#specifying-globals
        eslintrc: {
          enabled: true,
          filepath: './.eslintrc-auto-import.json', // Default `./.eslintrc-auto-import.json`
          globalsPropValue: true // Default `true`, (true | false | 'readonly' | 'readable' | 'writable' | 'writeable')
        },
        resolvers: [ArcoResolver()]
      }),
      Components({
        // imports 指定组件所在位置,默认为 src/components
        dirs: ['src/components/', 'src/view/'],
        include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
        resolvers: [
          ArcoResolver({
            sideEffect: true
          }),
          VueUseComponentsResolver(),
	  VueUseDirectiveResolver(),
          IconsResolver({
            // icon自动引入的组件前缀 - 为了统一组件icon组件名称格式
            prefix: 'icon',
            // 自定义的icon模块集合
            customCollections: ['user', 'home']
          })
        ]
      }),
      Icons({
        compiler: 'vue3',
        customCollections: {
          // user图标集,给svg文件设置fill="currentColor"属性,使图标的颜色具有适应性
          user: FileSystemIconLoader('src/assets/svg/user', svg =>
            svg.replace(/^<svg /, '<svg fill="currentColor" ')
          ),
          // home 模块图标集
          home: FileSystemIconLoader('src/assets/svg/home', svg =>
            svg.replace(/^<svg /, '<svg fill="currentColor" ')
          )
        },
        autoInstall: true
      })
    ]
  }
})

那项目配置文件暂时就到这里了,后期写项目还会用到一些东西,我们到时候再配置。

添加 Config 配置

上面说了,环境变量默认以 VITE_ 开头的配置,才会暴露给客户端,我们也写了几个 VITE_ 开头的配置,所以在项目运行时,我们可以直接 import.meta.env.VITE_XXX 去查看配置,但是这样太麻烦了,所以我们写一个统一的配置文件去获取环境变量,包括项目后期的一些全局配置也可以写里面

项目 src 目录下新建 config/config.js 文件,写入下面文件:

/*
 * @LastEditors: isboyjc
 * @Description: 全局config配置文件
 * @Date: 2022-09-17 14:35:02
 * @LastEditTime: 2022-09-17 14:35:02
 * @Author: isboyjc
 */

// 获取环境变量
const ENV = import.meta.env
// 配置文件
let config = {}
// 默认配置文件
const configSource = {
  appCode: ENV.VITE_APP_CODE,
  // 项目标识代码
  projectCode: `${ENV.VITE_APP_CODE}_${ENV.VITE_APP_ENV}`,
  // 项目名
  projectName: ENV.VITE_APP_NAME,
  // 项目描述
  projectDesc: ENV.VITE_APP_DESCRIPTION,
  // 资源base地址
  base: ENV.VITE_BASE,
  // 接口代理URL路径
  baseUrl: ENV.VITE_BASE_URL,
  // 模拟数据接口路径
  mockBaseUrl: ENV.VITE_BASE_MOCK_URL,
  // 服务端接口路径
  serverUrl: ENV.VITE_BASE_SERVER_URL
}

/**
 * @Author isboyjc
 * @Date 2022-09-17 14:35:02
 * @description 设置全局配置
 * @param {Object} cfg 配置项
 * @return {Object} 新的全局配置 config
 */
const setConfig = cfg => {
  config = Object.assign(config, cfg)
  return config
}

/**
 * @Author isboyjc
 * @Date 2022-09-17 14:35:02
 * @description 重置全局配置
 * @param {*}
 * @return {Object} 全局默认配置 configSource
 */
const resetConfig = () => {
  config = { ...configSource }
  return config
}
resetConfig()

/**
 * @Author isboyjc
 * @Date 2022-09-17 14:35:02
 * @description 获取全局配置
 * @param {String} key 配置项,支持 'a.b.c' 的方式获取
 * @return {Object} 新的全局配置 config
 */
const getConfig = key => {
  if (typeof key === 'string') {
    const arr = key.split('.')
    if (arr && arr.length) {
      let data = config
      arr.forEach(v => {
        if (data && typeof data[v] !== 'undefined') {
          data = data[v]
        } else {
          data = null
        }
      })
      return data
    }
  }
  if (Array.isArray(key)) {
    const data = config
    if (key && key.length > 1) {
      let res = {}
      key.forEach(v => {
        if (data && typeof data[v] !== 'undefined') {
          res[v] = data[v]
        } else {
          res[v] = null
        }
      })
      return res
    }
    return data[key]
  }
  return { ...config }
}

export { getConfig, setConfig, resetConfig }

上面代码不复杂,所以不过多解释了,介绍下为什么用这种方式,而不是直接写一个对象导出,其实是因为有次写项目有用到了动态修改全局配置的需求,所以就把全局配置项获取写成动态的了。

我们写入配置时,只需要在 configSource 对象中写入就可以了,项目中使用起来的话如下:

import { getConfig, setConfig, resetConfig } from "@/config/config.js"

// 获取配置
getConfig("a")
getConfig("a.b")
getConfig("a.b.c")

// 动态设置
setConfig({ ... })

// 重置配置
resetConfig()

配置 VSCode 推荐扩展插件

打开项目根目录的 .vscode/extensions.json 文件如下:

{
  "recommendations": [
    "vue.volar",
    "vue.vscode-typescript-vue-plugin"
  ]
}

这是创建项目时默认存在的扩展插件推荐配置,此文件的作用上面也介绍过了,就是个扩展推荐,数组里是 VSCode 的扩展插件 ID,你在根目录打开此项目时,如果编辑器没有安装这两个插件,VSCode 就会在右下角自动提示你去安装插件。

那我们没有使用 TS 直接干掉 vue.vscode-typescript-vue-plugin 这一项。

我们想要添加一个插件到推荐里,可以复制插件 ID 写到此配置中,也可以直接点击添加到工作区建议,如下:

回顾一下上文,我们一共装了 2 个插件

Iconify IntelliSense
UnoCSS

那我这里把这两个插件的 ID (注意是 ID 不是插件名字)都写进推荐列表里了:

{
  "recommendations": [
    "vue.volar",
    "antfu.iconify",
    "antfu.unocss"
  ]
}

大家如果克隆项目使用 VSCode 启动,就会提示你去安装这些插件了。

项目协同

创建 GIT 代码仓库

GitHub 创建仓库,仓库名为 toolsdog,开源许可选择 MIT

为项目绑定远程仓库

先在本地仓库初始化一下 git

git init

Copy 一下仓库地址,给本地项目关联远程仓库,入下:

git remote add origin git@github.com:isboyjc/toolsdog.git

然后首次提交一下代码,就 OK 了!

由于项目一直在开发中,所以我给当前分支提交打了个标记,大家无需拉取主分支代码,直接下载当前标记的代码就和文中代码一致了,地址如下 👇🏻

项目 GitHub 地址

最后

简单写个小项目,没有配置太多东西,像一般来说我们构建工作流都会用 GitHook 工具在提交时去检测代码,那目前碍于文章篇幅加上眼下就我自己开发,所以也没配置,后期都会补充,不要着急,此项目大家仅供参考,照搬不可取,汲取对自身有用的就可以了,有什么不足之处也请指出,如果你想学习下 Vue3,可以跟着一块来敲一敲,不喜勿喷!如果您觉得文章不错,记得 点赞 ,欢迎关注 Vue3 实战专栏

本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!