环境搭建
创建项目
可以使用 Vue CLI 或 Vite来创建初始化一个vue3项目,我是使用vite(目前官方也推荐使用vite),使用的是yarn管理器
# For Yarn Modern (v2+)
yarn create vue@latest
# For Yarn ^v4.11
yarn dlx create-vue@latest
✔ Project name: … <vue3_project>
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes ✔ Add Vitest for Unit testing? … No / Yes
✔ Add an End-to-End Testing Solution? … No / Cypress / Nightwatch / Playwright
✔ Add ESLint for code quality? … No / Yes ✔ Add Prettier for code formatting? … No / Yes
✔ Add Vue DevTools 7 extension for debugging? (experimental) … No / Yes Scaffolding project in ./<your-project-name>... Done.
ESLint+Prettier,代码规范,Prettier可以让代码格式化,好看
代码规范
1、editorconfig统一编码风格
EditorConfig 有助于为不同 IDE 编辑器上处理同一项目的多个开发人员维护一致的编码风格。
根目录下创建 .editorconfig 文件
# http://editorconfig.org
root = true
[*] # 表示所有文件适用
charset = utf-8 # 设置文件字符集为 utf-8
indent_style = space # 缩进风格(tab | space)
indent_size = 2 # 缩进大小
end_of_line = lf # 控制换行类型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行首的任意空白字符
insert_final_newline = true # 始终在文件末尾插入一个新行
[*.md] # 表示仅 md 文件适用以下规则
max_line_length = off
trim_trailing_whitespace = false
CRLF、CR、LF详解
很长一段时间里,对于CRLF、CR、LF的理解仅限于不同操作系统下对换行符的定义。
CR:Carriage Return,对应ASCII中转义字符\r,表示回车
LF:Linefeed,对应ASCII中转义字符\n,表示换行
CRLF:Carriage Return & Linefeed,\r\n,表示回车并换行
众所周知,
Windows操作系统采用两个字符来进行换行,即CRLF;
Unix/Linux/Mac OS X操作系统采用单个字符LF来进行换行;另外,MacIntosh操作系统(即早期的Mac操作系统)采用单个字符CR来进行换行。
VSCode需要安装一个插件才可以读取这个文件:EditorConfig for VS Code,webstorm不需要安装
2、prettier工具_格式化代码
Prettier 是一款强大的代码格式化工具,支持 JavaScript、TypeScript、CSS、SCSS、Less、JSX、Angular、Vue、GraphQL、JSON、Markdown 等语言,基本上前端能用到的文件格式它都可以搞定,是当下最流行的代码格式化工具。
1.安装prettier
npm install prettier -D
2.配置.prettierrc文件:
{
"useTabs": false,
"tabWidth": 2,
"printWidth": 80,
"singleQuote": false,
"trailingComma": "none",
"semi": false,
"endOfLine": "auto",
"htmlWhitespaceSensitivity": "ignore"
}
代码拉取到本地可能会有类似
error Delete␍prettier/prettier的错误导致项目运行失败,是因为prettier对行尾控制换行类型检验的问题,可以设置"endOfLine": "auto",取消对该项的检验
/* prettier的配置 */
"prettier.printWidth": 100, // 超过最大值换行
"prettier.tabWidth": 4, // 缩进字节数
"prettier.useTabs": false, // 缩进不使用tab,使用空格
"prettier.semi": true, // 句尾添加分号
"prettier.singleQuote": true, // 使用单引号代替双引号
"prettier.proseWrap": "preserve", // 默认值。因为使用了一些折行敏感型的渲染器(如GitHub comment)而按照markdown文本样式进行折行
"prettier.arrowParens": "avoid", // (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid:省略括号
"prettier.bracketSpacing": true, // 在对象,数组括号与文字之间加空格 "{ foo: bar }"
"prettier.disableLanguages": ["vue"], // 不格式化vue文件,vue文件的格式化单独设置
"prettier.endOfLine": "auto", // 结尾是 \n \r \n\r auto
"prettier.eslintIntegration": false, //不让prettier使用eslint的代码格式进行校验
"prettier.htmlWhitespaceSensitivity": "ignore",//⭐结束标签的结尾尖括号掉到了下一行
"prettier.ignorePath": ".prettierignore", // 不使用prettier格式化的文件填写在项目的.prettierignore文件中
"prettier.jsxBracketSameLine": false, // 在jsx中把'>' 是否单独放一行
"prettier.jsxSingleQuote": false, // 在jsx中使用单引号代替双引号
"prettier.parser": "babylon", // 格式化的解析器,默认是babylon
"prettier.requireConfig": false, // Require a 'prettierconfig' to format prettier
"prettier.stylelintIntegration": false, //不让prettier使用stylelint的代码格式进行校验
"prettier.trailingComma": "es5", // 在对象或数组最后一个元素后面是否加逗号(在ES5中加尾逗号)
"prettier.tslintIntegration": false // 不让prettier使用tslint的代码格式进行校验
有的文件不需要进行代码格式化,可以创建下面的文件
3.创建.prettierignore忽略文件
/dist/*
.local
.output.js
/node_modules/**
**/*.svg
**/*.sh
/public/*
4.VSCode需要安装prettier的插件
进入插件配置设置与.prettierrc文件相同的格式化风格⭐
5.测试prettier是否生效
- 测试一:在代码中保存代码;
- 测试二:配置一次性修改的命令;
如果保存时不能格式化,可能是因为vscode的设置没有勾选
代码中保存一次只能格式化一个文件,如果想一次性格式化除忽略外的所有文件,可以在pakeage.json中配置脚本
在package.json中配置一个scripts:
"prettier": "prettier --write ."
补充:
// 使能每一种语言默认格式化规则 `settings.json` "[html]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[css]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[less]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },prettier插件失效可能原因:
Require config set to true and no config present. Skipping file.解决:扩展设置 > 去掉勾选
3、使用ESLint检测
1.在前面创建项目的时候,我们就选择了ESLint,所以Vue会默认帮助我们配置需要的ESLint环境。
2.VSCode需要安装ESLint插件:
3.解决eslint和prettier冲突的问题:
安装插件:(vue在创建项目时,如果选择prettier,那么这两个插件会自动安装)
npm i eslint-plugin-prettier eslint-config-prettier -D
在.eslintrc.js中添加prettier插件:
extends: [
"plugin:vue/essential", //vue2
"plugin:vue/vue3-essential", //vue3
"eslint:recommended",
"@vue/typescript/recommended", //ts校验
"@vue/prettier",
"@vue/prettier/@typescript-eslint", //ts校验
'plugin:prettier/recommended'
],
npm i @vue/eslint-config-prettier -D
4、husky和eslint
虽然我们已经要求项目使用eslint了,但是不能保证组员提交代码之前都将eslint中的问题解决掉了:
- 也就是我们希望保证代码仓库中的代码都是符合eslint规范的;
- 那么我们需要在组员执行
git commit命令的时候对其进行校验,如果不符合eslint规范,那么自动通过规范进行修复;
那么如何做到这一点呢?可以通过Husky工具:
- husky是一个git hook工具,可以帮助我们触发git提交的各个阶段:pre-commit、commit-msg、pre-push
如何使用husky呢?
这里我们可以使用自动配置命令:注意⭐:这里自动执行这个命令就会做下面的三件事,如果你只是安装husky-init,那么请自己做下面的三件事
npx husky-init && npm install
npx husky-init '&&' npm install
//或者
npx husky-init; npm install
使用上面的命令就会做下面三件事:(如果失败就手动做下面这几件事)
throw new Error(
.git can't be found (see ${url}));如果报这种错可能是因为该项目文件夹中没有.git文件夹,就是没有连接远端
1.安装husky相关的依赖:
npm i husky -D
2.在项目目录下创建 .husky 文件夹:
npx husky install
3.在package.json中添加一个脚本:
接下来,我们需要去完成一个操作:在进行commit时,执行lint脚本,就是在组员执行commit提交时,它会自动执行这个命令进行检测,如果不规范会纠正代码格式
这个时候我们执行git commit的时候会自动对代码进行lint校验。
注意:vscode里面的终端打开的其实是windows系统里面的powershell(微软弄得,想挤掉cmd)
好用排行:
git bash>cmd>powershell像上面的 npx husky-init && npm install 命令中的 && 连接符在cmd中识别不了,但是在git bash里面就可以,如果想在cmd中使用,可以把&&加上引号
'&&'
5、git commit规范
5.1. 代码提交风格
通常我们的git commit会按照统一的风格来提交,这样可以快速定位每次提交的内容,方便之后对版本进行控制。
不统一的话,你的提交就是自己写的信息,有的信息是不规范的,看看尤雨溪提交的规范信息
但是如果每次手动来编写这些是比较麻烦的事情,我们可以使用一个工具:Commitizen
Commitizen是一个帮助我们编写规范 commit message 的工具;
1.安装Commitizen
npm install commitizen -D
2.安装cz-conventional-changelog,并且初始化cz-conventional-changelog:
npx commitizen init cz-conventional-changelog --save-dev --save-exact
这个命令会帮助我们安装cz-conventional-changelog:
并且在package.json中进行配置:
这个时候我们提交代码需要使用 npx cz而不是使用 git commit
- 第一步是选择type,本次更新的类型
| Type | 作用 |
|---|---|
| feat | 新增特性 (feature) |
| fix | 修复 Bug(bug fix) |
| docs | 修改文档 (documentation) |
| style | 代码格式修改(white-space, formatting, missing semi colons, etc) |
| refactor | 代码重构(refactor) |
| perf | 改善性能(A code change that improves performance) |
| test | 测试(when adding missing tests) |
| build | 变更项目构建或外部依赖(例如 scopes: webpack、gulp、npm 等) |
| ci | 更改持续集成软件的配置文件和 package 中的 scripts 命令,例如 scopes: Travis, Circle 等 |
| chore | 变更构建流程或辅助工具(比如更改测试环境) |
| revert | 代码回退 |
- 第二步选择本次修改的范围(作用域)
- 第三步选择提交的信息
- 第四步提交详细的描述信息
- 第五步是否是一次重大的更改(直接回车默认就是no)
- 第六步是否影响某个open issue(开源项目会用到)
我们也可以在scripts中构建一个命令来执行 cz:
本地安装的commitizen如果是用不了,可以全局安装,之后使用
cz命令代替npx cz,使用的就是全局安装的commitizen
5.2. 代码提交验证commitlint
如果我们按照cz来规范了提交风格,但是依然有同事通过 git commit 按照不规范的格式提交应该怎么办呢?
- 我们可以通过
commitlint来限制提交;
1.安装 @commitlint/config-conventional 和 @commitlint/cli
npm i @commitlint/config-conventional @commitlint/cli -D
2.在根目录创建commitlint.config.js文件,配置commitlint
module.exports = {
extends: ['@commitlint/config-conventional']
}
3.使用husky生成commit-msg文件,验证提交信息(拦截组员编写的提交信息)
npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"
之后修改,使用git commit -m '哈哈哈' 就会提交不成功,还是需要使用 npx cz
node-modules文件夹下的 .bin文件夹中,都是配置的脚本,执行这里的脚本要使用:
npx 脚本名
_src文件夹别名配置
Vite 项目,在 vite.config.js 文件中配置别名:
javascript
// vite.config.js
import { defineConfig } from 'vite';
import path from 'path';
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
// 其他别名配置
}
}
});
在这个配置中,@ 被设置为 src 目录的别名。
如果你的项目使用 TypeScript,你还需要在 tsconfig.json 文件中配置路径映射:
json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
在这个配置中,"@/*" 表示任何以 @ 开头的导入路径都会被映射到 src 目录下的相应路径。
注意事项
- 如果代码报错
//根据项目中使用的构建工具进行安装
npm install --save-dev @types/node
yarn add --dev @types/node
- 确保别名不与现有的模块或包冲突。
- 在使用别名时,不需要加
~或其他特殊字符,直接使用@或你定义的别名即可。 - 如果你的项目中有多个
tsconfig.json文件(例如使用了 TypeScript 的项目引用),确保在每个相关的tsconfig.json文件中都添加了别名配置。
配置完成后,你就可以在项目中使用别名了,例如:
// 使用别名导入组件
import HelloWorld from "@/components/HelloWorld.vue";
第三方库集成
1、vue.config.js集成
vue.config.js有三种配置方式: 查看vue cli文档
-
方式一:直接通过CLI提供给我们的选项来配置:
- 比如publicPath:配置应用程序部署的子目录(默认是
/,相当于部署在https://www.my-app.com/); - 比如outputDir:修改输出的文件夹;
- 比如publicPath:配置应用程序部署的子目录(默认是
-
方式二:通过configureWebpack修改webpack的配置:
- 可以是一个对象,直接会被合并;
- 可以是一个函数,会接收一个config,可以通过config来修改配置;
-
方式三:通过chainWebpack修改webpack的配置:
- 是一个函数,会接收一个基于 webpack-chain 的config对象,可以对配置进行修改;
const path = require('path')
module.exports = {
//配置方式一:使用vue cli提供的配置,与webpack类似,查看vue cli文档
outputDir: './build',
//配置方式二:和webpack属性完全一致
//1、配置在configureWebpack对象内:最后会和原有配置进行合并
// configureWebpack: {
// resolve: {
// alias: {
// components: '@/components'
// }
// }
// },
//2、配置在configureWebpack函数内:重新配置的属性会把原有的属性进行覆盖
// configureWebpack: (config) => {
// //重新配置原有配置
// config.resolve.alias = {
// '@': path.resolve(__dirname, 'src'),
// components: '@/components'
// }
// },
// 3.配置方式三:webpack链式编程,覆盖
chainWebpack: (config) => {
config.resolve.alias
.set('@', path.resolve(__dirname, 'src'))
.set('components', '@/components')
}
}
这里可能会有一个报错,这是ESLint的报错,他不让我们使用commonjs的模块导入,但是如果我们像这样做的话,可以在 .eslintrc.js里面把这个检测配置为off
2、vue-router-ts集成
导入类型,import后面可以加上 type 以示区分,也可以不加
import { createRouter, createWebHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'
import localCache from '@/utils/cache'
import Login from '../views/login/index.vue'
const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/main'
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/main',
name: 'Main',
component: () =>
import(/* webpackChunkName: "about" */ '../views/main/index.vue')
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
//判断token,如果没有,一律跳转到登陆页面
router.beforeEach((to) => {
if (to.path !== '/login') {
const token = localCache.getCache('token')
if (!token) {
return '/login'
}
}
})
export default router
import type { RouteRecordRaw } from 'vue-router'
import 后面有一个type,只是表示
导入的模块是类型,不加也可以
3、vuex-ts集成
vuex对于ts的语法来说是不完善的,在ts中使用vuex是非常不好使用的。。。
index.ts入口文件:
rootState是state的类型,建议传入
import { createStore } from 'vuex'
import login from './login'
import type { rootState } from './types'
interface rootState {
name: string
age: number
}
export default createStore<rootState>({
state() {
return {
name: '',
age: 0
}
},
mutations: {},
actions: {},
modules: {
login//login模块
}
})
login模块:
模块的类型需要单独导入,并且传入两个泛型
import type { Module } from 'vuex'
import type { rootState } from '../types'
export interface loginState {
token: string
userInfo: any
menus: Array<any>
}
const loginModule: Module<loginState, rootState> = {
//Module<S,R>,这里的Module必须传递两个泛型类型(点击源码查看),S是当前模块的state类型,R是rootstate的类型⭐⭐
namespaced: true,
state() {
return {
token: '',
userInfo: {},
menus: []
}
},
getters: {},
mutations: {
setToken(state, payload) {
state.token = payload
},
setUserInfo(state, payload) {
state.userInfo = payload
},
setMenus(state, payload) {
state.menus = payload
}
},
actions: {}
}
export default loginModule
4、elementplus集成
按需引入,详见项目配置及文档
main.ts
createApp(App)的类型就是App //import type { App } from 'vue'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
//按需引入
import 'element-plus/packages/theme-chalk/lib/base.css'
import registerApp from './global'
const app = createApp(App)
registerApp(app)
app.use(store).use(router).mount('#app')
5、axios-ts集成
//res的类型问题:
import axios from 'axios'
//promise是有类型的,泛型类,resolve参数的类型就是res的类型,定义的泛型的类型就是这两个的类型,在axios里面定义好了res的类型
const p = new Promise<string>((resolve) => {
resolve('uyfb')
})
p.then((res) => {
console.log(res.length)
})
axios.all([
axios.get('127.0.0.1:3000/user'),
axios.get('127.0.0.1:3000/pic')
]).then(res=>console.log(res))//res是一个数组
const token = 'this is token'
// 请求拦截器:两个参数
/**
* 参数一:请求发送成功会执行的函数
* 参数二:请求发送失败会执行的函数
*/
axios.interceptors.request.use(
(config) => {
config.headers['authorization'] = 'Bearer ' + token
return config
},
(err) => {
console.log('这次请求发送失败了哦')
}
)
//响应拦截器
/**
* 参数一:响应成功会执行的函数
* 参数二:响应失败会执行的函数
*/
axios.interceptors.response.use(res=>{
return res.data
},err=>{
console.log('响应失败了')
})
axios封装
封装类service/request/index.ts
import axios from 'axios'
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import { ElLoading } from 'element-plus'
import type { ILoadingInstance } from 'element-plus/lib/el-loading/src/loading.type'
//定义拦截器接口
interface RequestInterceptors<T = AxiosResponse> {
requestInterceptor?: (cinfig: AxiosRequestConfig) => AxiosRequestConfig
requestInterceptorCatch?: (err: any) => any
responseInterceptor?: (cinfig: T) => T
responseInterceptorCatch?: (err: any) => any
}
// 自定义config类型,继承自axios原本的config类型AxiosRequestConfig,这样自定义类型里面既有AxiosRequestConfig所有的类型,又有额外加上的interceptors类型
interface MyRequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
interceptors?: RequestInterceptors<T>
showLoading?: boolean
}
class Request {
instance: AxiosInstance //axios实例
interceptors?: RequestInterceptors //拦截器保存
loading?: ILoadingInstance //ElementPlus中loading函数保存
showLoading?: boolean //是否使用loading加载图标
// constructor(config: AxiosRequestConfig) {
constructor(config: MyRequestConfig) {
this.instance = axios.create(config)
//拦截器保存为变量,不保存的话就config.interceptor获取
this.interceptors = config.interceptors
//1、 每个baseURL实例对象拥有的拦截器
this.instance.interceptors.request.use(
this.interceptors?.requestInterceptor,
this.interceptors?.requestInterceptorCatch
) //?.为可选链操作符
this.instance.interceptors.response.use(
this.interceptors?.responseInterceptor,
this.interceptors?.responseInterceptorCatch
)
//2、所有实例对象共有的拦截器
this.instance.interceptors.request.use(
(config) => {
// console.log('所有请求的请求拦截')
if (this.showLoading) {
this.loading = ElLoading.service({
text: 'Loading',
spinner: 'el-icon-loading'
})
}
return config
},
(err) => {
return err
}
)
this.instance.interceptors.response.use(
(res) => {
// console.log('所有请求的响应拦截')
this.loading?.close()
const data = res.data
if (data.returnCode === '-1001') {
console.log('请求失败~, 错误信息')
} else {
return data
}
},
(err) => {
// 判断响应状态码,给出失败提示
// 将loading移除
this.loading?.close()
// 例子: 判断不同的HttpErrorCode显示不同的错误信息
if (err.response.status === 404) {
console.log('404的错误~')
}
return err
}
)
}
//定义请求方法
//泛型T是使用时定义的res的类型
api<T>(config: MyRequestConfig<T>): Promise<T> {
return new Promise((resolve, reject) => {
//3、单个请求独有的请求拦截器(看需求,需要就在定义接口时传入)
if (config.interceptors?.requestInterceptor) {
config = config.interceptors.requestInterceptor(config)
}
//判断有没有传入showLoading
if (config.showLoading) {
this.showLoading = true
} else {
//默认不显示loading图标
this.showLoading = false
}
//响应拦截返回的是res.data,已不是AxiosResponse类型,改为<any,T>
this.instance
.request<any, T>(config)
.then((res) => {
//单个请求独有的数据处理(看需求,需要就在定义接口时传入)
if (config.interceptors?.responseInterceptor) {
res = config.interceptors.responseInterceptor(res)
}
this.showLoading = false
resolve(res)
})
.catch((err) => {
this.showLoading = false
reject(err)
return err
})
})
}
// 基于api封装不同的请求,get请求就直接使用 .get()
get<T>(config: MyRequestConfig<T>): Promise<T> {
return this.api<T>({ ...config, method: 'GET' })
}
post<T>(config: MyRequestConfig<T>): Promise<T> {
return this.api<T>({ ...config, method: 'POST' })
}
put<T>(config: MyRequestConfig<T>): Promise<T> {
return this.api<T>({ ...config, method: 'PUT' })
}
delete<T>(config: MyRequestConfig<T>): Promise<T> {
return this.api<T>({ ...config, method: 'DELETE' })
}
patch<T>(config: MyRequestConfig<T>): Promise<T> {
return this.api<T>({ ...config, method: 'PATCH' })
}
}
export default Request
入口文件service/index.ts
import Request from './request'
import localCache from '@/utils/cache'
const request = new Request({
// 这里使用第三种环境变量方法,第二种在config.ts
baseURL: process.env.VUE_APP_BASE_URL,
timeout: process.env.VUE_APP_TIME_OUT,
// 创建实例时传入拦截器,好处在于不同的实例有自己的拦截器,可以实现不同配置(可选)
interceptors: {
requestInterceptor(config) {
// console.log('请求成功')
const token = localCache.getCache('token')
if (token) {
config.headers['Authorization'] = 'Bearer ' + token
}
return config
},
requestInterceptorCatch(err) {
// console.log('请求失败')
return err
},
responseInterceptor(res) {
// console.log('响应成功')
return res
},
responseInterceptorCatch(err) {
// console.log('响应失败')
return err
}
}
})
export default request
请求封装
import request from '../index'
enum LoginAPI {
AccountLogin = '/login',
UserInfo = '/users/', // /users/id
UserMenus = '/role/' // /role/1/menu
}
interface Account {
name: string
password: string
}
//后端返回的数据格式类型,就是axios封装中的那个泛型 T⭐
interface DataType<T = any> {
code: number
data: T //这个data类型不确定(每个接口返回的数据都是不一样的),可以自己定义
}
/**
* 请求的数据data:有的data很简单,可以自己定义它的类型,有的较为复杂,可以使用工具json2ts自动生成(可能会有错,检查),但是那些过于复杂的数据格式,定义类型就很复杂了,对于前端的工作量非常大,建议直接使用any
*/
// data里面的具体类型
interface loginResult {
id: number
name: string
token: string
}
//登录请求 请求返回promise对象
export function accountLoginRequest(account: Account) {
return request.post<DataType<loginResult>>({
url: LoginAPI.AccountLogin,
data: account
})
}
//用户信息
export function userInfoRequestById(id: number) {
return request.get<DataType>({
url: LoginAPI.UserInfo + id,
interceptors: {
requestInterceptor(config) {
console.log('配置单个请求的拦截器')
return config
}
},
showLoading: true
})
}
6、区分不同环境
在开发中,我们需要根据不同的环境设置不同的环境变量
- 开发环境:development
- 生产环境:production
- 测试环境:test
不同环境的区分:
-
方式一:不同情况下手动修改不同变量
-
方式二:根据
process.env.NODE_ENV的值进行区分process.env.NODE_ENV在不同的环境下有不同的值,他会通过webpack的插件DefinePlugin注入不同的值 在开发环境下的值:development 在生产环境下的值:production 在测试环境下的值:testrequest文件夹下config.js(在网络请求封装文件夹里面配置)
console.log(process.env.NODE_ENV) let base_url='' if(process.env.NODE_ENV==="development"){ base_url='127.0.0.1:3000/api' }else if(process.env.NODE_ENV==="production"){ base_url='127.0.0.1:3000/aaa' }else if(process.env.NODE_ENV==="development"){ base_url='127.0.0.1:3000/test' } export {base_url} -
方式三:编写不同环境变量配置文件
在项目根目录下创建不同的
.env文件来区分不同的环境配置。.env:默认环境变量文件,所有环境都会加载这个文件中的变量。.env.development:开发环境的环境变量文件。.env.production:生产环境的环境变量文件。注意:要根据使用的工具来决定,比如
Vue CLI创建的项目的环境变量的命名必须以VITE_APP_开头,这样Vue CLI才能正确地将其注入到项目中。我使用的是viteVITE_APP_API_URL=https://api.example-development.com VITE_APP_AUTH_KEY=your_auth_key之后可以通过
console.log(import.meta.env);来获取变量
svg图标的引入和封装
- 安装插件:
- 安装
vite-plugin-svg-icons插件,它可以帮助你将 SVG 文件转换为 Vue 组件,并允许你动态更改图标的颜色和大小; - 安装
vite-plugin-svg-icons插件,因为vite-plugin-svg-icons插件插件依赖fast-glob包,不安装会报错
yarn add vite-plugin-svg-icons -D
yarn add fast-glob --dev
- 配置 Vite: 在
vite.config.js或vite.config.ts文件中配置插件,指定 SVG 文件的目录。
//引入
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
...
createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
symbolId: 'icon-[dir]-[name]',
}),
完整文件:
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
import path from "path";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), "src/assets/iconfont")],
symbolId: "icon-[dir]-[name]",
}),
],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
});
- 引入 SVG 注册脚本: 在
main.ts或main.js文件中引入 SVG 注册脚本。
import { createApp } from "vue";
import "./style.css";
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import App from "./App.vue";
//引入svg注册脚本
import "virtual:svg-icons-register";
//引入封装好的图标库
import SvgIcon from "@/components/SvgIcon.vue";
const app = createApp(App);
app.use(ElementPlus);
//注册图标库
app.component("SvgIcon", SvgIcon);
app.mount("#app");
- 添加 SVG 图标: 将 SVG 文件放入指定的目录(如
src/assets/iconfont)。 - 自定义 SVG 图标组件
创建一个自定义的 SVG 图标组件,以便更灵活地控制图标的样式。因为希望更改图标的颜色,需要将
fill属性的值设置为currentColor或删除该属性,以便通过 CSS 控制颜色。
<template>
<svg :class="svgClass" aria-hidden="true">
<use :xlink:href="iconClassName" :fill="color" />
</svg>
</template>
<script setup lang="ts">
import { computed } from 'vue';
const props = defineProps({
iconName: {
type: String,
required: true
},
className: {
type: String,
default: ''
},
color: {
type: String,
default: '#409eff'
}
});
const iconClassName = computed(() => `#${props.iconName}`);
const svgClass = computed(() => {
return `svg-icon ${props.className}`;
});
</script>
<style scoped>
.svg-icon {
width: 30px;
height: 30px;
position: relative;
fill: currentColor;
vertical-align: -2px;
}
</style>
- 使用 SVG 图标: 在 Vue 组件中使用
<svg-icon>组件,并传递icon-class属性来指定图标。
<template>
<SvgIcon iconName="icon-a" color="#e6899b" />
</template>