本文主要是介绍《硅谷甄选》项目笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
该项目技术选型
- Vue3 + 组合式API
- Vite 构建工具
- TypeScript
- vue-router
- Pinia状态管理
一、vue3组件通信方式
不管是vue2还是vue3, 组件通信方式很重要, 不管是项目还是面试都是经常用到的知识点
比如: vue2组件通信方式
- props:可以实现父子组件、子父组件、甚至兄弟组件通信
- 自定义事件:可以实现子父组件通信
- 全局事件总线$bus:可以实现任意组件通信
- pubsub:发布订阅模式实现任意组件通信
- vuex:集中式状态管理容器,实现任意组件通信
- ref:父组件获取子组件实例VC,获取子组件的响应式数据以及方法
- slot:插槽(默认插槽、具名插槽、作用域插槽) 实现父子组件通信........
(1)props
props:可以实现父子组件通信, props数据还是只读的!
props可以省略前面的名字
- 需要使用到defineProps方法去接受父组件传递过来的数据
- defineProps是Vue3提供方法,不需要引入直接使用
在 Vue.js 中,props 是用来从父组件向子组件传递数据的机制。当我们在子组件中使用 props 接收数据时,实际上接收到的是一个代理对象。这是因为 Vue.js 在进行组件渲染时会对 props 进行响应式处理(reactive),从而可以实现在父组件中更新 props 数据时,自动更新子组件的显示内容。
具体来说,当我们在父组件中更新 props 数据时,实际上会触发一个更新副作用(update effect),从而重新渲染子组件。此时,Vue.js 会重新创建一个代理对象,并将最新的 props 数据绑定到该代理对象上。然后,将该代理对象作为参数传递给子组件,并在子组件中使用该代理对象进行数据的读取和更新。
这种设计方式可以使得我们在子组件中使用 props 数据时,就像使用普通的响应式数据一样方便和灵活。我们可以通过直接读取代理对象属性或者使用计算属性、监听器等方式对 props 数据进行操作,而无需关心该数据的更新和同步,这些都由 Vue.js 内部自动完成。
因此Vue.js 中的 props 采用代理对象的方式进行实现,既可以实现组件间数据的有效传递,又能够保证数据的响应式特性,是 Vue.js 框架中非常重要的一个特性。
父组件给子组件传递数据
<Child info="我爱祖国" :money="money"></Child>
子组件获取父组件传递数据:方式①
let props = defineProps({info:{type:String,//接受的数据类型default:'默认参数',//接受默认数据},money:{type:Number,default:0
}})
方式②
let props = defineProps(["info",'money']);
⭕子组件获取到props数据就可以在模板中使用了,但是切记props是只读的(只能读取,不能修改)
(2)自定义事件
在vue框架中事件分为两种: 一种是原生的DOM事件,另外一种自定义事件。原生DOM事件可以让用户与网页进行交互,比如cick、dbclick、change、 mouseenter、mouseleave...自定义事件可以实现子组件给父组件传递数据。
1️⃣原生DOM事件
<pre @click="handler">我是祖国的老花骨朵</pre>
pre 是一个表示预格式化文本的标签,用于以固定宽度的字体显示文本内容,其中所有的空格和换行符都会被保留并原样显示,而不会被忽略或被合并。
与 pre 标签相对应的是另外一个 HTML5 标签:code 标签。code 标签也可以用来表示计算机程序代码等预格式化文本,但是它不会保留所有空格和换行符,只会将文本中的特殊字符进行转义,并以默认的字体显示文本内容。因此,code 标签通常用于短小的代码片段,而 pre 标签则适用于较长的代码或其他复杂的预格式化文本内容。
当前代码级给pre标签绑定原生DOM事件点击事件,默认会给事件回调注入event事件对象。当然点击事件想注入多个参数可以按照下图操作。但是切记注入的事件对象务必叫做$event。
<div @click="handler1(1,2,3,$event)">我要传递多个参数</div>
在vue3框架click、dbclick、change(这类原生DOM事件),不管是在标签、自定义标签上(组件标签)都是原生DOM事件。
2️⃣自定义事件
自定义事件可以实现子组件给父组件传递数据.在项目中是比较常用的。
比如在父组件内部给子组件(Event2)绑定一个自定义事件。
<Event2 @xxx="handler3"></Event2>
在Event2子组件内部触发这个自定义事件
<template><div><h1>我是子组件2</h1><button @click="handler">点击我触发xxx自定义事件</button></div>
</template><script setup lang="ts">
let $emit = defineEmits(["xxx"]);
const handler = () => {$emit("xxx", "法拉利", "茅台");
};
</script>
<style scoped>
</style>
我们会发现在script标签内部,使用了defineEmits方法,此方法是vue3提供的方法,不需要引入直接使用。defineEmits方法执行,传递一个数组,数组元素即为将来组件需要触发的自定义事件类型,此方执行会返回一个$emit方法用于触发自定义事件。
当点击按钮的时候,事件回调内部调用$emit方法去触发自定义事件,第一个参数为触发事件类型,第二个、三个、N个参数即为传递给父组件的数据。
需要注意的是:代码如下
<Event2 @xxx="handler3" @click="handler"></Event2>
正常说组件标签书写@click应该为原生DOM事件,但是如果子组件内部通过defineEmits定义就变为自定义事件了
let $emit = defineEmits(["xxx",'click']);
(3)全局事件总线
全局事件总线可以实现任意组件通信,在vue2中可以根据VM与VC关系推出全局事件总线但是在vue3中没有Vue构造函数,也就没有Vue.prototype.以及组合式API写法没有this,那么在Vue3想实现全局事件的总线功能就有点不现实啦,如果想在Vue3中使用全局事件总线功能可以使用插件mitt实现。
mitt 通过创建一个事件总线(event bus)来实现事件的订阅和发布。我们可以通过 on() 方法向总线订阅一个事件,并定义该事件被触发时执行的回调函数;也可以使用 emit() 方法向总线发布一个事件,并传递相应的数据参数。mitt 还支持 off() 方法用于取消事件的订阅,并支持 once() 方法用于一次性订阅某个事件。
// 导入 mitt 模块 import mitt from 'mitt'// 创建一个事件总线实例 const bus = mitt()// 向总线订阅一个事件 bus.on('foo', (data) => {console.log('Foo event triggered with data:', data) })// 向总线发布一个事件 bus.emit('foo', { bar: 'baz' }) // 输出:Foo event triggered with data: { bar: 'baz' }// 取消事件的订阅 bus.off('foo')// 一次性订阅某个事件 bus.once('bar', () => {console.log('Bar event triggered once.') })// 其他高级使用方式,请参考 mitt 的官方文档
(4)v-model
v-model指令: 收集表单数据,数据双向绑定。
v-model也可以实现组件之间的通信,实现父子组件数据同步的业务。
- 父亲给子组件数据 props
- 子组件给父组件数据 自定义事件
- 引入子组件
<div>
<!-- props:父亲给儿子数据 -->
<!-- <Child :modelValue="money” @update:modelValue="handler">/Child>-->
<!--v-mode1组件身上使用第一:相当有给子组件传递props[modelValue] = 10000第二:相当于给子组件绑定自定义事件update:modelValue
--><Child v-model="money"></Child>
</div>
v-model指令可是收集表单数据(数据双向绑定),除此之外它也可以实现父子组件数据同步。
而v-model实指利用props[modelValue]与自定义事件[update:modelValue]实现的。
下方代码:相当于给组件Child传递一个props(modelValue)与绑定一个自定义事件update:modelValue
实现父子组件数据同步
<Child v-model="msg"></Child>
在vue3中一个组件可以通过使用多个v-model,让父子组件多个数据同步,下方代码相当于给组件Child传递两个props分别是pageNo与pageSize,以及绑定两个自定义事件update:pageNo与update:pageSize实现父子数据同步。
<Child v-model:pageNo="msg" v-model:pageSize="msg1"></Child>
(5)useAttrs
vue3框架提供一个方法useAttrs方法,它可以获取组件身上的属性与事件!
useAttrs 是 Vue.js 3 中的一个新特性,用于在组件中获取所有传递给组件的非响应式属性(non-reactive props)。与 props 属性不同,useAttrs 返回的是一个对象,包含了所有未声明为 props 的属性。这些属性通常被称为“剩余属性”(rest attributes)或“未知属性”(unknown attributes),因为它们在组件内部可能不会被直接使用,而是被传递到其他子组件或用于样式类等。
在Vue3中可以利用useAttrs方法获取组件的属性与事件(包含:原生DOM事件或者自定义事件),次函数功能类似于Vue2框架中$attrs属性与$listeners方法。
比如:在父组件内部使用一个子组件my-button
<my-button type="success" size="small" title='标题' @click="handler"></my-button>
子组件内部可以通过useAttrs方法获取组件属性与事件.因此你也发现了,它类似于props,可以接受父组件传递过来的属性与属性值。需要注意如果defineProps接受了某一个属性,useAttrs方法返回的对象身上就没有相应属性与属性值。
<script setup lang="ts">import {useAttrs} from 'vue';let $attrs = useAttrs();
</script>
props与useAttrs方法都可以获取父组件传递过来的属性与属性值。
但是props接受了useAttrs方法就获取不到了。
(6)ref与$parent
ref 提及到 ref 可能会想到它可以获取元素的DOM或者获取子组件实例的VC。既然可以在父组件内部通过ref获取子组件实例VC,那么子组件内部的方法与响应式数据父组件可以使用的。
- ref:可以获取真实的DOM节点,可以获取到子组件实例VC
- $parent:可以在子组件内部获取到父组件的实例
比如:
在父组件挂载完毕获取组件实例
父组件内部代码:
<template><div><h1>ref与$parent</h1><Son ref="son"></Son></div>
</template>
<script setup lang="ts">import Son from "./Son.vue";import { onMounted, ref } from "vue";const son = ref();onMounted(() => {console.log(son.value);});
</script>
但是需要注意,如果想让父组件获取子组件的数据或者方法需要通过defineExpose对外暴露,因为vue3中组件内部的数据对外“关闭的”,外部不能访问。
<script setup lang="ts">
import { ref } from "vue";
//数据
let money = ref(1000);
//方法
const handler = ()=>{
}
defineExpose({money,handler
})
</script>
$parent 可以获取某一个组件的父组件实例VC,因此可以使用父组件内部的数据与方法。必须子组件内部拥有一个按钮点击时候获取父组件实例,当然父组件的数据与方法需要通过 defineExpose 方法对外暴露。
<button @click="handler($parent)">点击我获取父组件实例</button>
(7)provide与inject
provide[提供]
inject[注入]
vue3提供两个方法 provide 与 inject, 可以实现隔辈组件传递参数。
组件组件提供数据:
provide方法用于提供数据,此方法执需要传递两个参数, 分别提供数据的key与祖先组件提供数据value。
<script setup lang="ts">import {provide} from 'vue'provide('token','admin_token');
</script>
后代组件可以通过 inject 方法获取数据,通过 key 获取存储的数值。
<script setup lang="ts">import {inject} from 'vue'let token = inject('token');
</script>
(8)pinia
pinia官网:https://pinia.web3doc.top/**https://pinia.web3doc.top/**
pinia也是集中式管理状态容器,类似于vuex。但是核心概念没有mutation、modules,使用方式参照官网。
(9)slot
插槽:默认插槽、具名插槽、作用域插槽可以实现父子组件通信.
1️⃣默认插槽:
在子组件内部的模板中书写slot全局组件标签
<template><div><slot></slot></div>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>
在父组件内部提供结构:Todo即为子组件,在父组件内部使用的时候,在双标签内部书写结构传递给子组件
注意 :开发项目的时候默认插槽一般只有一个。
2️⃣具名插槽:
顾名思义,此插槽带有名字在组件内部留多个指定名字的插槽。
下面是一个子组件内部,模板中留两个插槽。
<template><div><h1>todo</h1><slot name="a"></slot><slot name="b"></slot></div>
</template>
<script setup lang="ts">
</script><style scoped>
</style>
父组件内部向指定的具名插槽传递结构。需要注意 v-slot:可以替换为 #
<template><div><h1>slot</h1><Todo><template v-slot:a> //可以用#a替换<div>填入组件A部分的结构</div></template><template v-slot:b>//可以用#b替换<div>填入组件B部分的结构</div></template></Todo></div>
</template><script setup lang="ts">
import Todo from "./Todo.vue";
</script>
<style scoped>
</style>
3️⃣作用域插槽
作用域插槽:可以理解为,子组件数据由父组件提供,但是子组件内部决定不了自身结构与外观(样式)
子组件Todo代码如下:
<template><div><h1>todo</h1><ul><!--组件内部遍历数组--><li v-for="(item,index) in todos" :key="item.id"><!--作用域插槽将数据回传给父组件--><slot :$row="item" :$index="index"></slot></li></ul></div>
</template>
<script setup lang="ts">
defineProps(['todos']);//接受父组件传递过来的数据
</script>
<style scoped>
</style>
父组件内部代码如下:
<template><div><h1>slot</h1><Todo :todos="todos"><template v-slot="{$row,$index}"><!--父组件决定子组件的结构与外观--><span :style="{color:$row.done?'green':'red'}">{{$row.title}}</span></template></Todo></div>
</template><script setup lang="ts">
import Todo from "./Todo.vue";
import { ref } from "vue";
//父组件内部数据
let todos = ref([{ id: 1, title: "吃饭", done: true },{ id: 2, title: "睡觉", done: false },{ id: 3, title: "打豆豆", done: true },
]);
</script>
<style scoped>
</style>
搭建后台管理系统模板
(1)项目初始化
一个项目要有统一的规范,需要使用 eslint + stylelint + prettier 来对我们的代码质量做检测和修复,需要使用 husky 来做 commit 拦截,需要使用 commitlint 来统一提交规范,需要使用 preinstall 来统一包管理工具。
1.环境准备
- node v16.14.2
- pnpm 8.0.0
2.初始化项目
本项目使用vite进行构建,vite官方中文文档参考:cn.vitejs.dev/guide/
pnpm:performant npm ,意味“高性能的 npm”。pnpm由npm/yarn衍生而来,解决了npm/yarn内部潜在的bug,极大的优化了性能,扩展了使用场景。被誉为“最先进的包管理工具”。
//package.json中"dev": "vite --open",
(2)项目配置
1.eslint 配置
eslint中文官网:http://eslint.cn/**http://eslint.cn/**
ESLint最初是由Nicholas C. Zakas 于2013年6月创建的开源项目。它的目标是提供一个插件化的javascript代码检测工具。
安装eslint
pnpm i eslint -D
生成配置文件:.eslint.cjs
npx eslint --init
module.exports = {//运行环境"env": { "browser": true,//浏览器端"es2021": true,//校验语法 es2021},//规则继承"extends": [ //全部规则默认是关闭的,这个配置项开启推荐规则,推荐规则参照文档//比如:函数不能重名、对象不能出现重复key"eslint:recommended",//vue3语法规则"plugin:vue/vue3-essential",//ts语法规则"plugin:@typescript-eslint/recommended"],//要为特定类型的文件指定处理器"overrides": [],//指定解析器:解析器//Esprima 默认解析器//Babel-ESLint babel解析器//@typescript-eslint/parser ts解析器"parser": "@typescript-eslint/parser",//指定解析器选项"parserOptions": {"ecmaVersion": "latest",//校验ECMA最新版本"sourceType": "module"//设置为"script"(默认),或者"module"代码在ECMAScript模块中},//ESLint支持使用第三方插件。在使用插件之前,您必须使用npm安装它//该eslint-plugin-前缀可以从插件名称被省略"plugins": ["vue","@typescript-eslint"],//eslint规则"rules": {}
}
vue3环境代码校验插件
# 让所有与prettier规则存在冲突的Eslint rules失效,并使用prettier进行代码检查
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-node": "^11.1.0",
# 运行更漂亮的Eslint,使prettier规则优先级更高,Eslint优先级低
"eslint-plugin-prettier": "^4.2.1",
# vue.js的Eslint插件(查找vue语法错误,发现错误指令,查找违规风格指南
"eslint-plugin-vue": "^9.9.0",
# 该解析器允许使用Eslint校验所有babel code
"@babel/eslint-parser": "^7.19.1",
vue3 环境代码校验插件
# 让所有与prettier规则存在冲突的Eslint rules失效,并使用prettier进行代码检查
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-node": "^11.1.0",
# 运行更漂亮的Eslint,使prettier规则优先级更高,Eslint优先级低
"eslint-plugin-prettier": "^4.2.1",
# vue.js的Eslint插件(查找vue语法错误,发现错误指令,查找违规风格指南
"eslint-plugin-vue": "^9.9.0",
# 该解析器允许使用Eslint校验所有babel code
"@babel/eslint-parser": "^7.19.1",
安装指令
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/
安装指令
pnpm install -D eslint-plugin-import eslint-plugin-vue eslint-plugin-node esli
修改.eslintrc.cjs配置文件
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', // 对模板中的自定义组件强制执行属性命名样式},
}
eslintignore 忽略文件
dist
node_modules
运行脚本
package.json新增两个运行脚本
"scripts": {"lint": "eslint src","fix": "eslint src --fix",
}
2.项目配置
有了eslint,为什么还要有prettier?eslint针对的是javascript,他是一个检测工具,包含js语法以及少部分格式问题,在eslint看来,语法对了就能保证代码正常运行,格式问题属于其次;
而prettier属于格式化工具,它看不惯格式不统一,所以它就把eslint没干好的事接着干,另外,prettier支持
包含js在内的多种语言。
总结起来,eslint和prettier这俩兄弟一个保证js代码质量,一个保证代码美观。
安装依赖包
pnpm install -D eslint-plugin-prettier prettier eslint-config-prettier
prettierrc.json添加规则
{"singleQuote": true, //字符串全为单引号"semi": false,//无需分号"bracketSpacing": true,"htmlWhitespaceSensitivity": "ignore","endOfLine": "auto","trailingComma": "all","tabWidth": 2
}
.prettierignore忽略文件
/dist/*
/html/*
.local
/node_modules/**
**/*.svg
**/*.sh
/public/*
通过 pnpm run lint 去检测语法,如果出现不规范格式,通过 pnpm run fix 修改
3.配置stylelint
stylelint 为 css的lint工具。可格式化css代码,检查css语法错误与不合理的写法,指定css书写顺序等。
项目中使用scss作为预处理器,安装以下依赖:
pnpm add sass sass-loader stylelint postcss postcss-scss postcss-html stylelint-config-prettier stylelint-config-recess-order stylelint-config-recommended-scss stylelint-config-standard stylelint-config-standard-vue stylelint-scss stylelint-order stylelint-config-standard-scss -D
.stylelintrc.cjs
配置文件
// @see https://stylelint.bootcss.com/module.exports = {extends: ['stylelint-config-standard', // 配置stylelint拓展插件'stylelint-config-html/vue', // 配置 vue 中 template 样式格式化'stylelint-config-standard-scss', // 配置stylelint scss插件'stylelint-config-recommended-vue/scss', // 配置 vue 中 scss 样式格式化'stylelint-config-recess-order', // 配置stylelint css属性书写顺序插件,'stylelint-config-prettier', // 配置stylelint和prettier兼容],overrides: [{files: ['**/*.(scss|css|vue|html)'],customSyntax: 'postcss-scss',},{files: ['**/*.(html|vue)'],customSyntax: 'postcss-html',},],ignoreFiles: ['**/*.js','**/*.jsx','**/*.tsx','**/*.ts','**/*.json','**/*.md','**/*.yaml',],/*** null => 关闭该规则* always => 必须*/rules: {'value-keyword-case': null, // 在 css 中使用 v-bind,不报错'no-descending-specificity': null, // 禁止在具有较高优先级的选择器后出现被其覆盖的较低优先级的选择器'function-url-quotes': 'always', // 要求或禁止 URL 的引号 "always(必须加上引号)"|"never(没有引号)"'no-empty-source': null, // 关闭禁止空源码'selector-class-pattern': null, // 关闭强制选择器类名的格式'property-no-unknown': null, // 禁止未知的属性(true 为不允许)'block-opening-brace-space-before': 'always', //大括号之前必须有一个空格或不能有空白符'value-no-vendor-prefix': null, // 关闭 属性值前缀 --webkit-box'property-no-vendor-prefix': null, // 关闭 属性前缀 -webkit-mask'selector-pseudo-class-no-unknown': [// 不允许未知的选择器true,{ignorePseudoClasses: ['global', 'v-deep', 'deep'], // 忽略属性,修改element默认样式的时候能使用到},],},
}
.stylelintignore忽略文件
/node_modules/*
/dist/*
/html/*
/public/*
添加运行脚本
"scripts": {"lint:style": "stylelint src/**/*.{css,scss,vue} --cache --fix"
}
最后配置统一的prettier来格式化我们的js和css,html代码
"scripts": {"dev": "vite --open","build": "vue-tsc && vite build","preview": "vite preview","lint": "eslint src","fix": "eslint src --fix","format": "prettier --write \"./**/*.{html,vue,ts,js,json,md}\"","lint:eslint": "eslint src/**/*.{ts,vue} --cache --fix","lint:style": "stylelint src/**/*.{css,scss,vue} --cache --fix"},
当我们运行 pnpm run format
的时候,会把代码直接格式化。
4.配置husky
在上面我们已经集成好了我们代码校验工具,但是需要每次手动的去执行命令才会格式化我们的代码。如果有人没有格式化就提交了远程仓库中,那这个规范就没什么用。所以我们需要强制让开发人员按照代码规范来提交。
要做到这件事情,就需要利用husky在代码提交之前触发 git hook (git在客户端的钩子),然后执行pnpm run format
来自动的格式化我们的代码。
安装husky
pnpm install -D husky
会在根目录下生成个一个.husky目录,在这个目录下面会有一个pre-commit文件,这个文件里面的命令在我们执行commit的时候就会执行
在.husky/pre-commit
文件添加如下命令:
npx husky-init
会在根目录下生成个一个.husky目录,在这个目录下面会有一个pre-commit文件,这个文件里面的命令在我们执行commit的时候就会执行
在.husky/pre-commit
文件添加如下命令:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm run format
5.配置commitlint
对于我们的commit信息,也是有统一规范的,不能随便写,要让每个人都按照统一的标准来执行,我们可以利用commitlint来实现。
安装包
pnpm add @commitlint/config-conventional @commitlint/cli -D
添加配置文件,新建 commitlint.config.cjs
(注意是cjs),然后添加下面的代码:
module.exports = {extends: ['@commitlint/config-conventional'],// 校验规则rules: {'type-enum': [2,'always',['feat','fix','docs','style','refactor','perf','test','chore','revert','build',],],'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', 72],},
}
在 package.json
中配置scripts命令
// 在scrips中添加下面的代码
{
"scripts": {"commitlint": "commitlint --config commitlint.config.cjs -e -V"},
}
配置结束,现在当我们填写commit
信息的时候,前面就需要带着下面的subject
'feat',//新特性、新功能
'fix',//修改bug
'docs',//文档修改
'style',//代码格式修改, 注意不是 css 修改
'refactor',//代码重构
'perf',//优化相关,比如提升性能、体验
'test',//测试用例修改
'chore',//其他修改, 比如改变构建流程、或者增加依赖库、工具等
'revert',//回滚到上一个版本
'build',//编译相关的修改,例如发布版本、对项目构建或者依赖的改动
配置husky
npx husky add .husky/commit-msg
在生成的commit-msg文件中添加下面的命令
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm commitlint
当commit 提交信息时,就不能再随意写了,必须是 git commit -m 'fix: xxx' 符合类型的才可以,需要注意的是类型的后面需要用英文的 :,并且冒号后面是需要空一格的,这个是不能省略的。
6.强制使用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提供的生命周期钩子)这个文件里面的代码。
三、项目集成
3.1 集成element-plus
硅谷甄选运营平台,UI组件库采用的element-plus,因此需要集成element-plus插件!!!
官网地址:一个 Vue 3 UI 框架 | Element Plus
pnpm install element-plus @element-plus/icons-vue
入口文件main.ts全局安装element-plus,element-plus默认支持语言英语设置为中文
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css'
//@ts-ignore忽略当前文件ts类型的检测否则有红色提示(打包会失败)
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
app.use(ElementPlus, {locale: zhCn
})
Element Plus全局组件类型声明
// tsconfig.json
{"compilerOptions": {// ..."types": ["element-plus/global"]}
}
配置完毕可以测试element-plus组件与图标的使用。
3.2 src别名的配置
在开发项目的时候文件与文件关系可能很复杂,因此我们需要给src文件夹配置一个别名!!!
// vite.config.ts
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
export default defineConfig({plugins: [vue()],resolve: {alias: {"@": path.resolve("./src") // 相对路径别名配置,使用 @ 代替 src}}
})
TypeScript 编译配置
// tsconfig.json
{"compilerOptions": {"baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录"paths": { //路径映射,相对于baseUrl"@/*": ["src/*"] }}
}
3.3环境变量的配置
项目开发过程中,至少会经历开发环境、测试环境和生产环境(即正式环境)三个阶段。不同阶段请求的状态(如接口地址等)不尽相同,若手动切换接口地址是相当繁琐且易出错的。于是环境变量配置的需求就应运而生,我们只需做简单的配置,把环境状态切换的工作交给代码。
开发环境(development)
顾名思义,开发使用的环境,每位开发人员在自己的dev分支上干活,开发到一定程度,同事会合并代码,进行联调。
测试环境(testing)
测试同事干活的环境啦,一般会由测试同事自己来部署,然后在此环境进行测试
生产环境(production)
生产环境是指正式提供对外服务的,一般会关掉错误报告,打开错误日志。(正式提供给客户使用的环境。)
注意:一般情况下,一个环境对应一台服务器,也有的公司开发与测试环境是一台服务器!!!
项目根目录分别添加 开发、生产和测试环境的文件!
.env.development
.env.production
.env.test
3.4 SVG图标配置
在开发项目的时候经常会用到svg矢量图,而且我们使用SVG以后,页面上加载的不再是图片资源,
这对页面性能来说是个很大的提升,而且我们SVG文件比img要小的很多,放在项目中几乎不占用资源。
安装SVG依赖插件
pnpm install vite-plugin-svg-icons -D
测试SVG图标使用
svg:图标外层容器节点, 内部需要与use标签结合使用
<svg><!-- 测试SVG图标使用svg:图标外层容器节点,内部需要与use标签结合使用 --><!-- xlink:href执行用哪一个图标,属性值务必#icon-图标名字 --><!-- use标签fill属性可以设置图标的颜色 --><use xlink:href="#icon-icon" fill="red"></use></svg>
在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 cachediconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],// Specify symbolId formatsymbolId: 'icon-[dir]-[name]',}),],}
}
入口文件导入
import 'virtual:svg-icons-register'
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);
3.5 集成sass
我们目前在组件内部已经可以使用scss样式,因为在配置styleLint工具的时候,项目当中已经安装过sass sass-loader,因此我们再组件内可以使用scss语法!!!需要加上lang="scss"
<style scoped lang="scss"></style>
接下来我们为项目添加一些全局的样式
在src/styles目录下创建一个index.scss文件,当然项目中需要用到清除默认样式,因此在index.scss引入reset.scss
@import reset.scss
在入口文件引入
import '@/styles'
但是你会发现在src/styles/index.scss全局样式文件中没有办法使用$变量.因此需要给项目中引入全局变量$.
在style/variable.scss创建一个variable.scss文件!
在vite.config.ts文件配置如下:
export default defineConfig((config) => {css: {preprocessorOptions: {scss: {javascriptEnabled: true,additionalData: '@import "./src/styles/variable.scss";',},},},}
}
@import "./src/styles/variable.less";
后面的;
不要忘记,不然会报错!
配置完毕你会发现scss提供这些全局变量可以在组件样式中使用了!!
3.6 mock数据
安装依赖: https://www.npmjs.com/package/vite-plugin-mock
pnpm install -D vite-plugin-mock mockjs
mock中配置文件会报错,是因为vite-plugin-mock版本匹配导致的,可以 pnpm i vite-plugin-mock@2.0.0
还有pinia最新版也不匹配 可以安装时候 pnpm i pinia@2.0.34
createUserList:次函数执行会返回一个数组,数组里面包含两个用户信息。
对外暴露一个数组:数组里面包含两个接口
登录假的接口
获取用户信息的假的接口
在 vite.config.js 配置文件启用插件。
mport { 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数据与接口!!!
在mock文件夹内部创建一个user.ts文件
//用户信息数据
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 } = checkUserreturn { code: 200, data: { token } }},},// 获取用户信息{url: '/api/user/info',method: 'get',response: (request) => {//获取请求头携带tokenconst 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
pnpm install axios
最后通过axios测试接口!!!
3.7 axios二次封装
在开发项目的时候避免不了与后端进行交互,因此我们需要使用axios插件实现发送网络请求。在开发项目的时候
我们经常会把axios进行二次封装。
目的:
1:使用请求拦截器,可以在请求拦截器中处理一些业务(开始进度条、请求头携带公共参数)
2:使用响应拦截器,可以在响应拦截器中处理一些业务(进度条结束、简化服务器返回的数据、处理http网络错误)
在根目录下创建utils/request.ts
这篇关于《硅谷甄选》项目笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!