Vue3学习笔记(二)

目录

七、插槽

1、匿名插槽

2、具名插槽

3、作用域插槽

4、动态插槽

八、传送(Teleport)

1、Teleport的使用

2、使用场景

九、动态组件

十、异步组件

十一、Mixin

十二、Provide / Inject (依赖注入)

十三、Vuex

十四、Pinia

1、state的使用 

2、getters的使用

3、actions的使用

4、分模块

5、Pinia持久化存储

十五、设置代理解决跨域问题

1、设置代理

2、axios二次封装

3、解耦合

4、使用


七、插槽

插槽就是子组件中的提供给父组件使用的一个占位符,用<slot></slot> 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的<slot></slot>标签。

1、匿名插槽

父级:

<!--例-->
<template>
  <div>
    <A>
      数据或结构A1
      数据或结构A2
    </A>
  </div>
</template>
<script setup>
import A from '../url.vue'
</script>

子级 :

<template>
  <div>
    <header>
      <div>
        头部
      </div>
      <slot></slot> <!--接收父级传输数据-->
    </header>
    <footer>
      <div>
        底部
      </div>
      <slot></slot> <!--接收父级传输数据-->
    </footer>
  </div>
</template>

以上为匿名插槽,缺点明显,slot会接收父级传来的所有匿名插槽数据或结构,且使用一遍slot,就遍历一遍,无法选择想插入的父级的某一部分匿名插槽数据或结构。

2、具名插槽

父级:

<!--例-->
<template>
  <div>
    <A>
      <template v-slot:A1>
        数据或结构A1
      </template>
      <template v-slot:A2>
        数据或结构A2
      </template>
    </A>
  </div>
</template>
<script setup>
import A from '../url.vue'
</script>

子级:

<template>
  <div>
    <header>
      <div>
        头部
      </div>
      <slot name='A1'></slot> <!--接收父级传输数据-->
    </header>
    <footer>
      <div>
        底部
      </div>
      <slot name='A2'></slot> <!--接收父级传输数据-->
    </footer>
  </div>
</template>

 其中,父级的 <template v-slot:A2>,可简写成 <template #A2>

具名插槽充分解决了匿名插槽不能特定引用的问题。 

3、作用域插槽

父级:

<!--例-->
<template>
  <div>
    <A>
      <template v-slot='{data}'>
        {{ data.age }}
      </template>
      <template v-slot='{data}'>
        {{ data.name }}
      </template>
    </A>
  </div>
</template>
<script setup>
import A from '../url.vue'
</script>

子级:

<!--例-->
<template>
  <div>
    <header>
      <div>
        头部
      </div>
      <slot name='A1'></slot> <!--接收父级传输数据-->
    </header>
    <footer>
      <div>
        底部
      </div>
      <div v-for="item in list":key="item.id"> <!--接收父级传输数据-->
        <slot :data="item"></slot>
      </div>
    </footer>
  </div>
</template>
<script setup>
import {ref} from "vue";
let list = ref([
  {
    id:1001,
    name:Tony,
    age:24
  },
  {
    id:1002,
    name:Sandy,
    age:24
  }
])
</script>

4、动态插槽

父级:

<!--例-->
<template>
  <div>
    <A>
      <template #[str]>
        数据A1
      </template>
      <template v-slot='{data}'>
        {{ data.name }}
      </template>
    </A>
  </div>
</template>
<script setup>
import A from '../url.vue'
import {ref} from "vue";
let str = ref('A1');
</script>

子级:

<!--例--> 
<template>
  <div>
    <header>
      <div>
        头部
      </div>
      <slot name='A1'></slot> <!--接收父级传输数据-->
    </header>
    <footer>
      <div>
        底部
      </div>
      <div v-for="item in list":key="item.id"> <!--接收父级传输数据-->  
        <slot :data="item"></slot>
      </div>
    </footer>
  </div>
</template>
<script setup>
import {ref} from "vue";
let list = ref([
  {
    id:1001,
    name:Tony,
    age:24
  },
  {
    id:1002,
    name:Sandy,
    age:24
  }
])
</script>

八、传送(Teleport)

1、Teleport的使用

通过类传送:

<!--父级--> 
<div class='main'>
    这是类
</div>
<A></A>
<script setup>
import A from '../url.vue'
</script>

<!--子级--> 
<teleport to='.main'></teleport>

通过id传送:

<!--父级--> 
<div id='main'>
    这是id
</div>
<A></A>
<script setup>
import A from '../url.vue'
</script>

<!--子级--> 
<teleport to='#main'></teleport>

通过组件传送:

<!--父级--> 
<div id='main'>
    这是id
</div>
<div class='main'>
    这是类
</div>
<A></A>
<script setup>
import A from '../url.vue'
</script>

<!--子级--> 
<teleport to='body'></teleport>  <!--传送至body信息显示完全后-->

注意!!!teleport传送目标必须在传送命令之前,即传送命令执行时,目标dom已存在,否则不进行传送。

2、使用场景

常见的情景是创建一个包含全屏模式的组件,在大多数情况下,你希望模态框的逻辑存在于组件中,但是模态框的快速定位就很难通过CSS来解决,或者需要更改组件组合。

九、动态组件

通过点击切换页面

<template>
  <ul>
   <li
       v-for='(data,key) in tabList'
       :key="key"
       @click="changevalue(key)"
   >
      id: {{ data.id }} 姓名:{{ data.name }}
   </li>
  </ul>
  <component :is="begin.key"></component>
</template>
<script setup>
import A from './A.vue'
import B from './B.vue'
import C from './C.vue'
import {markRaw, reactive} from "vue";
const tabList =reactive([
  {key:markRaw(A),id:10010,name:'Tony'},  // markRaw 用于提高响应
  {key:markRaw(B),id:10011,name:'Sandy'},
  {key:markRaw(C),id:10012,name:'John'},
]);
const begin = reactive({  // 设置默认,当前设置默认为A
  key:A
});
const changevalue = (key) => {  // 设置鼠标点击事件
  begin.key = tabList[key].key
}
</script>
<!--./A.vue--> 
<template>
  这是A页面
</template>

<!--./B.vue--> 
<template>
  这是B页面
</template>

<!--./C.vue--> 
<template>
  这是C页面
</template>

十、异步组件

异步组件用于提升性能,当下滑滑到目标组件区域时,组件再进行显示。

参考链接:异步组件:https://cn.vuejs.org/guide/components/async.html

                  vueuse:https://vueuse.org/core/useintersectionobserver

vueuse库安装命令:

npm i @vueuse/core -S

异步组件案例一,简单异步显示:

<!--主页面-->
<template>
  <A></A>
  <B></B>
  <div ref='target'>
    <C v-if="targetIsVisible"></C>
  </div>
</template>

<script setup>
import A from './A.vue'
import B from './B.vue'
import { useIntersectionObserver } from '@vueuse/core' 
// 若useIntersectionObserver在下载vueuse后还无法使用(No documentation found),就将@vueuse/core的路径写全
import {defineAsyncComponent, ref} from "vue"; // defineAsyncComponent设定异步组件,当下滑滑到组件区域组件再显示
const C = defineAsyncComponent(()=>
  import('./C.vue')
)
const target = ref(null);
const targetIsVisible = ref(false);
const { stop } = useIntersectionObserver(
    target,
    ([{ isIntersecting }]) => {
      if ( isIntersecting ){  // 一定要if语句判断,否则你的组件C会鬼畜
        targetIsVisible.value = isIntersecting
      }
    },
)
</script>
<!--A、B、C样本界面-->
<template>
  <div style="width: 100%;height: 700px;background-color: #646cff">
    A model Hello world
  </div>
</template>

<script>
export default {
  name: "A"
}
</script>

异步组件案例二,接口数据引用:

<!--主页面-->
<template>
  <Suspense>
    <template #default> <!--加载成功-->
      <A></A>
    </template>
    <template #fallback>  <!--加载失败-->
      加载中...
    </template>
  </Suspense>
</template>

<script setup>
import A from './A.vue'
import {ref} from "vue"; 
</script>

Suspense方法使用参考连接:https://cn.vuejs.org/guide/built-ins/suspense.html

<!--A页面-->
<template>
  <div>
    <h1>A model</h1>
    {{ list }}
  </div>
</template>
<script setup>
import {ref} from "vue";
import axios from 'axios';
let list = ref([]);
let res = await axios.get('目标接口数据的URL');   //用于发送请求
list.value = res.data.data.list
</script>

axios安装命令:

npm install axios -S

十一、Mixin

        作用:用来分发Vue组件中的可复用功能

例1:

// Mixin.js文件
import {ref} from "vue";
export default function (){
    let num = ref(1);
    let fav = ref(false);  // 加一个判断
    let favBtn = ()=>{  // 添加点击事件
        num.value += 1;  // 点击一下num+1
        fav.value =true;  // 判断改为true
        setTimeout(()=>{  // 点击完成后,过两秒改为false
            fav.value = false;
        },2000)
    }
    return{
        num,
        fav,
        favBtn
    }
}

想要复用的A、B界面:

<!--A.vue-->
<template>
  <div>
    <h1>A model</h1>
    收藏数: {{ num }}&nbsp;
    <button @click="favBtn">
      {{ fav ? '收藏中...' : '收藏'}}
    </button>
  </div>
</template>
<script setup>
import Mixin from "./Mixin.js";
const { num , fav , favBtn } = Mixin();
console.log(Mixin());
</script>

<!--B.vue-->
<template>
  <div>
    <h1>B model</h1>
    收藏数: {{ num }}&nbsp;
    <button @click="favBtn">
      {{ fav ? '收藏中...' : '收藏'}}
    </button>
  </div>
</template>
<script setup>
import Mixin from "./Mixin.js";
const { num , fav , favBtn } = Mixin();
console.log(Mixin());
</script>

主页面:

<!--主页面-->
<template>
  <A></A>
  <B></B>
</template>

<script setup>
import A from './A.vue'
import B from './B.vue'
</script>

效果图:

十二、Provide / Inject (依赖注入)

<!--数据提供-->
<!--主页面-->
<template>
  <A></A>
</template>

<script setup>
import A from './A.vue'
import {provide, ref} from "vue";
const num = ref(100);
provide('changeNum',num);  // 数据提供,changeNum为接收名,num为提供数据内容
</script>
<!--子级注入 ——A.vue -->
<template>
  {{ aNum }}
  <hr />
  <B></B>
</template>
<script setup>
import { inject } from "vue";
import B from './B.vue'
const aNum = inject('changeNum')  // 数据接收
</script>
<!--孙级注入 ——B.vue-->
<template>
  {{ bNum }}
  <button @click='btn'>click</button>
</template>
<script setup>

import {inject} from "vue";

const bNum = inject('changeNum');  // 数据接收
const btn = () => {  // 对接收数据进行操作
  bNum.value++;
}
</script>

十三、Vuex

// stores中index.js文件
import { createStore } from 'vuex';
export default createStore({
    state:{
        num:10,  // 例
        str:'The data that we need to have',  // 例
    }, // vuex的基本数据,用来存储相关变量
    getters:{}, // 与state结合使用,相当于是state的计算属性
    mutations:{},// 提交更新数据的方法,必须是同步的(如果需要异步使用actions)。每个mutations都有一个字符串的事件类型(type)和一个回调函数(handler)。回调函数就是我们实际进行状态更改的地方,并且它会接受 state作为第一个参数,提交载荷作为第二个参数。
    actions:{},  // 和mutations的功能大致相同
    modules:{},  // 模块化vuex,可以让每一个模块拥有自己的 state、mutation、action、 getters,使得结构非常清晰,方便管理。
});

Vue2与Vue3的区别参考网址:https://github.com/vuejs/rfcs/pull/271

十四、Pinia

Pinia同Vuex一样同为vue中的状态管理

Pinia的特点:

1、支持选项式api和组合式api写法

2、Pinia没有mutations,只有state、getters、actions

3、Pinia分块不需要modules

4、自动化代码拆分

5、支持TS写法

6、Pinia性能更好

7、可以直接修改state数据

 Pinia的使用:

官方网址:https://pinia.vuejs.org/

Pinia安装指令:

主页面配置(main.ts):

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
import {createPinia} from "pinia";

createApp(App).use(createPinia()).use(router).mount('#app')

1、state的使用 

router配置:

import { defineStore } from 'pinia';
export const useStore = defineStore('storeId',{
    state:()=>{
        return {
            key:0,
            name:'Tony',
            age:24,
            gender:'boy'
        }
    },
    getters:{

    },
    actions:{
    }
})

Pinia中方法state的使用:

<!--显示-->
<template>
  {{ name }}
  {{ age }}
</template>
<script setup>
import { useStore } from "../stores";
const store = useStore();
let { name ,age } = store;
</script>
<!--修改1-->
<template>
  {{ name }}
  {{ age }}
  <button @click="changeName">修改名称</button>
</template>
<script setup>
import {storeToRefs} from 'pinia'
import { useStore } from "../stores";
const store = useStore();
let { name ,age } = storeToRefs(store);
const changeName = () => {
  name.value = 'John';
}
</script>
<!--修改2-->
<template>
  {{ name }}
  {{ age }}
  <button @click="changeName">修改名称</button>
</template>
<script setup>
import {storeToRefs} from 'pinia'
import { useStore } from "../stores";
const store = useStore();
let { name ,age } = storeToRefs(store);
const changeName = () => {
  store.$patch(state=>{
    state.name = 'John';
    state.age = 23;
  })
}
</script>

2、getters的使用

router配置:

import { defineStore } from 'pinia';
export const useStore = defineStore('storeId',{
    state:()=>{
        return {
            key:0,
            name:'Tony',
            age:24,
            gender:'boy'
        }
    },
    getters:{
        changeAge(){
            return this.age + 1;
        }
    },
    actions:{

    }
})
<!--显示-->
<template>
  {{ name }}
  {{ age }}
  <button @click="changeName">修改名称</button>
  {{ changeAge }}
</template>
<script setup>
import {storeToRefs} from 'pinia'
import { useStore } from "../stores";
const store = useStore();
let { name ,age ,changeAge } = storeToRefs(store);
const changeName = () => {
  store.$patch(state=>{
    state.name = 'John';
    state.age = 23;
  })
}
</script>

3、actions的使用

router配置:

import { defineStore } from 'pinia';
export const useStore = defineStore('storeId',{
    state:()=>{
        return {
            key:0,
            name:'Tony',
            age:24,
            gender:'boy'
        }
    },
    getters:{
        changeAge(){
            return this.age + 1;
        }
    },
    actions:{  // 用于写方法
        upAge(val){
            this.age += val;
        }
    }
})
<!--显示-->
<template>
  {{ name }}
  {{ age }}
  <button @click="changeName">修改名称</button>
  <button @click="changebtn">年龄增加</button>
  {{ changeAge }}
</template>
<script setup>
import {storeToRefs} from 'pinia'
import { useStore } from "../stores";
const store = useStore();
let { name ,age ,changeAge } = storeToRefs(store);
const changeName = () => {
  store.$patch(state=>{
    state.name = 'John';
    state.age = 23;
  })
}
const changebtn = ()=>{
  store.upAge( 200 );  // 年龄上+200
}
</script>

4、分模块

例:

<!--主页面-->
<template>

</template>
<script setup>
import { user } from "../stores/user";
import { shop } from "../stores/shop";
const userStore = user();
const userShop = shop();
</script>
// user.js
import {defineStore} from "pinia";
export const user =defineStore('user',{
    state:()=>{
        return{
            key:0,
            name:'Tony',
            age:24,
            gender:'boy'
        }
    },
    getters:{
    },
    actions:{
    }
})
// shop.js
import {defineStore} from "pinia";
export const shop =defineStore('shop',{
    state:()=>{
        return{
            shopList:['显卡','固态','内存条']
        }
    },
    getters:{
    },
    actions:{
    }
})

5、Pinia持久化存储

下载插件:

npm i pinia-plugin-persist --save

插件引入:

import {createPinia} from 'pinia';
// 引入Pinia的持久化存储插件
import piniaPluginPersist from 'pinia-plugin-persist'
const store = createPinia()
// 使用插件
store.use(piniaPluginPersist)
export default store

main.ts配置

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
import store from './stores'
createApp(App).use(store).use(router).mount('#app')

十五、设置代理解决跨域问题

1、设置代理

// vite.config.ts 文件
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/dist/vite'
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
      vue(),
      AutoImport({
          imports:['vue','vue-router']
      })
  ],
  server:{
        proxy:{
            '/api':'https://blog.csdn.net/qq_39753433?spm=1011.2266.3001.5343(目标链接)'
        }
  }
})

2、axios二次封装

import axios from "axios";
// 创建axios对象
const service = axios.create();
// 请求拦截器
service.interceptors.request.use(config =>{
    return config;
},error => {
    Promise.reject(error);
});
// 响应拦截器
service.interceptors.response.use(response =>{
    // 判断code码
    return response.data;
},error => {
    return Promise.reject(error);
});

export default service;

3、解耦合

import request from "../utils/request";
export function getSliders(  ){  // 解耦合
    return request({
        url:'/api/slider/getSliders'
    })
}

4、使用

<template>

</template>
<script setup>
import {getSliders} from "../api/slider.js";
import {onBeforeMount} from "vue";  // 选择生命周期
onBeforeMount(()=>{
  getSliders().then(res=>{
    console.log( res );
  })
})
</script>

如果能解决您的问题,记得收藏+关注呀!!!

创作时间:2022.9.7

文章编号YU.NO.6

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值