• 硅谷甄选(Vue3+Typescript)


    硅谷甄选运营平台

    此次教学课程为硅谷甄选运营平台项目,包含运营平台项目模板从0到1开发,以及数据大屏幕、权限等业务。

    此次教学课程涉及到技术栈包含:vue3+TypeScript+vue-router+pinia+element-plus+axios+echarts等技术栈。

    一、vue3组件通信方式

    通信仓库地址:https://gitee.com/jch1011/vue3_communication.git

    不管是vue2还是vue3,组件通信方式很重要,不管是项目还是面试都是经常用到的知识点。

    比如:vue2组件通信方式

    props:可以实现父子组件、子父组件、甚至兄弟组件通信

    自定义事件:可以实现子父组件通信

    全局事件总线$bus:可以实现任意组件通信

    pubsub:发布订阅模式实现任意组件通信

    vuex:集中式状态管理容器,实现任意组件通信

    ref:父组件获取子组件实例VC,获取子组件的响应式数据以及方法

    slot:插槽(默认插槽、具名插槽、作用域插槽)实现父子组件通信…

    1.1props

    props可以实现父子组件通信,在vue3中我们可以通过defineProps获取父组件传递的数据。且在组件内部不需要引入defineProps方法可以直接使用!

    父组件给子组件传递数据

    
    
    • 1

    子组件获取父组件传递数据:方式1

    let props = defineProps({
      info:{
       type:String,//接受的数据类型
       default:'默认参数',//接受默认数据
      },
      money:{
       type:Number,
       default:0
    }})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    子组件获取父组件传递数据:方式2

    let props = defineProps(["info",'money']);
    
    • 1

    子组件获取到props数据就可以在模板中使用了,但是切记props是只读的(只能读取,不能修改)

    Child.vue

    <template>
      <div class="son">
           <h1>我是子组件:曹植</h1>
           <p>{{props.info}}</p>
           <p>{{props.money}}</p>
          <!--props可以省略前面的名字--->
           <p>{{info}}</p>
           <p>{{money}}</p>
           <button @click="updateProps">修改props数据</button>
      </div>
    </template>
    
    <script setup lang="ts">
    //需要使用到defineProps方法去接受父组件传递过来的数据
    //defineProps是Vue3提供方法,不需要引入直接使用
    let props = defineProps(['info','money']); //数组|对象写法都可以
    //按钮点击的回调
    const updateProps = ()=>{
      // props.money+=10;  props:只读的
      console.log(props.info)
    }
    </script>
    
    <style scoped>
    .son{
      width: 400px;
      height: 200px;
      background: hotpink;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    PropsTest.vue

    <template>
      <div class="box">
        <h1>props:我是父组件曹操</h1>
        <hr />
        <Child info="我是曹操" :money="money"></Child>
      </div>
    </template>
    
    <script setup lang="ts">
    //props:可以实现父子组件通信,props数据还是只读的!!!
    import Child from "./Child.vue";
    import { ref } from "vue";
    let money = ref(10000);
    </script>
    
    <style scoped>
    .box {
      width: 100vw;
      height: 400px;
      background: yellowgreen;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    1.2自定义事件

    在vue框架中事件分为两种:一种是原生的DOM事件,另外一种自定义事件。

    原生DOM事件可以让用户与网页进行交互,比如click、dbclick、change、mouseenter、mouseleave…

    自定义事件可以实现子组件给父组件传递数据

    1.2.1原生DOM事件

    代码如下:

     
          我是祖国的老花骨朵
     
    • 1
    • 2
    • 3

    当前代码级给pre标签绑定原生DOM事件点击事件,默认会给事件回调注入event事件对象。当然点击事件想注入多个参数可以按照下图操作。但是切记注入的事件对象务必叫做$event.

      
    我要传递多个参数
    • 1

    vue3框架click、dbclick、change(这类原生DOM事件),不管是在标签、自定义标签上(组件标签)都是原生DOM事件。

    1.2.2自定义事件

    自定义事件可以实现子组件给父组件传递数据.在项目中是比较常用的。

    比如在父组件内部给子组件(Event2)绑定一个自定义事件

    
    
    • 1

    在Event2子组件内部触发这个自定义事件

    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    我们会发现在script标签内部,使用了defineEmits方法,此方法是vue3提供的方法,不需要引入直接使用。defineEmits方法执行,传递一个数组,数组元素即为将来组件需要触发的自定义事件类型,此方执行会返回一个$emit方法用于触发自定义事件。

    当点击按钮的时候,事件回调内部调用$emit方法去触发自定义事件,第一个参数为触发事件类型,第二个、三个、N个参数即为传递给父组件的数据。

    需要注意的是:代码如下

    
    
    • 1

    正常说组件标签书写@click应该为原生DOM事件,但是如果子组件内部通过defineEmits定义就变为自定义事件了

    let $emit = defineEmits(["xxx",'click']);
    
    • 1

    Event1.vue

    <template>
      <div class="son">
          <p>我是子组件1</p>
          <button>点击我也执行</button>
      </div>
    </template>
    
    <script setup lang="ts">
    
    </script>
    
    <style scoped>
    .son{
      width: 400px;
      height: 200px;
      background: skyblue;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    Event2.vue

    <template>
      <div class="child">
        <p>我是子组件2</p>
        <button @click="handler">点击我触发自定义事件xxx</button>
        <button @click="$emit('click','AK47','J20')">点击我触发自定义事件click</button>
      </div>
    </template>
    
    <script setup lang="ts">
    //利用defineEmits方法返回函数触发自定义事件
    //defineEmits方法不需要引入直接使用
    let $emit = defineEmits(['xxx','click']);
    //按钮点击回调
    const handler = () => {
      //第一个参数:事件类型 第二个|三个|N参数即为注入数据
        $emit('xxx','东风导弹','航母');
    };
    </script>
    
    <style scoped>
    .child {
      width: 400px;
      height: 200px;
      background: pink;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    EventTest.vue

    <template>
      <div>
        <h1>事件</h1>
        <!-- 原生DOM事件 -->
        <pre @click="handler">
          大江东去浪淘尽,千古分流人物
        </pre>
        <button @click="handler1(1,2,3,$event)">点击我传递多个参数</button>
        <hr>
        <!--
            vue2框架当中:这种写法自定义事件,可以通过.native修饰符变为原生DOM事件
            vue3框架下面写法其实即为原生DOM事件
    
            vue3:原生的DOM事件不管是放在标签身上、组件标签身上都是原生DOM事件
          -->
        <Event1 @click="handler2"></Event1>
        <hr>
        <!-- 绑定自定义事件xxx:实现子组件给父组件传递数据 -->
        <Event2 @xxx="handler3" @click="handler4"></Event2>
      </div>
    </template>
    
    <script setup lang="ts">
    //引入子组件
    import Event1 from './Event1.vue';
    //引入子组件
    import Event2 from './Event2.vue';
    //事件回调--1
    const handler = (event)=>{
        //event即为事件对象
        console.log(event);
    }
    //事件回调--2
    const handler1 = (a,b,c,$event)=>{
       console.log(a,b,c,$event)
    }
    //事件回调---3
    const handler2 = ()=>{
        console.log(123);
    }
    //事件回调---4
    const handler3 = (param1,param2)=>{
        console.log(param1,param2);
    }
    //事件回调--5
    const handler4 = (param1,param2)=>{
         console.log(param1,param2);
    }
    </script>
    
    <style scoped>
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    在这里插入图片描述

    1.3全局事件总线

    全局事件总线可以实现任意组件通信,在vue2中可以根据VM与VC关系推出全局事件总线。

    但是在vue3中没有Vue构造函数,也就没有Vue.prototype.以及组合式API写法没有this,

    那么在Vue3想实现全局事件的总线功能就有点不现实啦,如果想在Vue3中使用全局事件总线功能

    可以使用插件mitt实现。

    mitt:官网地址:https://www.npmjs.com/package/mitt

    Child1.vue

    <template>
      <div class="child1">
        <h3>我是子组件1:曹植</h3>
      </div>
    </template>
    
    <script setup lang="ts">
    import $bus from "../../bus";
    //组合式API函数
    import { onMounted } from "vue";
    //组件挂载完毕的时候,当前组件绑定一个事件,接受将来兄弟组件传递的数据
    onMounted(() => {
      //第一个参数:即为事件类型  第二个参数:即为事件回调
      $bus.on("car", (car) => {
        console.log(car);
      });
    });
    </script>
    
    <style scoped>
    .child1 {
      width: 300px;
      height: 300px;
      background: hotpink;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    Child2.vue

    <template>
      <div class="child2">
         <h2>我是子组件2:曹丕</h2>
         <button @click="handler">点击我给兄弟送一台法拉利</button>
      </div>
    </template>
    
    <script setup lang="ts">
    //引入$bus对象
    import $bus from '../../bus';
    //点击按钮回调
    const handler = ()=>{
      $bus.emit('car',{car:"法拉利"});
    }
    </script>
    
    <style scoped>
    .child2{
      width: 300px;
      height: 300px;
      background: skyblue;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    EventBusTest.vue

    <template>
      <div class="box">
        <h1>全局事件总线$bus</h1>
        <hr />
        <div class="container">
          <Child1></Child1>
          <Child2></Child2>
        </div>
      </div>
    </template>
    
    <script setup lang="ts">
    //引入子组件
    import Child1 from "./Child1.vue";
    import Child2 from "./Child2.vue";
    </script>
    
    <style scoped>
    .box {
      width: 100vw;
      height: 400px;
      background: yellowgreen;
    }
    .container{
      display: flex;
      justify-content: space-between;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    在这里插入图片描述

    1.4v-model

    v-model指令可是收集表单数据(数据双向绑定),除此之外它也可以实现父子组件数据同步。

    而v-model实指利用props[modelValue]与自定义事件[update:modelValue]实现的。

    下方代码:相当于给组件Child传递一个props(modelValue)与绑定一个自定义事件update:modelValue

    实现父子组件数据同步

    
    
    • 1

    在vue3中一个组件可以通过使用多个v-model,让父子组件多个数据同步,下方代码相当于给组件Child传递两个props分别是pageNo与pageSize,以及绑定两个自定义事件update:pageNo与update:pageSize实现父子数据同步

    
    
    • 1

    Child.vue

    <template>
      <div class="child">
        <h3>钱数:{{ modelValue }}</h3>
        <button @click="handler">父子组件数据同步</button>
      </div>
    </template>
    
    <script setup lang="ts">
    //接受props
    let props = defineProps(["modelValue"]);
    let $emit = defineEmits(['update:modelValue']);
    //子组件内部按钮的点击回调
    const handler = ()=>{
       //触发自定义事件
       $emit('update:modelValue',props.modelValue+1000);
    }
    </script>
    
    <style scoped>
    .child {
      width: 600px;
      height: 300px;
      background: skyblue;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    Child1.vue

    <template>
      <div class="child2">
        <h1>同时绑定多个v-model</h1>
        <button @click="handler">pageNo{{ pageNo }}</button>
        <button @click="$emit('update:pageSize', pageSize + 4)">
          pageSize{{ pageSize }}
        </button>
      </div>
    </template>
    
    <script setup lang="ts">
    let props = defineProps(["pageNo", "pageSize"]);
    let $emit = defineEmits(["update:pageNo", "update:pageSize"]);
    //第一个按钮的事件回调
    const handler = () => {
      $emit("update:pageNo", props.pageNo + 3);
    };
    </script>
    
    <style scoped>
    .child2 {
      width: 300px;
      height: 300px;
      background: hotpink;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    ModelTest.vue

    <template>
      <div>
        <h1>v-model:钱数{{ money }}{{pageNo}}{{pageSize}}</h1>
        <input type="text" v-model="info" />
        <hr />
        <!-- props:父亲给儿子数据 -->
        <!-- <Child :modelValue="money" @update:modelValue="handler"></Child> -->
        <!-- 
           v-model组件身上使用
           第一:相当有给子组件传递props[modelValue] = 10000
           第二:相当于给子组件绑定自定义事件update:modelValue
         -->
        <Child v-model="money"></Child>
        <hr />
        <Child1 v-model:pageNo="pageNo" v-model:pageSize="pageSize"></Child1>
      </div>
    </template>
    
    <script setup lang="ts">
    //v-model指令:收集表单数据,数据双向绑定
    //v-model也可以实现组件之间的通信,实现父子组件数据同步的业务
    //父亲给子组件数据 props
    //子组件给父组件数据 自定义事件
    //引入子组件
    import Child from "./Child.vue";
    import Child1 from "./Child1.vue";
    import { ref } from "vue";
    let info = ref("");
    //父组件的数据钱数
    let money = ref(10000);
    //自定义事件的回调
    const handler = (num) => {
      //将来接受子组件传递过来的数据
      money.value = num;
    };
    
    //父亲的数据
    let pageNo = ref(1);
    let pageSize = ref(3);
    </script>
    
    <style scoped>
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    在这里插入图片描述

    1.5useAttrs

    在Vue3中可以利用useAttrs方法获取组件的属性与事件(包含:原生DOM事件或者自定义事件),次函数功能类似于Vue2框架中 a t t r s 属性与 attrs属性与 attrs属性与listeners方法。

    比如:在父组件内部使用一个子组件my-button

    
    
    • 1

    子组件内部可以通过useAttrs方法获取组件属性与事件.因此你也发现了,它类似于props,可以接受父组件传递过来的属性与属性值。需要注意如果defineProps接受了某一个属性,useAttrs方法返回的对象身上就没有相应属性与属性值。

    
    
    • 1
    • 2
    • 3
    • 4

    AttrsListenersTest.vue

    <template>
      <div>
        <h1>useAttrs</h1>
        <el-button type="primary" size="small" :icon="Edit"></el-button>
        <!-- 自定义组件 -->
        <HintButton type="primary" size="small" :icon="Edit" title="编辑按钮" @click="handler" @xxx="handler"></HintButton>
      </div>
    </template>
    
    <script setup lang="ts">
    //vue3框架提供一个方法useAttrs方法,它可以获取组件身上的属性与事件!!!
    //图标组件
    import {
      Check,
      Delete,
      Edit,
      Message,
      Search,
      Star,
    } from "@element-plus/icons-vue";
    import HintButton from "./HintButton.vue";
    //按钮点击的回调
    const handler = ()=>{
      alert(12306);
    }
    </script>
    
    <style scoped>
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    HintButton.vue

    <template>
      <div :title="title">
         <el-button :="$attrs"></el-button>   
      </div>
    </template>
    
    <script setup lang="ts">
    //引入useAttrs方法:获取组件标签身上属性与事件
    import {useAttrs} from 'vue';
    //此方法执行会返回一个对象
    let $attrs = useAttrs();
    
    //万一用props接受title
    let props =defineProps(['title']);
    //props与useAttrs方法都可以获取父组件传递过来的属性与属性值
    //但是props接受了useAttrs方法就获取不到了
    console.log($attrs);
    </script>
    
    <style scoped>
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    1.6ref与$parent

    ref,提及到ref可能会想到它可以获取元素的DOM或者获取子组件实例的VC。既然可以在父组件内部通过ref获取子组件实例VC,那么子组件内部的方法与响应式数据父组件可以使用的。

    比如:在父组件挂载完毕获取组件实例

    父组件内部代码:

    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    但是需要注意,如果想让父组件获取子组件的数据或者方法需要通过defineExpose对外暴露,因为vue3中组件内部的数据对外“关闭的”,外部不能访问

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    $parent可以获取某一个组件的父组件实例VC,因此可以使用父组件内部的数据与方法。必须子组件内部拥有一个按钮点击时候获取父组件实例,当然父组件的数据与方法需要通过defineExpose方法对外暴露

    
    
    • 1

    Daughter.vue

    <template>
      <div class="dau">
         <h1>我是闺女曹杰{{money}}</h1>
         <button @click="handler($parent)">点击我爸爸给我10000元</button>
      </div>
    </template>
    
    <script setup lang="ts">
    import {ref} from 'vue';
    //闺女钱数
    let money = ref(999999);
    //闺女按钮点击回调
    const handler = ($parent)=>{
       money.value+=10000;
       $parent.money-=10000;
    }
    </script>
    
    <style scoped>
    .dau{
      width: 300px;
      height: 300px;
      background: hotpink;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    RefChildrenParentTest.vue

    <template>
      <div class="box">
        <h1>我是父亲曹操:{{money}}</h1>
        <button @click="handler">找我的儿子曹植借10元</button>
        <hr>
        <Son ref="son"></Son>
        <hr>
        <Dau></Dau>
      </div>
    </template>
    
    <script setup lang="ts">
    //ref:可以获取真实的DOM节点,可以获取到子组件实例VC
    //$parent:可以在子组件内部获取到父组件的实例
    //引入子组件
    import Son from './Son.vue'
    import Dau from './Daughter.vue'
    import {ref} from 'vue';
    //父组件钱数
    let money = ref(100000000);
    //获取子组件的实例
    let son = ref();
    //父组件内部按钮点击回调
    const handler = ()=>{
       money.value+=10;
       //儿子钱数减去10
       son.value.money-=10;
       son.value.fly();
    }
    //对外暴露
    defineExpose({
       money
    })
    </script>
    
    <style scoped>
    .box{
      width: 100vw;
      height: 500px;
      background: skyblue;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    Son.vue

    <template>
      <div class="son">
        <h3>我是子组件:曹植{{money}}</h3>
      </div>
    </template>
    
    <script setup lang="ts">
    import {ref} from 'vue';
    //儿子钱数
    let money = ref(666);
    const fly = ()=>{
      console.log('我可以飞');
    }
    //组件内部数据对外关闭的,别人不能访问
    //如果想让外部访问需要通过defineExpose方法对外暴露
    defineExpose({
      money,
      fly
    })
    </script>
    
    <style scoped>
    .son {
      width: 300px;
      height: 200px;
      background: cyan;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    在这里插入图片描述

    1.7provide与inject

    provide[提供]

    inject[注入]

    vue3提供两个方法provide与inject,可以实现隔辈组件传递参数

    组件组件提供数据:

    provide方法用于提供数据,此方法执需要传递两个参数,分别提供数据的key与提供数据value

    
    
    • 1
    • 2
    • 3
    • 4

    后代组件可以通过inject方法获取数据,通过key获取存储的数值

    
    
    • 1
    • 2
    • 3
    • 4

    Child.vue

    <template>
      <div class="child">
         <h1>我是子组件1</h1>
         <Child></Child>
      </div>
    </template>
    
    <script setup lang="ts">
    import Child from './GrandChild.vue';
    </script>
    
    <style scoped>
    .child{
      width: 300px;
      height: 400px;
      background: yellowgreen;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    GrandChild.vue

    <template>
      <div class="child1">
        <h1>孙子组件</h1>
        <p>{{car}}</p>
        <button @click="updateCar">更新数据</button>
      </div>
    </template>
    
    <script setup lang="ts">
    import {inject} from 'vue';
    //注入祖先组件提供数据
    //需要参数:即为祖先提供数据的key
    let car = inject('TOKEN');
    const updateCar = ()=>{
       car.value  = '自行车';
    }
    </script>
    
    <style scoped>
    .child1 {
      width: 200px;
      height: 200px;
      background: red;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    ProvideInjectTest.vue

    <template>
      <div class="box">
        <h1>Provide与Inject{{car}}</h1>
        <hr />
        <Child></Child>
      </div>
    </template>
    
    <script setup lang="ts">
    import Child from "./Child.vue";
    //vue3提供provide(提供)与inject(注入),可以实现隔辈组件传递数据
    import { ref, provide } from "vue";
    let car = ref("法拉利");
    //祖先组件给后代组件提供数据
    //两个参数:第一个参数就是提供的数据key
    //第二个参数:祖先组件提供数据
    provide("TOKEN", car);
    </script>
    
    <style scoped>
    .box {
      width: 100vw;
      height: 600px;
      background: skyblue;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在这里插入图片描述

    1.8pinia

    pinia官网:https://pinia.web3doc.top/

    pinia也是集中式管理状态容器,类似于vuex。但是核心概念没有mutation、modules,使用方式参照官网
    Child.vue

    <template>
      <div class="child">
        <h1>{{ infoStore.count }}---{{infoStore.total}}</h1>
        <button @click="updateCount">点击我修改仓库数据</button>
      </div>
    </template>
    
    <script setup lang="ts">
    import useInfoStore from "../../store/modules/info";
    //获取小仓库对象
    let infoStore = useInfoStore();
    console.log(infoStore);
    //修改数据方法
    const updateCount = () => {
      //仓库调用自身的方法去修改仓库的数据
      infoStore.updateNum(66,77);
    };
    </script>
    
    <style scoped>
    .child {
      width: 200px;
      height: 200px;
      background: yellowgreen;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    Child1.vue

    <template>
      <div class="child1">
        {{ infoStore.count }}
        <p @click="updateTodo">{{ todoStore.arr }}{{todoStore.total}}</p>
      </div>
    </template>
    
    <script setup lang="ts">
    import useInfoStore from "../../store/modules/info";
    //获取小仓库对象
    let infoStore = useInfoStore();
    
    //引入组合式API函数仓库
    import useTodoStore from "../../store/modules/todo";
    let todoStore = useTodoStore();
    
    //点击p段落去修改仓库的数据
    const updateTodo = () => {
      todoStore.updateTodo();
    };
    </script>
    
    <style scoped>
    .child1 {
      width: 200px;
      height: 200px;
      background: hotpink;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    index.vue

    <template>
      <div class="box">
        <h1>pinia</h1>
        <div class="container">
          <Child></Child>
          <Child1></Child1>
        </div>
      </div>
    </template>
    
    <script setup lang="ts">
    import Child from "./Child.vue";
    import Child1 from "./Child1.vue";
    //vuex:集中式管理状态容器,可以实现任意组件之间通信!!!
    //核心概念:state、mutations、actions、getters、modules
    
    //pinia:集中式管理状态容器,可以实现任意组件之间通信!!!
    //核心概念:state、actions、getters
    //pinia写法:选择器API、组合式API
    </script>
    
    <style scoped>
    .box {
      width: 600px;
      height: 400px;
      background: skyblue;
    }
    .container{
      display: flex;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    在这里插入图片描述

    1.9slot

    插槽:默认插槽、具名插槽、作用域插槽可以实现父子组件通信.

    默认插槽:

    在子组件内部的模板中书写slot全局组件标签

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在父组件内部提供结构:Todo即为子组件,在父组件内部使用的时候,在双标签内部书写结构传递给子组件

    注意开发项目的时候默认插槽一般只有一个

    
      

    我是默认插槽填充的结构

    • 1
    • 2
    • 3

    具名插槽:

    顾名思义,此插槽带有名字在组件内部留多个指定名字的插槽。

    下面是一个子组件内部,模板中留两个插槽

    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    父组件内部向指定的具名插槽传递结构。需要注意v-slot:可以替换为#

    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    作用域插槽

    作用域插槽:可以理解为,子组件数据由父组件提供,但是子组件内部决定不了自身结构与外观(样式)

    子组件Todo代码如下:

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    父组件内部代码如下:

    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    SlotTest.vue

    <template>
      <div>
        <h1>slot</h1>
        <Test1 :todos="todos">
          <template v-slot="{ $row, $index }">
            <p :style="{ color: $row.done ? 'green' : 'red' }">
              {{ $row.title }}--{{ $index }}
            </p>
          </template>
        </Test1>
        <Test>
          <div>
            <pre>大江东去浪淘尽,千古分流人物</pre>
          </div>
          <!-- 具名插槽填充a -->
          <template #a>
            <div>我是填充具名插槽a位置结构</div>
          </template>
          <!-- 具名插槽填充b v-slot指令可以简化为# -->
          <template #b>
            <div>我是填充具名插槽b位置结构</div>
          </template>
        </Test>
      </div>
    </template>
    
    <script setup lang="ts">
    import Test from "./Test.vue";
    import Test1 from "./Test1.vue";
    //插槽:默认插槽、具名插槽、作用域插槽
    //作用域插槽:就是可以传递数据的插槽,子组件可以讲数据回传给父组件,父组件可以决定这些回传的
    //数据是以何种结构或者外观在子组件内部去展示!!!
    
    import { ref } from "vue";
    //todos数据
    let todos = ref([
      { id: 1, title: "吃饭", done: true },
      { id: 2, title: "睡觉", done: false },
      { id: 3, title: "打豆豆", done: true },
      { id: 4, title: "打游戏", done: false },
    ]);
    </script>
    
    <style scoped>
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    Test.vue

    <template>
      <div class="box">
        <h1>我是子组件默认插槽</h1>
        <!-- 默认插槽 -->
        <slot></slot>
        <h1>我是子组件默认插槽</h1>
        <h1>具名插槽填充数据</h1>
        <slot name="a"></slot>
        <h1>具名插槽填充数据</h1>
        <h1>具名插槽填充数据</h1>
        <slot name="b"></slot>
        <h1>具名插槽填充数据</h1>
      </div>
    </template>
    
    <script setup lang="ts">
    </script>
    
    <style scoped>
    .box {
      width: 100vw;
      height: 500px;
      background: skyblue;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    Test1.vue

    <template>
      <div class="box">
        <h1>作用域插槽</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">
    //通过props接受父组件传递数据
    defineProps(["todos"]);
    </script>
    
    <style scoped>
    .box {
      width: 100vw;
      height: 400px;
      background: skyblue;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述
    App.vue

    <template>
      <div class="com">
        <h1>
          各种Vue组件间通信/传值
          <span style="color: red;">非常重要, 面试必备</span>
        </h1>
    
        <div>
          <h2 style="display: inline">演示:</h2>&nbsp;&nbsp;
          <router-link to="/props_pre">props</router-link>&nbsp;&nbsp;
          <router-link to="/event_pre">custom event</router-link>&nbsp;&nbsp;
          <router-link to="/bus_pre">event bus</router-link>&nbsp;&nbsp;
          <router-link to="/model_pre">v-model</router-link>&nbsp;&nbsp;
          <router-link to="/attrs-listeners_pre">useAttrs</router-link>&nbsp;&nbsp;
          <router-link to="/ref-parent_pre">ref$parent</router-link>&nbsp;&nbsp;
          <router-link to="/provide-inject_pre">provide-inject</router-link>&nbsp;&nbsp;
          <router-link to="/vuex_pre">pinia</router-link>&nbsp;&nbsp;
          <router-link to="/slot_pre">slot</router-link>&nbsp;&nbsp;
        </div>
        <br>
        <router-view></router-view>
      </div>
    </template>
    
    <script lang="ts">
      export default {
        name: 'App',
      }
    </script>
    <script lang="ts" setup>
      
    </script>
    <style lang="less" scoped>
      .com {
        margin: 10px;
        a {
          font-size: 16px;
          margin-right: 5px;
          &.router-link-active {
            color: red;
          }
        }
      }
    </style>
    <style>
      .box {
        border: solid 1px #aaa;
        margin: 5px;
        padding: 5px;
      }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    二、搭建后台管理系统模板

    2.1项目初始化

    今天来带大家从0开始搭建一个vue3版本的后台管理系统。一个项目要有统一的规范,需要使用eslint+stylelint+prettier来对我们的代码质量做检测和修复,需要使用husky来做commit拦截,需要使用commitlint来统一提交规范,需要使用preinstall来统一包管理工具。

    下面我们就用这一套规范来初始化我们的项目,集成一个规范的模版。

    2.1.1环境准备
    • node v16.14.2
    • pnpm 8.0.0
    2.1.2初始化项目

    本项目使用vite进行构建,vite官方中文文档参考:cn.vitejs.dev/guide/

    pnpm:performant npm ,意味“高性能的 npm”。pnpm由npm/yarn衍生而来,解决了npm/yarn内部潜在的bug,极大的优化了性能,扩展了使用场景。被誉为“最先进的包管理工具”

    pnpm安装指令

    npm i -g pnpm
    
    • 1

    项目初始化命令:

    pnpm create vite
    
    • 1

    进入到项目根目录pnpm install安装全部依赖.安装完依赖运行程序:pnpm run dev

    运行完毕项目跑在http://127.0.0.1:5173/,可以访问你得项目啦

    2.2项目配置

    一、eslint配置

    eslint中文官网:http://eslint.cn/

    ESLint最初是由Nicholas C. Zakas 于2013年6月创建的开源项目。它的目标是提供一个插件化的javascript代码检测工具

    首先安装eslint

    pnpm i eslint -D
    
    • 1

    生成配置文件:.eslint.cjs

    npx eslint --init
    
    • 1

    .eslint.cjs配置文件

    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": {
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    1.1vue3环境代码校验插件
    # 让所有与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",
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    安装指令

    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
    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', // 防止
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    在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]);
            })
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在入口文件引入src/index.ts文件,通过app.use方法安装自定义插件

    import gloablComponent from './components/index';
    app.use(gloablComponent);
    
    • 1
    • 2

    3.5集成sass

    我们目前在组件内部已经可以使用scss样式,因为在配置styleLint工具的时候,项目当中已经安装过sass sass-loader,因此我们再组件内可以使用scss语法!!!需要加上lang=“scss”

    
    
    • 1

    接下来我们为项目添加一些全局的样式

    在src/styles目录下创建一个index.scss文件,当然项目中需要用到清除默认样式,因此在index.scss引入reset.scss

    @import reset.scss
    
    • 1

    在入口文件引入

    import '@/styles'
    
    • 1

    但是你会发现在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";',
            },
          },
        },
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    @import "./src/styles/variable.less";后面的;不要忘记,不然会报错!

    配置完毕你会发现scss提供这些全局变量可以在组件样式中使用了!!!

    3.6mock数据

    安装依赖:https://www.npmjs.com/package/vite-plugin-mock

    pnpm install -D vite-plugin-mock mockjs
    
    • 1

    在 vite.config.js 配置文件启用插件。

    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',
          }),
        ],
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在根目录创建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 } = 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} }
            },
        },
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    安装axios

    pnpm install axios
    
    • 1

    最后通过axios测试接口!!!

    3.7axios二次封装

    在开发项目的时候避免不了与后端进行交互,因此我们需要使用axios插件实现发送网络请求。在开发项目的时候

    我们经常会把axios进行二次封装。

    目的:

    1:使用请求拦截器,可以在请求拦截器中处理一些业务(开始进度条、请求头携带公共参数)

    2:使用响应拦截器,可以在响应拦截器中处理一些业务(进度条结束、简化服务器返回的数据、处理http网络错误)

    在根目录下创建utils/request.ts

    import axios from "axios";
    import { ElMessage } from "element-plus";
    //创建axios实例
    let request = axios.create({
        baseURL: import.meta.env.VITE_APP_BASE_API,
        timeout: 5000
    })
    //请求拦截器
    request.interceptors.request.use(config => {
        return config;
    });
    //响应拦截器
    request.interceptors.response.use((response) => {
        return response.data;
    }, (error) => {
        //处理网络错误
        let msg = '';
        let status = error.response.status;
        switch (status) {
            case 401:
                msg = "token过期";
                break;
            case 403:
                msg = '无权访问';
                break;
            case 404:
                msg = "请求地址错误";
                break;
            case 500:
                msg = "服务器出现问题";
                break;
            default:
                msg = "无网络";
    
        }
        ElMessage({
            type: 'error',
            message: msg
        })
        return Promise.reject(error);
    });
    export default request;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    3.8API接口统一管理

    在开发项目的时候,接口可能很多需要统一管理。在src目录下去创建api文件夹去统一管理项目的接口;

    比如:下面方式

    //统一管理咱们项目用户相关的接口
    
    import request from '@/utils/request'
    
    import type {
    
     loginFormData,
    
     loginResponseData,
    
     userInfoReponseData,
    
    } from './type'
    
    //项目用户相关的请求地址
    
    enum API {
    
     LOGIN_URL = '/admin/acl/index/login',
    
     USERINFO_URL = '/admin/acl/index/info',
    
     LOGOUT_URL = '/admin/acl/index/logout',
    
    }
    //登录接口
    export const reqLogin = (data: loginFormData) =>
     request.post(API.LOGIN_URL, data)
    //获取用户信息
    
    export const reqUserInfo = () =>
    
     request.get(API.USERINFO_URL)
    
    //退出登录
    
    export const reqLogout = () => request.post(API.LOGOUT_URL)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    四、项目的资源地址

    贾成豪老师代码仓库地址:https://gitee.com/jch1011/vue3_admin_template-bj1.git

    项目在线文档:

    服务器域名:http://sph-api.atguigu.cn

    swagger文档:

    http://139.198.104.58:8209/swagger-ui.html

    http://139.198.104.58:8212/swagger-ui.html#/

    echarts:国内镜像网站

    https://www.isqqw.com/echarts-doc/zh/option.html#title

    http://datav.aliyun.com/portal/school/atlas/area_selector

  • 相关阅读:
    「基础知识」带你深度剖析路由器和交换机!
    线程简单知识点
    Vue_Bug VUE-ELEMENT-ADMIN默认是英文模式
    鸿蒙系统扫盲(一):鸿蒙OS和开源鸿蒙什么关系?
    “前端”工匠系列(一):合格的工匠,究竟该搞什么 | 京东云技术团队
    【蓝桥】通关
    Docker命令实战-打包镜像并发布到docker hub
    sql注入手法详解
    PDCA项目开发环境搭建说明
    前端工程化小记
  • 原文地址:https://blog.csdn.net/qq_46574748/article/details/132995351