首先我们要创建一个 Vue3+Vite 项目,目前 Vue 官方创建项目时默认就是 Vite 构建了,所以直接按照官网来就可以,如下:
确保你安装了最新版本的 Node.js[1],然后在命令行中运行以下命令
npm init vue@latest# orpnpm create vue@latest
「PS:」 我这边使用的是 pnpm ,它的优缺点我就不说了,大家自行百度吧,不过后面所有的命令中我都会写 npm 和 pnpm 两种,大家想用啥都无所谓的,工具而已,但是 pnpm 真的很香。
这一指令将会安装并执行 create-vue[2],它是 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)? : YesScaffolding project in ./tooldog...Done.
OK,解释一下
没有使用 TS 只是单纯的不太喜欢 TS,虽然有很多优点,但是个人认为它是一种束缚,大家想用的话选个 Yes 也可以,我们这里选了 No。
JSX 如果要使用的话,不如直接使用 React,所以我们选了 No。
做项目 VueRouter 必选,不解释。
状态管理也是必选,目前官方都不推荐 Vuex 而是 Pinia 了,后续大家用起来也是逃不过真香定律的。
Unit(单元测试)和 E2E(端到端测试),做组件库之类的是需要搞的,但一般对做项目来说,会很少留给我们写 Unit 和 E2E 的时间,所以我们先不选了,后续有时间写再单独安装(没有这个可能 )。
最后的 ESLint 和 Prettier,一个保证质量,一个保证风格,必选,我们都选择 Yes 就好了。
这里都很基础,没有什么特殊要说的,不过我留意到很多新手前端甚至很多有些经验的前端都搞不懂 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#orcd toolsdogpnpm installpnpm 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#urlimport { fileURLToPath, URL } from 'node:url'
Vue 官方文档上明确说了开始项目之前,请确保安装了最新版本的 NodeJS,我们的 node 版本要在 v16+ ,所以出现这种问题你的电脑 node 版本可能有些老了,赶快 node -v 查看一下自己电脑的 node 版本吧!
「PS:」 对于经验不多的同学,可能认为换个版本太麻烦,所以,下面还是简单介绍下切换 node 版本包括切换 npm 镜像源,这是很基础的常识,新同学要敲黑板了。
切换不同版本的 node 对前端来说是很常见的事情(比如那个可恶的 node-sass,就不支持 node16+ 。。。),如果每次都删除重装,那也太麻烦了,我们可以直接使用 nvm 工具去管理多个版本的 node,想使用哪个版本,安装好直接切换使用即可。
「Windows 同学安装 nvm」
使用 win 的同学需注意,安装 nvm 之前需要先卸载掉 node,所以安装前可以先查看下当前本机 node 版本,下载好 nvm 后再通过 nvm 安装上这个版本 node,防止突然换了其他版本 node 后对当前电脑上的项目造成一些影响。
使用 win 的同学安装起来可以很简单,打开 nvm-windows[3],找到最新版本。
如上图
nvm-noinstall.zip # 绿色免安装版,使用时需进行配置nvm-setup.zip # 全自动安装版,推荐使用
新手同学建议直接下载一键自动安装版就行,想挑战一下自己,也可以下载绿色免安装版本,不过需要自己配置一些环境变量(希望不会有这种大聪明,有简单的方式就用简单的嘛)。
下载下来 nvm-setup.zip 解压后,直接双击一路 next 安装即可,需要注意安装时选择安装地址,只要你选的安装地址目录没有中文和空格,一般不会出现问题的。
安装好后,打开 cmd 输入 nvm 回车显示 nvm 帮助命令提示即安装成功!
「Mac 同学安装 nvm」
Mac 同学如果装了 brew,那直接使用它安装即可
brew install nvm
如果没有 brew,使用官方 sh 脚本安装也可以,查看官方最新版脚本[4]
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 # 安装指定node版本nvm uninstall # 卸载指定node版本nvm use # 切换使用指定版本nodenvm use [version] [arch] # 切换指定node版本和位数nvm reinstall-packages # 在当前版本node环境下,重新全局安装指定版本号的npm包nvm on # 打开nodejs控制nvm off # 关闭nodejs控制nvm alias # 给不同的版本号添加别名nvm unalias # 删除已定义别名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 项目,需要使用到 node 的 16+ 版本,那么我们可以直接选择安装 16+ 大版本中最新最稳定的小版本,即 v16.17.0 (Latest LTS: Gallium)。
之前还有同学问我 Latest、LTS、 Gallium 这些都是啥意思,这。。。好吧,给新同学们解释下。。
软件工程中,我们对一个项目的整个生命周期是没有一套常规定义的,所以不同的项目用的标识是有区别的,到如今,大致项目用的其实也差不多了,比较常用的几个我简单列了,如下:
Dev # 开发版,频繁出新功能,另外还修复了一些Bug和不稳定因素Alpha # 软件或系统的内部测试版本,会有不少Bug,仅内部人员使用浏览器Beta # 软件或系统的测试版本,这一版本一般是在Alpha版本后,会有不少新功能,同时也有很多BugGamma # 软件或系统接近于成熟的版本,只须要作一些小的改进就能发行测试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 之后,重启编辑器再次运行项目,应该就可以看到启动成功了!
除了管理 node 版本外,npm 镜像源的管理在开发中也是比较常用的,那假如你没有翻出墙的话,可能安装某些依赖会很慢甚至失败,你可以尝试切换一下 npm 镜像源,或者你的公司有 npm 私服,你也可以设置一个公司私服的源地址,使用 nrm 能够很好的管理和切换这些源
「全局安装」
npm install -g nrm
当你安装完成后,可以使用 nrm ls 命令查看当前可用的源
如上,这是我电脑 nrm 管理的镜像源列表,最后一个是我公司内网的所以打码了,除了最后一个都是安装 nrm 自带的,你可以使用 nrm use
「常用命令」
nrm -h /nrm -help # 查看 nrm 帮助(相关命令、信息)nrm -V # 查看当前 nrm 版本nrm ls # 查看当前 nrm 中可用的镜像源地址nrm current # 查看当前使用镜像源nrm use # 切换为某个镜像源 registry-镜像源名nrm add # 添加一个镜像源 registry-镜像源名 url-镜像源地址nrm del # 删除一个镜像源nrm test # 测试该镜像源下载响应时间
「PS:」 当然还有像 yrm 等其他管理源的工具,但工具不在多,而是够用就行。
到此我们已经初步创建并启动了项目,其实很多人只关注代码开发相关的文件,并不会去纠结项目中和核心开发无关配置文件的作用,这是不对的,我们应该对自己的项目做到极致掌控,了解项目中每一个文件每一行代码对项目的作用,接下来就来一起看看我们创建的项目中所有文件的作用吧!
在初始化创建项目时,默认创建了很多子文件(一些组件、样式文件等等),我们先把不需要的项目无关文件删干净,需要我们处理的无用文件都在 src 文件夹下:
删除 src/views 下所有文件
删除 src/stores 下所有文件
删除 src/components 下所有文件
删除 src/assets 下所有文件
清除干净之后,我们在 src/views 文件夹下新建一个 HomePage.vue 文件,随便写点东西:
hello isboyjc, This is toolsdog home page!
然后修改一下 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 的内容,把无用的删了,只留下面内容即可
最后,项目入口文件里,有一行 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[5] ,那至于为什么用这个,因为 ElementPlus 用腻了,尝试下新鲜的,我也是头一次用,组件库嘛,用什么都无所谓。
「安装 ArcoVue」
npm install --save-dev @arco-design/web-vue# or pnpm add -D @arco-design/web-vue
「配置按需加载」
因为懒,所以我们组件使用 unplugin-vue-components[6] 和 unplugin-auto-import[7] 这两款 vite 插件来开启按需加载及自动导入,插件会自动解析模板中的使用到的组件,并导入组件和对应的样式文件。
其实说白了,这两个插件一个是自动帮我们引入一些组件和指令(只做 HTML 中使用的常规组件例如各种 .vue 组件的引入以及指令的自动引入),另一个是自动帮我们做一些 API 组件的自动引入(像直接在 script 中引入的必须用 API 调用的 Message 组件以及后面我们还会用它做 Vue 的一些 API 自动引入等等)
先安装
npm i unplugin-vue-components -Dnpm i -D unplugin-auto-import# orpnpm add -D unplugin-vue-componentspnpm 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 等)自动引入的一些配置都被内置了 查看内置支持的组件库解析器[8],我们只需要导出对应 UI库 的解析器用就行了,如果你使用的组件库没有被内置,你完全可以自己写一个,如果是通用组件库的话,你甚至可以直接 PR 到插件 GitHub,混一个开源项目的 Contributors 玩,并不复杂,因为后面写项目的时候应该会简单写到几个解析函数,不多解释了。
OK,现在组件库和自动引入都做好了,先试一试,我们在 home 页面分别用 ArcoVue 的普通按钮 AButton 组件和全局提示 AMessage 组件试一试。
hello isboyjc, This is toolsdog home page! Mini Small Medium Large
可以看到,我们不需要自己去引入就可以随时随地的使用组件库中的组件了!
我们在使用 Vue 的过程中,每个 script 以及 js 文件中或多或少需要引入一些像 ref、reactive 等 VueAPI,包括 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 文件,写上下面内容。
hello {{ name }}, this is helloworld components
然后,直接在 src/views/HomePage.vue 文件中使用 HelloWorld 组件,不要引入,如下:
hello isboyjc, This is toolsdog home page! Mini Small Medium Large
上面我们在创建的 HelloWorld 组件中使用了 Vue 的 ref API,并没有引入它,而后在 HomePage 页面中使用该组件也没有引入,我们来跑一下项目。
nice!后面我们使用 Vue、VueRouter、Pinia、ArcoVue 包括自建组件等等都不需要手动引入了,当然,后续你的项目中有用到其他地方你依然可以在插件中去配置!
VueUse[9] 大家没用过的话可以先把它理解为一个基于 Vue 的工具库,Vue2、Vue3 都可以用,有很多实用的方法、组件包括指令,超级方便,后续我们会用到其中的一些方法,所以先装上
「安装」
npm i @vueuse/core// orpnpm 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 ,虽然还没有进行配置,但是默认配置会给那些自动引入的 API 报红,就比如下面这样
还有这个
作为一个强迫症患者,这是不能存在的,所以我们配置下 ESLint 和 Prettier ,配置之前我们先看看初始化的配置是什么样子
根目录下的 .eslintrc.cjs 是 ESLint 配置,当前默认如下
/* 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.json 是 Prettier 配置,当前默认如下
{}
那么如何让我们自动引入的那些 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 ,这里直接给大家说,由于我们接下来要使用 Vue3 的 CompositionAPI,那 Vue3 有几个可以直接在 最终效果: 「安装 VSCode 插件 Iconify IntelliSense」 这是 iconify 图标库的 VSCode 插件,VSCode 中搜索插件: 安装之后,我们在使用图标库内图标时,直接可以在 VSCode 中预览到图标,超级方便,如下 图标样式的话你可以直接在 icon 组件中添加,OK,图标也好了! 接下来我们简单做一下 CSS 公共样式的处理,我们在项目 src 目录下新增一个 styles 文件夹,此文件夹我们后期可以放一些公共的样式文件。 大家都知道,HTML 标签是有默认样式的,一般我们在写项目时都会直接清除掉这个默认样式,也就是做个重置。 那相较于 Eric Merer[13] 原版的清楚样式文件,Normalize.css 它在默认的 HTML 元素样式上提供了跨浏览器的高度一致性,是一种现代的、为 HTML5 准备的优质替代方案,所以我们直接使用它就好了。 下载 Normalize.css[14] 到 Styles 文件夹下,当然你也可以直接 npm 安装它,不过我比较喜欢直接下载下来这个文件。 下载下来之后直接在 main.js 最上面引入一下就行了,如下 其他的公共 css 文件我们用到的时候也可以这样引入一下就可以了。 CSS 这块,我们的原则是能简单就简单,所以我们基于 ACSS 即原子化的 CSS 框架来做。 不了解 原子化 的同学建议先看下这篇文章 「前端工程四部曲」模块化的前世今生(下) Tailwind CSS[15] 大家应该都知道, WindiCSS[16] 呢,算是他的一个超集,本来我想用 WindiCSS 的,但是 WindiCSS 作者们都不咋维护了,然后 UnoCSS[17] 又这么便捷,配置文件都不需要写,直接引入 Vite 插件和对应的预设就可以了,So,上 UnoCSS。。。 UnoCSS,官方说它是一个按需原子 CSS 引擎,它默认提供了流行实用程序优先框架 Tailwind CSS、Windi CSS、Bootstrap、Tachyon 等的通用超集,如果你习惯这些框架,依旧可以按照熟悉的方式写,无缝衔接。 不用不知道,用了是真香啊,当然,肯定是有人不喜欢原子化 CSS 的,不喜欢不用,毕竟就是写 CSS 的方式不一样而已,我这边也不会做很多配置,你甚至可以用原生 CSS ! 「安装」 如上,我们装了 4 个包 unocss[18] 核心插件 @unocss/preset-uno[19] 默认预设,Tailwind / WindiCSS 等超集 @unocss/preset-attributify[20] 属性预设,为其他预设和规则提供属性模式[21],下面会介绍 @unocss/transformer-directives[22] 指令转换器插件,允许使用 @apply指令在 style 中写原子化 css ,后面还会介绍 「配置」 还是在 Vite 插件配置中,也就是 vite.config.js 文件中配置 OK,就这么简单。 「使用」 在使用之前我们先在入口文件 main.js 中一下 UnoCSS 的 css 文件: 基础使用如下: 如果你对这些原子化样式不太熟悉的话,直接打开样式查询地址 https://uno.antfu.me/[23] 输入对应 CSS 样式去搜就可以了,如下: 当然,我是不建议你这么做的,因为个人觉得不太好用,感觉好像不太完善,既然我们用的预设支持 Tailwind / WindiCSS ,所以,我建议大家直接去看看这两个文档,了解一个大概,按照这两个东西的写法来就可以,有啥不会的去这两个的文档里搜 TailwindCSS[24] & TailwindCSS中文文档[25] WindiCSS[26] 那推荐最好是直接去看 WindiCSS 使用文档,因为作者是一个人嘛,如下,直接搜对应 CSS 看怎样使用就可以了 然后在项目中直接使用就行了,真丫机智!!! 我们上面安装了 @unocss/preset-attributify 属性预设,所以我们也可以使用属性模式,可以将实用程序分为多个属性,这样写: 肯定有人觉得原子化 CSS 全写在 HTML 中,太多的话,看上去眼花缭乱很不爽,那所以我们还安装了指令转换器插件 @unocss/transformer-directives 就是为了解决这个问题了。 它允许我们使用 @apply指令在 style 中写原子化 CSS : 「安装 VSCode 插件 UnoCSS」 我们安装一下 UnoCSS 官方的 VSCode 插件,VSCode 扩展中搜索: 安装之后,就可以在编辑器中看到我们写的原子化对应的 CSS 样式了,如下: 当然 UnoCSS 还有很多功能,这里不过多描述了,有兴趣文档看一看,后面写项目的过程中也会逐步的去介绍。 如果我们是刚开始使用,肯定会不习惯,但是,只要坚持几天,直接真香警告!毕竟,一切都是为了简单,一切都是为了省事儿,一切都为了少写代码!!! 我们在项目 src 目录下添加一个 utils 文件夹,此文件夹用于存放我们项目中用到的一些公共方法文件。 同样的,我们在项目 src 目录下添加一个 hooks 文件夹,此文件夹用于存放我们项目中用到的一些 hooks 文件,因为我们用 Vue3 的 CompsitionAPI,后面用多了自然会有很多 hooks 文件,针对一些公用的,我们统一管理在此文件夹下。 平常我们做项目,一般和请求相关的文件都统一放在一个文件夹下,所以我们在项目 src 目录下添加一个 api 文件夹,用于存放和请求相关的文件,因为项目性质,所以我们应该暂时用不到请求后端接口,那这边 api 文件的一些配置以及 axios 的封装甚至 API Mock 配置这里都先不展开说了,回头我会写好这块代码提交到 GitHub ,后面有需要的话会写一篇 axios 封装相关的文章来单独介绍这块。 先来说环境的配置,先放个官方文档压压惊 Vite env 配置文档[27] OK,我们在 env 目录下新建下面 3 个文件 .env 所有模式下都会加载 .env.development 只在开发模式下加载 .env.production 只在生产模式下加载 .env 文件在所有模式下都会加载,所以这里我们可以写一些所有环境都通用的环境变量,如下: 注意,我们在 Vite 中配置的环境变量默认只有以 VITE_ 开头的配置,才会暴露给客户端,我们才能在项目中获取到。 开发模式 .env.development 配置 那生产环境除了环境标识 VITE_APP_ENV 和开发模式标识不同,其他配置项应尽量保持一致,只是配置项的内容不同而已,不一一的展示了。 接下来修改下 package.json 脚本命令如下 上面脚本中我们给启动命令搞成了 在 serve 脚本命令配置中,我们还传了一个 mode,其实这个 mode 就是对应我们的环境文件 .env.[mode] 开发环境默认 mode 就是 development,生产环境默认 mode 就是 development,所以脚本命令这里我不传 mode 也可以,但是如果大家把开发环境文件由 .env.development 改成 .env.dev,那脚本中 mode 就得传 —-mode dev,build 时也是一样的道理,如果有其他环境,那脚本命令传入对应的 mode 就可以了。 如果想要在 vite.config.js 文件中获取对应运行 mode 环境变量的配置,我们可以使用 vite 的 loadEnv API[28]。 Vite 的 defineConfig 方法也可以接收一个返回配置对象的回调函数,回调函数的参数里我们可以拿到运行脚本命令时传入的 mode 值,从而使用 loadEnv 方法去在 Vite 配置文件中获取对应 mode 下的环境变量,如下: 那其他一些基础配置就不一一说明了,大家可以直接看 Vite 官方文档[29] 目前我们的配置如下: 那项目配置文件暂时就到这里了,后期写项目还会用到一些东西,我们到时候再配置。 上面说了,环境变量默认以 VITE_ 开头的配置,才会暴露给客户端,我们也写了几个 VITE_ 开头的配置,所以在项目运行时,我们可以直接 import.meta.env.VITE_XXX 去查看配置,但是这样太麻烦了,所以我们写一个统一的配置文件去获取环境变量,包括项目后期的一些全局配置也可以写里面 项目 src 目录下新建 config/config.js 文件,写入下面文件: 上面代码不复杂,所以不过多解释了,介绍下为什么用这种方式,而不是直接写一个对象导出,其实是因为有次写项目有用到了动态修改全局配置的需求,所以就把全局配置项获取写成动态的了。 我们写入配置时,只需要在 configSource 对象中写入就可以了,项目中使用起来的话如下: 打开项目根目录的 .vscode/extensions.json 文件如下: 这是创建项目时默认存在的扩展插件推荐配置,此文件的作用上面也介绍过了,就是个扩展推荐,数组里是 VSCode 的扩展插件 ID,你在根目录打开此项目时,如果编辑器没有安装这两个插件,VSCode 就会在右下角自动提示你去安装插件。 那我们没有使用 TS 直接干掉 vue.vscode-typescript-vue-plugin 这一项。 我们想要添加一个插件到推荐里,可以复制插件 ID 写到此配置中,也可以直接点击添加到工作区建议,如下: 回顾一下上文,我们一共装了 2 个插件 那我这里把这两个插件的 ID (注意是 ID 不是插件名字)都写进推荐列表里了: 大家如果克隆项目使用 VSCode 启动,就会提示你去安装这些插件了。 GitHub 创建仓库,仓库名为 toolsdog,开源许可选择 MIT 先在本地仓库初始化一下 git 。 Copy 一下仓库地址,给本地项目关联远程仓库,入下: 然后首次提交一下代码,就 OK 了! 由于项目一直在开发中,所以我给当前分支提交打了个标记,大家无需拉取主分支代码,直接下载当前标记的代码就和文中代码一致了,地址如下 项目 GitHub 地址[30] 简单写个小项目,没有配置太多东西,像一般来说我们构建工作流都会用 GitHook 工具在提交时去检测代码,那目前碍于文章篇幅加上眼下就我自己开发,所以也没配置,后期都会补充,不要着急,此项目大家仅供参考,照搬不可取,汲取对自身有用的就可以了,有什么不足之处也请指出,如果你想学习下 Vue3,可以跟着一块来敲一敲,不喜勿喷! [1] Node.js:https://nodejs.org/ [2] create-vue:https://github.com/vuejs/create-vue [3] nvm-windows:https://github.com/coreybutler/nvm-windows/releases [4] 查看官方最新版脚本:https://github.com/nvm-sh/nvm [5] arco.design:https://arco.design/vue/docs/start [6] unplugin-vue-components:https://github.com/antfu/unplugin-vue-components [7] unplugin-auto-import:https://github.com/antfu/unplugin-auto-import [8] 查看内置支持的组件库解析器:https://github.com/antfu/unplugin-vue-components#importing-from-ui-libraries [9] VueUse:https://vueuse.org/ [10] unplugin-icons:https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fantfu%2Funplugin-icons [11] iconify:https://link.juejin.cn/?target=https%3A%2F%2Ficon-sets.iconify.design%2F [12] icones:https://icones.js.org/ [13] Eric Merer:https://meyerweb.com/eric/tools/css/reset/ [14] Normalize.css:https://necolas.github.io/normalize.css/latest/normalize.css [15] Tailwind CSS:https://link.juejin.cn/?target=https%3A%2F%2Ftailwindcss.com%2F [16] WindiCSS:https://windicss.org/ [17] UnoCSS:https://github.com/unocss/unocss [18] unocss:https://github.com/unocss/unocss [19] @unocss/preset-uno:https://github.com/unocss/unocss/tree/main/packages/preset-uno [20] @unocss/preset-attributify:https://github.com/unocss/unocss/tree/main/packages/preset-attributify [21] 属性模式:https://github.com/unocss/unocss/tree/main/packages/preset-attributify#attributify-mode [22] @unocss/transformer-directives:https://github.com/unocss/unocss/blob/main/packages/transformer-directives/README.md [23] https://uno.antfu.me/:https://uno.antfu.me/ [24] TailwindCSS:https://tailwindcss.com/ [25] TailwindCSS中文文档:https://www.tailwindcss.cn/ [26] WindiCSS:https://windicss.org/ [27] Vite env 配置文档:https://cn.vitejs.dev/guide/env-and-mode.html#env-files [28] loadEnv API:https://cn.vitejs.dev/guide/api-javascript.html#loadenv [29] Vite 官方文档:https://cn.vitejs.dev/ [30] 项目 GitHub 地址:https://github.com/isboyjc/toolsdog/releases/tag/v0.0.1-dev 阅读原文Iconify IntelliSense// or 搜索插件IDantfu.iconify
Styles 公共样式管理、初始化样式
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")
配置 UnoCSS
npm install --save-dev unocss @unocss/preset-uno @unocss/preset-attributify @unocss/transformer-directives# orpnpm i -D unocss @unocss/preset-uno @unocss/preset-attributify @unocss/transformer-directives
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: [] }), ] // ...})
import { createApp } from 'vue'import { createPinia } from 'pinia'import '@/styles/normalize.css' // 导入Unocss样式 import 'uno.css' // ...
UnoCSS// or 搜索ID antfu.unocss
Utils、Hooks、API 管理
其他 Vite 配置
# 所有环境都会加载# 项目标识代码VITE_APP_CODE="TOOLSDOG"# 项目名VITE_APP_NAME="工具狗"# 项目描述VITE_APP_DESCRIPTION="你用的到的工具,这里都有!"
# 开发环境加载# 环境标识VITE_APP_ENV="development"# 公共基础路径VITE_BASE="/"# 代理URL路径VITE_BASE_URL ="/api"# 模拟数据接口路径VITE_BASE_MOCK_URL ="/mock-api"# 服务端接口路径VITE_BASE_SERVER_URL = "..."# 打包是否使用MockVITE_APP_PRODMOCK=false
{ "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// orpnpm serve
// export default defineConfig({}) 修改export default defineConfig(({mode}) => { return {}})
/* * @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 加载 loaderimport { 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(/^
添加 Config 配置
/* * @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 }
import { getConfig, setConfig, resetConfig } from "@/config/config.js"// 获取配置getConfig("a")getConfig("a.b")getConfig("a.b.c")// 动态设置setConfig({ ... })// 重置配置resetConfig()
配置 VSCode 推荐扩展插件
{ "recommendations": [ "vue.volar", "vue.vscode-typescript-vue-plugin" ]}
Iconify IntelliSenseUnoCSS
{ "recommendations": [ "vue.volar", "antfu.iconify", "antfu.unocss" ]}
项目协同
创建 GIT 代码仓库
为项目绑定远程仓库
git init
git remote add origin [email protected]:isboyjc/toolsdog.git
最后
Reference