vue3中的插槽和其他

默认插槽

使用自定义组件,在组件标签对中添加内容

vue 复制代码
<slot-child>this is parent slot</slot-child>

中间添加的内容会展示到自定义组件中的 <slot>默认内容</slot> 标签中,如果没传内容则展示默认内容

具名插槽

子组件中可以有多个坑位让父组件定义内容,使用<slot name="s1"> 不同的name来做坑位的区分

Vue 复制代码
<slot name="s1">默认内容</slot>
<slot name="s2">默认内容2</slot>

父组件可以在自定义组件中,定义属性v-slot:name 来填充name指定的坑位,可以简写为#name

vue 复制代码
<slot-child #s2 或者 v-slot:s2>
    this is s2
</slot-child> 

多个坑位的时候,可以在template中指定坑位名称来定义坑位内容

vue 复制代码
<template>
   <slot-child>
      <template #s2>this is s1</template>
      <template #s1>this is s2</template>
   </slot-child> 
</template>

作用域插槽

数据在子组件中,但是需要在父组件中决定子组件展示成什么样

首先子组件中定义了数据,以及slot坑位,将数据作为slot的prop

vue 复制代码
<template>
    <div>
       <h1>slot child</h1>
        <slot name="s1" :users="users"></slot>
        <slot name="s2" :users="users"></slot>
    </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
const users = ref([
    { name: '张三', age: 18 },
    { name: '李四', age: 20 },
    { name: '王五', age: 22 }
])
</script>

然后在父组件中接收prop数据

vue 复制代码
<template #s1="props"> // 子组件的prop属性都会在父组件的props中接收

父组件示例如下

vue 复制代码
<template>
   <slot-child>
      this is s1,接收子组件所有的prop到props中,从props中拿到users数据遍历,同时可以给插槽起个名字(具名插槽)
      <template #s1="props"> // 等同于 v-slot:s1="props"
         <ul>
            <li v-for="user in props.users" :id="user.name">{{user.name}} - {{user.age}}</li>
         </ul>
      </template>
   </slot-child> 
   <slot-child>
      this is s2
      <template #s2="props">
         <ol>
            <li v-for="user in props.users" :id="user.name">{{user.name}} - {{user.age}}</li>
         </ol>
      </template>
   </slot-child> 
</template>

shallowRef & shallowReactive

先看下ref的示例

vue 复制代码
<template>
    <div>
        <h1>当前求和为 {{sum}}, name:{{person.name}}  age:{{person.age}}</h1>
        <button @click="changeSum">sum+1</button>
        <button @click="changeName">changeName</button>
        <button @click="changeAge">changeAge</button>
        <button @click="changePerson">changePerson</button>
    </div>
</template>

<script setup lang="ts">
    import { ref } from 'vue'
    let sum = ref(0)
    let person = ref({
        name: 'why',
        age: 18
    })
    function changeSum() {
        sum.value += 1
    }
    function changeName() {
        person.value.name += '~'
    }
    function changeAge() {
        person.value.age += 1
    }
    function changePerson() {
        person.value = { name: 'kobe', age: 38 }
    }
</script>

将ref改为shallowRef,我们发现sum能改,person中的内容改了没反应,能改整个person,说明shallowRef只对第一层响应式有效

typescript 复制代码
import { shallowRef } from 'vue'
let sum = shallowRef(0)
let person = shallowRef({
    name: 'why',
    age: 18
})

reactive的示例

vue 复制代码
<template>
    <div>
        <h1>当前汽车是 {{car}}</h1>
        <button @click="changeName">changeName</button>
        <button @click="changeColor">changeColor</button>
        <button @click="changeSize">changeSize</button>
    </div>
</template>

<script setup lang="ts">
  	// reactive对象没办法直接修改car,只能使用Object.assign来改内容
    import { reactive } from 'vue'
    let car = reactive({
        name: '奔驰',
        options: {
            color: "red",
            size: "big"
        }
    })
    function changeName() {
        car.name = '宝马'
    }
    function changeColor() {
        car.options.color = 'blue'
    }
    function changeSize() {
        car.options.size = 'small'
    }
</script>

改为shallowReactive之后,只有修改名字生效。

通过shallowXXX来绕开深度响应,浅层次api创建的状态只在其顶层做响应式,避免每一个都做响应式带来的性能成本

readonly & shallowReadonly

写一个sum求和示例

vue 复制代码
<template>
    <div>
        <h1>求和为:{{sum}}</h1>
        <button @click="changeSum">sum+1</button>
    </div>
</template>

<script setup lang="ts">
    import { ref } from 'vue'
    const sum = ref(0)
    function changeSum() {
        sum.value++
    }
</script>

只读示例如下

vue 复制代码
<template>
    <div>
        <h1>求和为:{{sum}}  sum2: {{sum2}}</h1>
        <button @click="changeSum">sum+1</button>
        <button @click="changeSum2">sum2+1</button>
    </div>
</template>

<script setup lang="ts">
    import { reactive, readonly, ref } from 'vue'
    const sum = ref(0)
    // sum的值改变,sum2的值会跟着改变
    let sum2 = readonly(sum) // 这里sum2是和sum绑定的,所以sum的值改变,sum2也会跟着改变
    function changeSum() {
        sum.value++
    }
    function changeSum2() {
        // sum2.value++  这句会报错,sum2是只读的,不能修改
    }

    // reactive也可以实现只读
    let car = reactive({
        name: '奔驰',
        options: {
            price: 100,
            color: '黑色'
        }
    })
    let car2 = readonly(car) // car2和car是绑定的,所以car的值改变,car2也会跟着改变, 但是car2中的内容是不能修改的
</script>

shallowReadonly是浅只读,只能修改响应式对象第一层的属性

vue 复制代码
<script setup lang="ts">
    import { reactive, readonly, ref, shallowReadonly } from 'vue'
    // reactive也可以实现只读
    let car = reactive({
        name: '奔驰',
        options: {
            price: 100,
            color: '黑色'
        }
    })
    let car2 = shallowReadonly(car) // car2是浅只读,只能修改car2的属性,不能修改嵌套属性的值
</script>

toRaw & markRaw

toRaw根据响应式对象生成一个原始对象

vue 复制代码
<template>
    <div>
        <h1>person:{{person}}</h1>
        <button @click="changeAge">age+1</button>
    </div>
</template>

<script setup lang="ts">
    import { reactive, ref, toRaw } from 'vue'
    let person = reactive({
        name: '奔驰',
        age: 18
    })
    // 获取响应式对象的原始对象,也就是非响应式对象,仅支持展示,不能修改原始对象的数据结构。
    let person2 = toRaw(person) 
    function changeAge() {
        person.age++
    }
</script>

markRaw 标记一个对象,使其永远不能成为响应式的

vue 复制代码
<template>
    <div>
        <h1>car{{car}}</h1>
        <button @click="changeCar">age+1</button>
    </div>
</template>

<script setup lang="ts">
import { markRaw } from 'vue';

// 标记为原始对象,不会被代理
let car = markRaw({
    name: 'benz',
    age: 10,
})

// 修改原始对象,不会触发视图更新
function changeCar() {
    car.age += 1;
}

</script>

customRef

场景:使用ref定义的数据修改之后会立即修改界面展示,如果我们想改了数据之后延迟2s再修改界面,就需要customRef

customRef一般会封装为hooks,创建 src/hooks/useMsgRef.ts

typescript 复制代码
import { customRef } from 'vue';
// 接收初始值initval 和 延迟时间 delay,都有默认值
export default function (initval: number = 0, delay: number = 1000) {
    let timer;
    let msg = customRef((track, trigger) => {
        return {
            get() {
              // track告诉vue数据很重要,一旦数据变化就要去更新
                track()
                return initval;
            },
            set(newValue) {
              // 每次设置都清楚上次的timer,避免set的时候开启很多timer
                clearTimeout(timer);
                timer = setTimeout(() => {
                    console.log('newValue', newValue);
                    initval = newValue;
                  // 通知vue数据变化了
                    trigger()
                }, delay);
            }
        }
    })
    return { msg }
}

使用者

vue 复制代码
<template>
    <div>
        <h1>{{msg}}</h1>
        <!-- 通过输入框修改msg值 -->
        <input type="text" v-model="msg">
    </div>
</template>

<script setup lang="ts">
import useMsgRef from '@/hooks/useMsgRef';
const msgRef = useMsgRef(1, 2000)
const {msg} = msgRef;
</script>

Teleport

一种能将我们组件html结构移动到指定位置的技术

示例:一个弹窗展示到视图中央

App.vue

vue 复制代码
<script setup>
import Modal from './Modal.vue';
</script>

<template>
<div class="outer">
  <h1>我是app组件</h1>
  <img src="https://ts1.tc.mm.bing.net/th/id/OIP-C.OFMlQdJWRGUjT2PNEWN00AHaEK?rs=1&pid=ImgDetMain&o=7&rm=3" alt="">
  <br>
   <Modal />
</div>
</template>

<style scoped>
.outer {
  background-color: #ddd;
  border-radius: 10px;
  box-shadow: 0 0 10px;
  padding: 5px;
  width: 400px;
  height: 400px;
  /* 这句filter会导致 Modal组件的postion:fixed失效,因此使用teleport解决 */
  filter: saturate(0%); 
}
img {
  width: 180px;
}
</style>

Modal.vue

vue 复制代码
<template>
    <button @click="isShow=true">展示弹窗</button>
    <div class="modal" v-show="isShow">
        <h1>我是弹窗标题</h1>
        <p>我是弹窗内容</p>
        <button @click="isShow=false">关闭</button>
    </div>
</template>

<script setup>
import { ref } from 'vue'
let isShow = ref(false)

</script>

<style lang="scss" scoped>
.modal {
    width: 200px;
    height: 170px;
    background-color: skyblue;
    border-radius: 10px;
    padding: 5px;
    box-shadow: 0 0 10px;
    text-align: center;
		/* 相对居中,但是父容器的filter会导致失效 */
    position: fixed;
    left: 50%;
    top: 20px;
    margin-left: -100px;
}
</style>

使用teleport解决弹窗位置问题

vue 复制代码
<!-- to代表将代码瞬移到哪个标签下 -->
<teleport to='body'>
    <div class="modal" v-show="isShow">
        <h1>我是弹窗标题</h1>
        <p>我是弹窗内容</p>
        <button @click="isShow=false">关闭</button>
    </div>    
</teleport>

Suspense

等待异步组件时渲染一些额外内容,提高用户体验

使用场景:子组件setup里包含异步任务,异步任务返回的内容还要展示

示例先安装网络请求库npm i axios,使用网络请求作为异步

Child.vue

vue 复制代码
<script setup>
import axios from 'axios';
const {data:{content}} = await axios.get('/api/rand.qinghua')
console.log(content);
</script>

<template>
<div class="child">
 <h1>我是child组件</h1>
 <div>{{content}}</div>
</div>
</template>

<style scoped> 
.child {
  background-color: skyblue;
  border-radius: 10px;
  padding: 10px;
  box-shadow: 0 0 10px;
}
</style>

App.vue

vue 复制代码
<script setup>
import Child from './Child.vue'
</script>

<template>
<div class="app">
  <h1>我是app组件</h1>
  <Suspense>
    <template v-slot:default>
      <Child></Child>
    </template>
     <template v-slot:fallback>
      <h1>加载中...</h1>
    </template>
  </Suspense>
 
</div>
</template>

<style scoped> 
.app {
  background-color: #ddd;
  border-radius: 10px;
  padding: 10px;
  box-shadow: 0 0 10px;
}
</style>

全局api转移到应用对象

通过app.component注册全局组件,在任何地方都可以直接使用,不需要再import

tsx 复制代码
const app = createApp(App)
app.component('Hello', Hello)

通过app.config.globalProperties.x设置全局属性

typescript 复制代码
app.config.globalProperties.x = 99

使用的时候ts会报类型找不到,在main.ts文件中(不是js文件)添加自定义属性类型即可结论类型问题

typescript 复制代码
declare module 'vue' {
    interface ComponentCustomProperties {
        x: number
    }
}

注册全局指令

typescript 复制代码
注册
app.directive('beauty', (element, {value})=>{
    element.innerText += ` --- ${value*2}`
    element.style.color = "yellow"
    element.style.backgroundColor = "green"
})
app.mount('#app')

使用:
<div class="child">
 <h1>我是Hello组件</h1>
 <div v-beauty="x">app.config.globalProperties.x : {{x}}</div>
</div>

其他

typescript 复制代码
app.mount('#app')
app.unmount()
app.use(router)
相关推荐
jqq6663 小时前
解析ElementPlus打包源码(二、buildFullBundle)
前端·javascript·vue.js
知识分享小能手7 小时前
React学习教程,从入门到精通, React 入门指南:React JSX 语法知识点详解及案例代码(8)
前端·javascript·vue.js·学习·react.js·前端框架·anti-design-vue
webYin14 小时前
vue2 打包生成的js文件过大优化
前端·vue.js·webpack
Gazer_S15 小时前
【Element Plus 表单组件样式统一 & CSS 文字特效实现指南】
前端·css·vue.js
小薛博客17 小时前
23、Jenkins容器化部署Vue应用
运维·vue.js·jenkins
用户516816614584119 小时前
Uncaught ReferenceError: __VUE_PROD_HYDRATION_MISMATCH_DETAILS__ is not defined
前端·vue.js
熊猫片沃子19 小时前
Vue 条件与循环渲染:v-if/v-else 与 v-for 的语法简介
前端·vue.js
拜无忧20 小时前
【教程】vue+vite+ts创建一个最新的高性能后台项目架构
vue.js·typescript·vite
蝶开三月20 小时前
从卡顿到丝滑:3 个实战场景教你搞定代码性能优化
javascript·vue.js·性能优化