步骤:
一、 yarn add @types/node
二、vite.config.ts 中 和 plugins 平齐 配置
resolve: {
alias:{
"@": path.resolve(__dirname,'./src')
}
}
注意 path 需要引入 import path from "path"
只需要在项目中下载sass scss即可: yarn add sass scss
@charset "utf-8";
/*全局样式*/
body,ol,ul,h1,h2,h3,h4,h5,h6,p,th,td,dl,dd,form,fieldset,legend,figure,input,textarea,select,div{margin:0;padding:0;box-sizing: border-box;}
html{font-size: 10px; height: 100%; overflow: hidden}
body{word-wrap:break-word; height: 100%; overflow: hidden;font-size: 14px;color: #333}
fieldset,a img,.bor0{border:0;}
li{list-style:none;}
/*a,u,s,del{color:#666;text-decoration:none;} !*文本没有修饰*!*/
/*i,em,b{font-style:normal;font-weight:100;}!*文本不倾斜不加粗*!*/
a img{border:0;}
img{vertical-align:middle;}
table{border-collapse:collapse;}
input,textarea{outline:none;}/*去除谷歌点击蓝色虚框*/
textarea{resize:none;}/*禁止多行文本输入框的拖动*/
#app{height: 100%; overflow: hidden;}
.ind2{text-indent:2em;}
.l{float:left;}
.r{float:right;}
.rel{position:relative;}
.abs{position:absolute;}
.over{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}/*溢出出现省略号*/
.vcenter:after{content:"";display:inline-block;vertical-align:middle;height:100%;width:0;}
.clear{zoom:1;}
.clear:after{content:"";display:block;clear:both;}/*清除浮动*/
.cu{cursor:pointer;}/*鼠标指针为手型*/
.el-textarea__inner{
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
}
::-webkit-scrollbar {
width: 6px;
height: 6px;
background: transparent;
}
::-webkit-scrollbar-thumb {
background: transparent;
border-radius: 4px;
}
:hover::-webkit-scrollbar-thumb {
background: hsla(0, 0%, 53%, 0.4);
}
:hover::-webkit-scrollbar-track {
background: hsla(0, 0%, 53%, 0.1);
}
在vite-env.d.ts 声明文件中配置如下:
declare module "*.vue" {
import { DefineComponent } from "vue"
const component: DefineComponent<{}, {}, any>
export default component
}
步骤:
一、yarn add element-plus unplugin-auto-import unplugin-vue-components vite-plugin-style-import consola
二、在vite.config.ts 中( 在plugins数组内,和 vue() 平级 ):
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// 自动导入element-plus的样式
//createStyleImportPlugin({
//resolves: [ElementPlusResolve()],
//libs: [
//{
//libraryName: 'element-plus',
//esModule: true,
//resolveStyle: (name: string) => {
//return `element-plus/theme-chalk/${name}.css`
//}
//}
//]
//}),
Components({//自定义的模块
dirs: ['src/components'],
extensions: ['vue', 'ts'],
resolvers: [ElementPlusResolver()]
}),
AutoImport({ // 插件进行自动导入相关的依赖库
//安装两行后你会发现在组件中不用再导入ref,reactive等
imports: ['vue', 'vue-router'],
// 可选,用于自动导入组件类型
dts: 'src/components.d.ts'
})
在vite.config.ts中,与 plugins 平齐:
server: {
port: 8080,//自定义端口
// 是否开启 https
https: false,
open: true,
proxy: {
'/api': {
// 后台地址
target: 'http://127.0.0.1:8300/',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
},
'/api2': {
// 后台地址
target: 'http://127.0.0.1:8956/',
changeOrigin: true,
rewrite: path => path.replace(/^\/api2/, '')
}
}
}
父组件传值给子组件(方式一):
父组件:
<Child :msg="msg"></Child>
子组件:
defineProps<{
msg: string
}>()
父组件传值给子组件(方式二 -- 默认值):
父组件:
<Child :msg="msg"></Child>
子组件(使用 withDefaults ,两个参数,第一个参数和上面的是一样的, 第二个参数是默认值):
withDefaults( defineProps<{
msg: string
}>(), {
msg: "默认值"
})
子传父:
父组件:
<Common @giveFatherStr="handleData"></Common>
const handleData = (val: string) => {
console.log(val);
}
子组件:
const emit = defineEmits(['giveFatherStr'])
const giveFatherData = () => {
emit("giveFatherStr", "传给父组件的值")
}
子传父(方式二):
父组件是一样的
子组件:
const emit = defineEmits<{
(event: "giveFatherStr", flag: string): void
}>()
<button @click="emit('giveFatherStr', '传过去的值')">传值</button>
步骤(不持久化):
一、 yarn add pinia
二、main.ts中: import { createPinia } from "pinia"
const pinia = createPinia()
app.use(pinia)
三、创建store目录,里面创建useUserStore.ts文件:
import { defineStore } from "pinia";
export const useUserStore = defineStore("user", () => {
// 定义 userName 状态
let userName = ref("")
// 设置 userName 状态
const setUserName = (payload: string) => {
userName.value = payload
}
return {
userName, setUserName
}
})
步骤(持久化):
一、yarn add pinia-plugin-persistedstate
二、main.ts中: import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import { createPinia } from "pinia"
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
app.use(pinia)
三、配置持久化: defineStore方法的第三个参数加上: { persist: true }
配置完成之后就是在组件中使用了:
import { useUserStore } from "@/store/useUserPinia.ts"
const store = useUserStore()
import { storeToRefs } from "pinia" // 结构出来的数据仍然有响应式
const { userName, setUserName } from storeToRefs(store)
// 取值
console.log(userName)
// 存值
setUserName('需要存进去的值')
注意:引入ts文件的时候会报ts文件找不到,需要配置tsconfig.json文件, 同时在store中尽量使用ref,不用reactive
在 compilerOptions 对象里面配置如下:
// 修改引入ts文件报错
"baseUrl": "./",
"paths": {
"@/*": [
"src/*"
]
}
步骤:
一、yarn add vue-router
二、创建router目录,里面创建index.ts文件
import { createRouter, createWebHashHistory, createWebHistory } from "vue-router"
const routes = [
{
path: "/",
name: 'Laout',
component: () => import("@/pages/Layout.vue")
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
三、在main.ts文件中:
import router from "@/router/index.ts"
app.use(router)
步骤:
一、组件的展示区需要用keep-alive包裹
<router-view class="view" v-slot="{ Component }">
<transition name="fade" mode="out-in" appear>
<keep-alive :include="cacheList">
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
css中(实现动画):
.fade-enter-from, .fade-leave-to{
opacity: 0;
transform: translateX(40px);
}
.fade-enter-avtive, .fade-leave-active{
transition: all .3s;
}
cacheList 存在pinia中
二、在列表中(由于 beforeRouteEnter 在setup语法糖中不能直接使用,需要在写一个script标签):
<!-- 缓存用的 -->
<script lang="ts">
import { cachList } from "@/eNum/index.ts"
import { useIndexStore } from "@/store/useIndexStore.ts"
const store = useIndexStore()
const toList = cachList // cachList里面存的是 ['UserDetail', 'RoleDetail', 'ProjectDetail'], 就是详情页面在路由表中配置的名称
export default defineComponent({
beforeRouteEnter(to: any, _: any, next: any) {
next(() => {
store.setCacheList([to.name])
})
},
beforeRouteLeave(to: any, from: any, next: any) {
if(toList.includes(to.name)) {
store.setCacheList([from.name])
}else{
store.setCacheList([])
}
next()
}
})
</script>
三、详情页面
<!-- 缓存用的 -->
<script lang="ts">
import { useIndexStore } from '@/store/useIndexStore.ts';
const store = useIndexStore()
export default defineComponent({
beforeRouteLeave(to: any, _: any, next: any) {
const { isCache } = to.query
let cache = isCache ? JSON.parse(isCache) : false
if(cache) {
store.setCacheList([to.name])
}else{
store.setCacheList([])
}
next()
}
})
</script>
步骤:
一、新建httpInstance.ts
import axios from "axios";
import Router from "@/router/index.ts";
import { useCachStore } from "@/store/useCachStore.ts";
import { ElMessage, ElLoading } from "element-plus"
import { storeToRefs } from "pinia";
const store = useCachStore()
const { token } = storeToRefs(store)
let instance = axios.create({
withCredentials: true, //token凭证
timeout: 1000 * 60 * 1
});
// 请求动画
let loading: any;
// 内存中正在请求的数量
let loadingNum = 0;
let text = "拼命加载中...";
function startLoading() {
if (loadingNum == 0) {
loading = ElLoading.service({
lock: true,
text: text,
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.7)"
});
}
loadingNum++;
}
function endLoading() {
// 关闭加载动画
loadingNum > 0 && loadingNum--;
if (loadingNum <= 0) {
loading && loading.close();
}
}
function checkWhetherString(blackList: string[], url: string) {
for (let i = 0; i < blackList.length; i++) {
var item = blackList[i];
if (url.indexOf(item) != -1) {
return true;
}
}
return false;
}
// 请求拦截
instance.interceptors.request.use(
function (config) {
config.headers.Accept = "application/json, text/plain, */*";
// 黑名单,写在此处的都不进loading加载动画
const blackList = ["/gd/year/v1/in/progress", "im/index/newMsg", "/receiveMessage/v1/getMsgNumber"];
const { url } = config;
if (!checkWhetherString(blackList, url as string)) {
startLoading();
}
// 请求添加token
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
function (err) {
return Promise.reject(err);
}
);
// 响应拦截
instance.interceptors.response.use(
(response) => {
endLoading();
// token过期
if (response.data.code != 0) {
// Router.replace("/login");
}
return response.data;
},
(err) => {
endLoading();
// console.log(err)
ElMessage.error({
message: err?.response?.data?.message
});
if (err.response.status == 401 || err.response.status == 403) {
// sessionStorage.clear();
// // removeToken()
// Router.replace("/login");
// window.location.reload();
}
return Promise.reject(err.message);
}
);
type Http = {
get: any,
post: any,
put: any,
delete: any
}
const http: Http = {
get: function (url: string, options: any) {
return new Promise((resolve, reject) => {
instance.get(url, options).then((res: any) => {
if (res.code == 20000) {
resolve(res.data);
} else {
ElMessage.error({
message: res.message
});
reject(res.message);
}
})
.catch((err) => {
reject(err);
});
});
},
post: function (url: string, data: any, options: any) {
return new Promise((resolve, reject) => {
instance
.post(url, data, options)
.then((res: any) => {
if (res.code == 20000) {
resolve(res.data);
} else {
ElMessage.error({
message: res.message
});
reject(res.message);
}
})
.catch((err) => {
console.log(err);
});
});
},
put: function (url: string, data: any, options: any) {
return new Promise((resolve, reject) => {
instance
.put(url, data, options)
.then((res: any) => {
if (res.code == 20000) {
resolve(res.data);
} else {
ElMessage.error({
message: res.message
});
reject(res.message);
}
})
.catch((err) => {
console.log(err);
});
});
},
delete: function (url: string, options: any) {
return new Promise((resolve, reject) => {
instance
.delete(url, options)
.then((res: any) => {
if (res.code == 20000) {
resolve(res.data);
} else {
ElMessage.error({
message: res.message
});
reject(res.message);
}
})
.catch((err) => {
console.log(err);
});
});
}
};
export default http;
二、新建api/index.ts文件
import { UserParam, UserListParams } from "@/type/api";
import axios from "@/utils/httpInstance/index.ts"
// 用户列表
export const userList = (params: UserParam /* 参数类型 */): Promise<UserListParams /* 响应的数据类型 */> => {
return axios.get(`/api/user/search`, params);
};
1、下载包
pnpm install eslint eslint-plugin-vue eslint-config-prettier prettier eslint-plugin-import eslint-plugin-prettier eslint-config-airbnb-base -D
pnpm install typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-import-resolver-alias @types/eslint @types/node -D
2、配置文件
脚本 增加 “lint:create”: “eslint --init”
执行 pnpm lint:create
开始选择
选择需要的配置点击确认就会生成一个 .eslintrc.cjs
文件
//.eslintrc.cjs
module.exports = {
// 环境:
env: {
// 浏览器
browser: true,
// 最新es语法
es2021: true,
// node环境
node: true,
},
// 扩展的eslint规范语法,可以被继承的规则
// 字符串数组:每个配置继承它前面的配置
// 分别是:
// eslint-plugin-vue提供的
// eslint-config-airbnb-base提供的
// eslint-config-prettier提供的
// 前缀 eslint-config-, 可省略
extends: ['plugin:vue/vue3-strongly-recommended', 'airbnb-base', 'prettier'],
// eslint 会对我们的代码进行检验
// parser的作用是将我们写的代码转换为ESTree(AST)
// ESLint会对ESTree进行校验
parser: 'vue-eslint-parser',
// 解析器的配置项
parserOptions: {
// es的版本号,或者年份都可以
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
// 源码类型 默认是script,es模块用module
sourceType: 'module',
// 额外的语言类型
ecmaFeatures: {
tsx: true,
jsx: true,
},
},
// 全局自定义的宏,这样在源文件中使用全局变量就不会报错或者警告
globals: {
defineProps: 'readonly',
defineEmits: 'readonly',
defineExpose: 'readonly',
withDefault: 'readonly',
},
// 插件
// 前缀 eslint-plugin-, 可省略
// vue官方提供了一个ESLint插件 eslint-plugin-vue,它提供了parser和rules
// parser为 vue-eslint-parser,放在上面的parsr字段,rules放在extends字段里,选择合适的规则
plugins: ['vue', '@typescript-eslint'],
settings: {
// 设置项目内的别名
'import/reslover': {
alias: {
map: [['@', './src']],
},
},
// 允许的扩展名
'import/extensions': ['.js', '.jsx', '.ts', '.tsx', '.mjs'],
},
// 自定义规则,覆盖上面extends继承的第三方库的规则,根据组内成员灵活定义
rules: {
'no-console': 0,
'vue/valid-template-root': 0,
'import/no-extraneous-dependencies': 0,
'no-param-reassing': 0,
'vue/multi-word-commponent-names': 0,
'vue/attribute-hyphenation': 0,
'vue/v-on-event-hyphenation': 0,
},
};
3、安装vite的插件配合eslint
pnpm i -D vite-plugin-eslint
4、配置
// vite.config.ts
import eslintPlugin from "vite-plugin-eslint"
plugins: [vue(), eslintPlugin()]
5、整合 prettier
// .prettierrc.cjs
module.exports = {
// 一行最多多少字符
printWidth: 80,
// 使用2个空格缩进
tabWidth: 2,
// 使用tab缩进,不使用空格
useTabs: true,
// 行尾需要分号
semi: false,
// 使用单引号
singleQuote: true,
// 对象的key仅在必要时使用引号
quoteProps: "as-needed",
// jsx不使用单引号,而使用双引号
jsxSingleQuote: false,
// 尾随逗号
trailingComma: "es5",
// 大括号内的收尾需要空格
bracketSpacing: true,
// 箭头函数,只有一个参数的时候,也需要括号
arrowParens: "always",
// 每个文件格式化的范围是文件的全部内容
rangeStart: 0,
rangeEnd: Infinity,
// 不需要写文件开头的@prettier
requirePragma: false,
// 不需要自动在文件开头插入@prettier
insertPragma: false,
// 使用默认的折行标准
proseWrap: "always",
// 根据显示样式决定html要不要折行
htmlWhitespaceSensitivity: "css",
// 换行符使用lf
endOfLine: "lf",
}
// .prettierignore
/dist/*
.local
.husky
.history
.output.js
/node_modules/**
src/.DS_Store
**/*.svg
**/*.sh
/public/*
components.d.ts
// .eslintrcignore
*.sh
node_modules
*.md
*.woff
*.ttf
.vscode
.idea
dist
/public
/docs
.husky
.history
/bin
.eslintrc.js
prettier.config.js
/src/mock/*
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
.DS_Store
dist-ssr
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode
!.vscode/extensions.json
.idea
*.suo
*.njsproj
*.sln
*.sw?
components.d.ts
6、vscode 全局配置文件 settings.json(也可以在各自的项目中配置)
"[vue]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
// 在保存时格式化文件
"editor.formatOnSave": true,
// 保存时候按照 eslint 格式化代码
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.fixAll.eslint": true,
"source.fixAll.stylelint": true
},
7、使用命令将项目中的所有的代码一键格式化
添加脚本
“prettier-format”: “prettier --config .prettierrc.cjs “src/**/*.{vue,js,ts}” --write”
执行
pnpm prettier-format
以上就实现了代码格式化的校验工作,要是各自的项目都有自己的配置
在 .vscode 目录创建 settings.json
文件
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.fixAll.eslint": true,
"source.fixAll.stylelint": true
},
"[vue]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[ts]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[less]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[scss]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"typescript.tsdk": "node_modules/typescript/lib"
}
1、安装 包
pnpm i husky lint-staged -D
2、创建 husky
//增加脚本
"prepare": "husky install"
// 执行脚本
pnpm prepare
3、执行命令
npx husky add .husky/pre-commit "npx lint-staged"
4、package.json 中
"dependencies": {},
"lint-staged": {
"*.{js,jsx,vue,ts,tsx}": [
"npm run lint",
"npm run prettier-format"
]
}
到此,提交代码不遵守 eslint 规范就不允许提交
下面是提交日志的校验
1、安装包
pnpm install @commitlint/config-conventional @commitlint/cli -D
2、添加
npx husky add .husky/commit-msg "npx --no -- commitlint --edit ${1}"
3、创建配置文件
// commitlint.config.cjs
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
// 编译相关的修改,例如发布版本,对项目构建或者依赖的改动
'build',
// 新功能(feature)
'feat',
// 修复bug
'fix',
// 更新某功能
'update',
// 重构
'refactor',
// 文档
'docs',
// 构建过程或者辅助工具的变动,如增加依赖库等
'chore',
// 不影响代码运行的变动
'style',
// 撤销commit,回滚到上一个版本
'revert',
// 性能优化
'perf',
// 测试(单元,集成测试)
'test',
],
],
'type-case': [0],
'type-empty': [0],
'scope-empty': [0],
'scope-case': [0],
'subject-full-stop': [0, 'never'],
'subject-case': [0, 'never'],
'header-max-length': [0, 'always', 74],
},
};
源代码地址