vite 项目起步式
npm create vite
- 1.命名项目名称 - 2. 选择技术框架 - 3. 进入项目文件夹 npm i 安装依赖, - 4. npm run dev 运行项目
配置 package.json 文件 ,使项目运行后自动再浏览器中打开。
在 dev 运行命令后添加一个
--open
即可。
"scripts": {
"dev": "vite --open",
"build": "vue-tsc && vite build",
"preview": "vite preview"
},
eslint 相关配置
安装依赖
pnpm i eslint -D
生成配置文件:.eslint.cjs
npx eslint --init
安装 eslint 相关依赖指令
pnpm install -D eslint-plugin-import eslint-plugin-vue eslint-plugin-node eslint-plugin-prettier eslint-config-prettier eslint-plugin-node @babel/eslint-parser
1.2 修改.eslintrc.cjs 配置文件
// @see https://eslint.bootcss.com/docs/rules/
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
jest: true,
},
/* 指定如何解析语法 */
parser: 'vue-eslint-parser',
/** 优先级低于 parse 的语法解析配置 */
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
parser: '@typescript-eslint/parser',
jsxPragma: 'React',
ecmaFeatures: {
jsx: true,
},
},
/* 继承已有的规则 */
extends: [
'eslint:recommended',
'plugin:vue/vue3-essential',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
plugins: ['vue', '@typescript-eslint'],
/*
* "off" 或 0 ==> 关闭规则
* "warn" 或 1 ==> 打开的规则作为警告(不影响代码执行)
* "error" 或 2 ==> 规则作为一个错误(代码不能执行,界面报错)
*/
rules: {
// eslint(https://eslint.bootcss.com/docs/rules/)
'no-var': 'error', // 要求使用 let 或 const 而不是 var
'no-multiple-empty-lines': ['warn', { max: 1 }], // 不允许多个空行
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-unexpected-multiline': 'error', // 禁止空余的多行
'no-useless-escape': 'off', // 禁止不必要的转义字符
// typeScript (https://typescript-eslint.io/rules)
'@typescript-eslint/no-unused-vars': 'error', // 禁止定义未使用的变量
'@typescript-eslint/prefer-ts-expect-error': 'error', // 禁止使用 @ts-ignore
'@typescript-eslint/no-explicit-any': 'off', // 禁止使用 any 类型
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-namespace': 'off', // 禁止使用自定义 TypeScript 模块和命名空间。
'@typescript-eslint/semi': 'off',
// eslint-plugin-vue (https://eslint.vuejs.org/rules/)
'vue/multi-word-component-names': 'off', // 要求组件名称始终为 “-” 链接的单词
'vue/script-setup-uses-vars': 'error', // 防止<script setup>使用的变量<template>被标记为未使用
'vue/no-mutating-props': 'off', // 不允许组件 prop的改变
'vue/attribute-hyphenation': 'off', // 对模板中的自定义组件强制执行属性命名样式
},
}
1.3.eslintignore 忽略文件
dist
node_modules
1.4 运行脚本
package.json 新增两个运行脚本
"scripts": {
"lint": "eslint src",
"fix": "eslint src --fix",
}
二、配置prettier
有了 eslint,为什么还要有 prettier?eslint 针对的是 javascript,他是一个检测工具,包含 js 语法以及少部分格式问题,在 eslint 看来,语法对了就能保证代码正常运行,格式问题属于其次;
而 prettier 属于格式化工具,它看不惯格式不统一,所以它就把 eslint 没干好的事接着干,另外,prettier 支持
包含 js 在内的多种语言。
总结起来,eslint 和 prettier 这俩兄弟一个保证 js 代码质量,一个保证代码美观。
2.1 安装依赖包
pnpm install -D eslint-plugin-prettier prettier eslint-config-prettier
2.2.prettierrc.json 添加规则
{
"singleQuote": true,
"semi": false,
"bracketSpacing": true,
"htmlWhitespaceSensitivity": "ignore",
"endOfLine": "auto",
"trailingComma": "all",
"tabWidth": 2
}
2.3.prettierignore 忽略文件
/dist/*
/html/*
.local
/node_modules/**
**/*.svg
**/*.sh
/public/*
通过 pnpm run lint 去检测语法,如果出现不规范格式,通过 pnpm run fix 修改
三、强制使用 pnpm 包管理器工具
团队开发项目的时候,需要统一包管理器工具,因为不同包管理器工具下载同一个依赖,可能版本不一样,
导致项目出现 bug 问题,因此包管理器工具需要统一管理!!!
在根目录创建scritps/preinstall.js
文件,添加下面的内容
if (!/pnpm/.test(process.env.npm_execpath || '')) {
console.warn(
`\u001b[33mThis repository must using pnpm as the package manager ` +
` for scripts to work properly.\u001b[39m\n`,
)
process.exit(1)
}
配置命令
"scripts": {
"preinstall": "node ./scripts/preinstall.js"
}
当我们使用 npm 或者 yarn 来安装包的时候,就会报错了。原理就是在 install 的时候会触发 preinstall(npm 提供的生命周期钩子)这个文件里面的代码。
vue3 项目中配置环境变量
项目根目录分别添加 开发、生产和测试环境的文件!
.env.development
.env.production
.env.test
文件内容
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
VITE_APP_TITLE = '硅谷甄选运营平台'
VITE_APP_BASE_API = '/dev-api'
NODE_ENV = 'production'
VITE_APP_TITLE = '硅谷甄选运营平台'
VITE_APP_BASE_API = '/prod-api'
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'test'
VITE_APP_TITLE = '硅谷甄选运营平台'
VITE_APP_BASE_API = '/test-api'
配置运行命令:package.json
"scripts": {
"dev": "vite --open",
"dev:test": "vue-tsc && vite dev --mode test",
"dev:pro": "vue-tsc && vite dev --mode production",
"build:test": "vue-tsc && vite build --mode test",
"build:pro": "vue-tsc && vite build --mode production",
"preview": "vite preview"
},
通过import.meta.env获取环境变量
svg 图标 组件注册并封装
在开发项目的时候经常会用到svg矢量图,而且我们使用SVG以后,页面上加载的不再是图片资源,
这对页面性能来说是个很大的提升,而且我们SVG文件比img要小的很多,放在项目中几乎不占用资源。
安装SVG依赖插件
pnpm install vite-plugin-svg-icons -D
在vite.config.ts
中配置插件
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import path from 'path'
export default () => {
return {
plugins: [
createSvgIconsPlugin({
// Specify the icon folder to be cached
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],//表示将所有的 svg 图标都放在 assets/icons 文件夹下面。
// Specify symbolId format
symbolId: 'icon-[dir]-[name]',
}),
],
}
}
统一管理 svg 图标
在 src/assets 文件夹下 新建一个 icons文件夹。(必须创建同名文件夹,因为上面的配置入口是src/assets/icons)
在文件夹下面新建 svg 文件,svg 文件名 就是 xlink-href 的属性值。( xlink-href=’ #icon - svg文件名 ')
将 icon-font 网站上 复制的 svg 代码 复制到上面新建的 svg 文件中。
入口文件导入
import 'virtual:svg-icons-register'
svg 图标使用
注意: xlink-href 的属性值一定要用 #icon-图标名字 的格式
fill 的属性值可以设置为 #d4237a
<!-- svg 图标外层容器节点,内部需要与 use标签结合使用 -->
<svg style="width: 200px; height: 200px">
<!-- use标签的 xlink:href 属性表示 svg 用哪一个样式的图标,属性值格式 #icon-图标名字 (必须用这种格式) -->
<!-- use 标签的 fill 属性可是设置 svg 图标的颜色。 fill 的属性值可以设置为 #d4237a -->
<use xlink:href="#icon-phone" fill="pink"></use>
</svg>
icon-font svg图标下载注意事项
问题:从 icon-font 网站上下载的 svg 图标没办法使用 fill 属性设置 颜色
解决办法:将 icon-font 网站上复制的 svg 代码中 所有的 fill 属性全部删除
将下面代码中所有的 fill属性和属性值 全部删除,然后再给 use 标签设置fill 属性 ,就可以生效了
<svg t="1685364513363" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1894" width="200" height="200"><path d="M931.2 35.04H97.76a80 80 0 0 0-80 80v557.76a80 80 0 0 0 80 80h315.52l-56.96 225.76h-72a16 16 0 1 0 0 32h460.16a16 16 0 0 0 0-32H672l-56.32-225.76H931.2a80 80 0 0 0 80-80V115.04a80 80 0 0 0-80-80z m-293.28 944H391.04L448 753.6h133.44zM931.2 720.8H97.76a48 48 0 0 1-48-45.6h929.12a48 48 0 0 1-47.68 45.6z m48-77.6H49.76v-528a48 48 0 0 1 48-48H931.2a48 48 0 0 1 48 48z" fill="#3FA9F5" p-id="1895"></path><path d="M813.76 272v-0.8a21.12 21.12 0 0 0-2.56-4 4.32 4.32 0 0 1-1.28-1.12 24.48 24.48 0 0 0-3.36-2.24h-1.6a16 16 0 0 0-5.44-1.12h-121.6a16 16 0 0 0 0 32h81.76L532.64 515.36l-198.56-97.12a16 16 0 0 0-18.88 3.84l-134.4 150.88a16 16 0 0 0 12 26.56 16 16 0 0 0 11.84-5.28l126.4-141.92 197.76 96.8a16 16 0 0 0 18.08-2.88l236.16-230.56V400a16 16 0 0 0 32 0v-122.24a16 16 0 0 0-1.28-5.76zM436.48 276.8a12.48 12.48 0 1 0 0 24.96h32v15.04a12.48 12.48 0 0 0 12.48 12.48 12.32 12.32 0 0 0 12.32-12.48v-15.04h32a12.48 12.48 0 0 0 0-24.96h-32v-14.4h32a12.48 12.48 0 0 0 12.48-12.48 12.32 12.32 0 0 0-12.48-12.32h-25.6l19.84-20a12.48 12.48 0 1 0-17.6-17.6l-20.32 21.76-21.76-21.76a12.48 12.48 0 1 0-17.6 17.6l20 20h-25.76a12.32 12.32 0 0 0-12.48 12.32 12.48 12.48 0 0 0 12.48 12.48h32v14.4z" fill="#3FA9F5" p-id="1896"></path><path d="M482.56 384.96a126.4 126.4 0 1 0-126.4-126.4 126.4 126.4 0 0 0 126.4 126.4z m0-220.8a94.4 94.4 0 1 1-94.4 94.4 94.56 94.56 0 0 1 94.4-94.4z" fill="#3FA9F5" p-id="1897"></path></svg>
svg 封装为全局组件
因为项目很多模块需要使用图标,因此把它封装为全局组件!!!
在src/components目录下创建一个SvgIcon组件:代表如下
<template>
<div>
<svg :style="{ width: width, height: height }">
<use :xlink:href="prefix + name" :fill="color"></use>
</svg>
</div>
</template>
<script setup lang="ts">
defineProps({
//xlink:href属性值的前缀
prefix: {
type: String,
default: '#icon-'
},
//svg矢量图的名字
name: String,
//svg图标的颜色
color: {
type: String,
default: ""
},
//svg宽度
width: {
type: String,
default: '16px'
},
//svg高度
height: {
type: String,
default: '16px'
}
})
</script>
<style scoped></style>
在src文件夹目录下创建一个index.ts文件:用于注册components文件夹内部全部全局组件!!!
import SvgIcon from './SvgIcon/index.vue';
import type { App, Component } from 'vue';
const components: { [name: string]: Component } = { SvgIcon };
export default {
install(app: App) {
Object.keys(components).forEach((key: string) => {
app.component(key, components[key]);
})
}
}
在入口文件引入src/index.ts文件,通过app.use方法安装自定义插件
import gloablComponent from './components/index';
app.use(gloablComponent);
vue3 批量全局注册 公共组件。
- 在 components 文件夹下面新建一个 index.js 文件,用于批量全局注册公共组件。
注意: components 文件下的 index.js 文件向外暴露的对象中,务必要有一个
install(){}
的函数。
且这个install()
方法会有一个app
入参,然后全局注册公共组件的时候直接app.component()
即可
//引入项目中全部的全局组件
import SvgIcon from './SvgIcons/index.vue'
import Pagination from './Pagination/index.vue'
// import Category from './Category/index.vue'
//引入element-plus提供全部图标组件
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
//全局对象
const allGloablComponent: any = { SvgIcon, Pagination }
//对外暴露插件对象
export default {
//务必叫做install方法
install(app: any) {
//注册项目全部的全局组件
Object.keys(allGloablComponent).forEach((key) => {
//注册为全局组件
app.component(key, allGloablComponent[key])
})
//将element-plus提供图标注册为全局组件
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
},
}
- 在入口文件引入 src/index.js 文件,通过 app.use 方法安装自定义插件
import gloablComponent from './components/index'
app.use(gloablComponent)
scss 配置相关
- 首先在 src 目录下新建一个 styles 文件夹,并新建一个
index.scss
文件。用作设置全局样式- 在 style 文件夹下 新建一个 reset.scss 文件用于清除浏览器自带的默认样式。
- 配置好 reset.scss 文件之后,需要
在 index.scss 文件
中@import 'reset.scss'
引入- 在 style 文件夹下 新建一个 variable.scss 文件用于设置
scss 的全局变量
注意: 在
variable.scss
文件中配置 css 全局变量时,需要通过$
符号定义
在入口文件中引入 index.scss 文件
import '@/styles/index.scss'
配置全局变量需要 在 vite.config.js 文件中 配置相关属性,代码如下
export default defineConfig((config) => {
// 只要 下面这些代码即可。
css: {
preprocessorOptions: {
scss: {
javascriptEnabled: true,
additionalData: '@import "./src/styles/variable.scss";',
},
},
},
}
}
Mock 数据
- 安装相关依赖 pnpm install -D vite-plugin-mock mockjs
- 在 vite.config.ts 文件中 配置相关规则
import { UserConfigExport, ConfigEnv } from 'vite'
import { viteMockServe } from 'vite-plugin-mock'
import vue from '@vitejs/plugin-vue'
export default ({ command }) => {
return {
plugins: [
vue(),
viteMockServe({
localEnabled: command === 'serve',
}),
],
}
}
- 在根目录创建 mock 文件夹:去创建我们需要 mock 数据与接口!!!
//用户信息数据
function createUserList() {
return [
{
userId: 1,
avatar:
'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
username: 'admin',
password: '111111',
desc: '平台管理员',
roles: ['平台管理员'],
buttons: ['cuser.detail'],
routes: ['home'],
token: 'Admin Token',
},
{
userId: 2,
avatar:
'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
username: 'system',
password: '111111',
desc: '系统管理员',
roles: ['系统管理员'],
buttons: ['cuser.detail', 'cuser.user'],
routes: ['home'],
token: 'System Token',
},
]
}
export default [
// 用户登录接口
{
url: '/api/user/login', //请求地址
method: 'post', //请求方式
response: ({ body }) => {
//获取请求体携带过来的用户名与密码
const { username, password } = body
//调用获取用户信息函数,用于判断是否有此用户
const checkUser = createUserList().find(
(item) => item.username === username && item.password === password,
)
//没有用户返回失败信息
if (!checkUser) {
return { code: 201, data: { message: '账号或者密码不正确' } }
}
//如果有返回成功信息
const { token } = checkUser
return { code: 200, data: { token } }
},
},
// 获取用户信息
{
url: '/api/user/info',
method: 'get',
response: (request) => {
//获取请求头携带token
const token = request.headers.token
//查看用户信息是否包含有次token用户
const checkUser = createUserList().find((item) => item.token === token)
//没有返回失败的信息
if (!checkUser) {
return { code: 201, data: { message: '获取用户信息失败' } }
}
//如果有返回成功信息
return { code: 200, data: { checkUser } }
},
},
]
- 安装 axios 测试 mock 的接口是否可以使用.
pnpm install axios
// 引入 axios 测试 mock 是否引入成功
import axios from 'axios'
axios({
method: 'post',
url: '/api/user/login',
data: { username: 'admin', password: '111111' },
}).then((res) => {
console.log(res, 'mock假数据')
})
axios 二次封装
- 新建一个 utils 文件夹,并添加一个 request.js 文件,用于统一配置相关 接口规则.( 如
请求拦截
,相应拦截
,基础路径
,超时时间
等等.)
//进行axios二次封装:使用请求与响应拦截器
import axios from 'axios'
import { ElMessage } from 'element-plus'
//引入用户相关的仓库
import useUserStore from '@/store/modules/user'
//第一步:利用axios对象的create方法,去创建axios实例(其他的配置:基础路径、超时的时间)
const request = axios.create({
//基础路径
baseURL: import.meta.env.VITE_APP_BASE_API, //基础路径上会携带/api
timeout: 5000, //超时的时间的设置
})
//第二步:request实例添加请求与响应拦截器
request.interceptors.request.use((config) => {
//获取用户相关的小仓库:获取仓库内部token,登录成功以后携带给服务器
const userStore = useUserStore()
if (userStore.token) {
config.headers.token = userStore.token
}
//config配置对象,headers属性请求头,经常给服务器端携带公共参数
//返回配置对象
return config
})
//第三步:响应拦截器
request.interceptors.response.use(
(response) => {
//成功回调
//简化数据
return response.data
},
(error) => {
//失败回调:处理http网络错误的
//定义一个变量:存储网络错误信息
let message = ''
//http状态码
const status = error.response.status
switch (status) {
case 401:
message = 'TOKEN过期'
break
case 403:
message = '无权访问'
break
case 404:
message = '请求地址错误'
break
case 500:
message = '服务器出现问题'
break
default:
message = '网络出现问题'
break
}
//提示错误信息
ElMessage({
type: 'error',
message,
})
return Promise.reject(error)
},
)
//对外暴露
export default request
路由搭建
- npm i vue-router 安装路由依赖
- 新建一个 router 文件夹,并新增 index.js 文件用于统一配置相关路由
- 在
main.js
文件中引入并注册
index.js 文件
//通过vue-router插件实现模板路由配置
import { createRouter, createWebHashHistory } from 'vue-router';
import { constantRoute } from './routes';
//创建路由器
let router = createRouter({
//路由模式hash
history: createWebHashHistory(),
routes: constantRoute,
//滚动行为
scrollBehavior() {
return {
left: 0,
top: 0
}
}
});
export default router;
routes.js 文件
//对外暴露配置路由(常量路由)
export const constantRoute = [
{
//登录
path: '/login',
component: () => import('@/views/login/index.vue'),
name: 'login',
meta: {
title: '登录', //菜单标题
hidden: true, //代表路由标题在菜单中是否隐藏 true:隐藏 false:不隐藏
icon: 'Promotion', //菜单文字左侧的图标,支持element-plus全部图标
},
},
{
//登录成功以后展示数据的路由
path: '/',
component: () => import('@/layout/index.vue'),
name: 'layout',
meta: {
title: '',
hidden: false,
icon: '',
},
redirect: '/home',
children: [
{
path: '/home',
component: () => import('@/views/home/index.vue'),
meta: {
title: '首页',
hidden: false,
icon: 'HomeFilled',
},
},
],
},
{
//404
path: '/404',
component: () => import('@/views/404/index.vue'),
name: '404',
meta: {
title: '404',
hidden: true,
icon: 'DocumentDelete',
},
},
{
//任意路由
path: '/:pathMatch(.*)*',
redirect: '/404',
name: 'Any',
meta: {
title: '任意路由',
hidden: true,
icon: 'DataLine',
},
},
]
main.js 文件
//引入路由
import router from './router'
//注册模板路由
app.use(router)
Vue 项目路由动态加载
需要注意问题。
出现问题:当项目
访问异步路由
的时候,浏览器可能会出现白屏
的现象。
原因:当访问异步路由的时候,项目可能出现路由加载没完成的情况。也就是
还没有这个路由的时候,就去访问,就会出现白屏的现象
解决办法:使用 next() 放行的时候,等路由加载完成之后再放行。
//万一:刷新的时候是异步路由,有可能获取到用户信息、异步路由还没有加载完毕,出现空白的效果,解决办法如下。
next({ ...to })
Vue 按钮权限相关方法
注册全局指令,然后根据后端返回的按钮权限的数组,使用 v-if 对按钮进行控制。
全局指令代码:
import pinia from '@/store'
import useUserStore from '@/store/modules/user'
const userStore = useUserStore(pinia)
export const isHasButton = (app: any) => {
//获取对应的用户仓库
//全局自定义指令:实现按钮的权限
app.directive('has', {
//代表使用这个全局自定义指令的DOM|组件挂载完毕的时候会执行一次
mounted(el: any, options: any) {
//自定义指令右侧的数值:如果在用户信息buttons数组当中没有
//从DOM树上干掉
if (!userStore.buttons.includes(options.value)) {
el.parentNode.removeChild(el)
}
},
})
}
// main.js 文件
//引入自定义指令文件
import { isHasButton } from '@/directive/has'
isHasButton(app)
// 使用:
<el-button @click="addTrademark" v-has="`btn.Trademark.add`">添加品牌</el-button>