vue3知识记录-高级篇

67 阅读1分钟

优化性能的指令

//`Vue.js` 提供了一个指令,以便只渲染一次元素和组件,并且跳过以后的更新
<script setup lang="ts">
import { ref } from "vue"

const count = ref(0)

setInterval(() => {
    count.value++
    console.log(count.value)
}, 1000)
</script>

<template>
    <span v-once>使它从不更新: {{ count }}</span>
</template>

切换器

<script setup lang='ts'>
import { ref } from "vue"
/**
 * Implement a composable function that toggles the state
 * Make the function work correctly
*/
function useToggle(open: boolean) {
    const stateRef = ref(open)
    const toggle=() => {
        stateRef.value = !stateRef.value
    }
    return [stateRef, toggle]
}

const [state, toggle] = useToggle(false)
</script>

<template>
    <p>State: {{ state ? 'ON' : 'OFF' }}</p>
    <p @click="toggle">
        Toggle state
    </p>
</template>

实现until函数

<script setup lang='ts'>
import {ref,watchEffect,type Ref } from "vue"

const count = ref(0)

/**
 * 实现`until`函数
*/

function until(initial:Ref<number>) {
    function toBe(value:number) {
        return new Promise((resolve) => {
            const stop=watchEffect(() => {
                console.log('我进来看了');
                if((initial.value === value)){
                    stop()
                    resolve(value);
                }
            });
        });
        
    }

    return {
        toBe,
    }
}

async function increase() {
    count.value = 0
    setInterval(() => {
        count.value++
    }, 1000)
    await until(count).toBe(3)
    console.log(count.value === 3) // 确保输出为true
}
increase()
</script>


实现计数器

<script setup lang='ts'>
import {ref} from 'vue'
interface UseCounterOptions {
    min?: number
    max?: number
}

/**
 * 实现计数器函数,确保功能正常工作
 * 1. 加 (+)
 * 2. 减 (-)
 * 3. 重置 
 * 4. 最小值 & 最大值 选项支持
*/
function useCounter(initialValue = 0, options: UseCounterOptions = {}) {
    const count=ref(initialValue)
    const {min,max}=options
    const inc=()=>{
        if(max!=undefined&&count.value>=max){
            count.value=max
        }else{
            count.value++
        }
    }
    const dec=()=>{
        if(min!=undefined&&count.value<=min){
            count.value=min
        }else{
            count.value--
        }
    }
    const reset=()=>{
        count.value=initialValue
    }
    return{
        count,
        inc,
        dec,
        reset
    }
}

const { count, inc, dec, reset } = useCounter(0, { min: 0, max: 10 })
</script>
<template>
    <p>{{count}}</p>
    <button @click="inc"></button>
    <button @click="dec"></button>
    <button @click="reset">重置</button>
</template>


一个简易的locastorage读写

<script setup lang='ts'>

import { ref, watchEffect } from "vue"

/**
 * 实现`useLocalStorage`函数
*/
function useLocalStorage(key: string, initialValue: any) {
    const value = ref(localStorage.getItem(key)??initialValue)
    watchEffect(()=>{
        localStorage.setItem(key,value.value)
    })
    return value
}

const counter = useLocalStorage("counter", 0)

// 我们可以通过触发`counter`的`getter`来获取本地存储的值
console.log(counter.value)
setInterval(()=>{
    counter.value++
},1000)
// 同样地,我们也可以通过触发`counter`的`setter`来设置本地存储的值

</script>
<template>
    <p>{{ counter }}</p>
</template>


自定义指令

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

const state = ref(false)

/**
 * 实现一个自定义指令,让元素获取焦点
 * 确保当切换`state`时,元素随着状态值获取/失去焦点
 *
*/
const VFocus = {
    updated:(el:HTMLElement,binding:{value:boolean})=>{
        binding.value?el.focus():el.blur()
    }
}
console.log(VFocus)
setInterval(() => {
    state.value = !state.value
}, 2000)

</script>

<template>
    <input v-focus="state" type="text">
</template>

问题 遗留 由于自定义指令未被读取。导致编辑器飘红问题

使用自定义指令实现防抖点击指令

<script setup lang='ts'>
import { ref } from 'vue';
/**
 * 实现以下自定义指令
 * 确保在一定时间内当快速点击按钮多次时只触发一次点击事件
 * 你需要支持防抖延迟时间选项, 用法如 `v-debounce-click:ms`
 *
*/
const VDebounceClick = {
    mounted:(el:HTMLElement,bingding:{arg:number})=>{
        const isClick=ref(true);
        el.addEventListener('click',()=>{
            if(isClick.value){
                isClick.value=false
                setTimeout(()=>isClick.value=true,bingding.arg)
                console.log('可以执行');
            }else{
                onClick()
            }
        })
    },
}

function onClick() {
    console.log("Only triggered once when clicked many times quicky")
}
console.log(VDebounceClick)
</script>

<template>
    <button v-debounce-click:1000="onClick">
        Click on it many times quickly
    </button>
</template>

函数式组件h函数的使用

<script setup lang='ts'>

import { ref, h } from "vue"

/**
 * 实现该函数式组件 :
 * 1. 使用`list`数据渲染列表元素 (ul/li)
 * 2. 当点击列表子元素时,将其文本颜色更改为红色
 * h函数的使用 当第三个参数不是插槽时 可以省略第二个props参数
*/
type props={
    list:{name:string}[],
    onToggle(index:number):void,
    activeIndex:number
}
const ListComponent = (props:props) => {
    console.log(props)
    return h('ul', props.list.map((element:{name:string},index:number) => {
        return h('li', {
            style: { color:  activeIndex.value==index?'red':null},
            onClick:() => props.onToggle(index),
        }, element.name)
    }))
}

const list = [{
    name: "John",
}, {
    name: "Doe",
}, {
    name: "Smith",
}]

const activeIndex = ref(0)

function toggle(index: number) {
    activeIndex.value = index
}

</script>

<template>
    <list-component :list="list" :active-index="activeIndex" @toggle="toggle" />
</template>


函数式组件练习

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

const onClick = () => {
    console.log('onClick')
}
const MyButton=(props,{ slots })=>{
    console.log(props)
    return h('button',{
        onClick:props.onCustomClick,
        disabled:props.disabled
    },slots.default())
}
</script>
<template>
    <MyButton :disabled="false" @custom-click="onClick">
        my button
    </MyButton>
</template>

按键修饰符和系统修饰符 exact的使用

<template>
    <!-- 添加按键修饰符让即使 AltShift 被一同按下时也会触发 -->
    <button @click.ctrl="onClick1">A</button>

    <!-- 添加按键修饰符让有且只有 Shift 被按下的时候才触发 -->
    <button @click.shift.exact="onCtrlClick">A</button>

    <!-- 添加按键修饰符让没有任何系统修饰符被按下的时候才触发 -->
    <button @click.exact="onClick2">A</button>
</template>
<script setup lang="ts">
const onClick1=()=>{
    console.log('添加按键修饰符让即使 Alt 或 Shift 被一同按下时也会触发');
    
}
const onCtrlClick=()=>{
    console.log('添加按键修饰符让有且只有 Shift 被按下的时候才触发 ');
    
}
const onClick2=()=>{
    console.log('添加按键修饰符让没有任何系统修饰符被按下的时候才触发');
    
}
</script>

监听鼠标坐标

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

// Implement ...
function useEventListener(target:Window, event:string,callback) {
    target.addEventListener(event,callback)
}

// Implement ...
function useMouse() {
    const x=ref(0)
    const y=ref(0)
    useEventListener(window, "mousemove",(event)=>{
        x.value=event.clientX
        y.value=event.clientY
    })
    return {x,y}
}
const { x, y } = useMouse()

</script>

<template>
    Mouse position is at: {{ x }}, {{ y }}
</template>

toRaw 和markRaw的使用

//toRaw 返回这个代理对象的原始对象
//markRaw 将一个对象标记为不可转换为代理
<script setup lang="ts">
import { reactive, isReactive,toRaw,markRaw } from "vue"

const state = { count: 1 }
const reactiveState = reactive(state)

/**
 * 修改以下代码使输出为true
*/
console.log(toRaw(reactiveState) === state)

/**
 * 修改以下代码使输出为false
*/
const info = markRaw({ count: 1 })
const reactiveInfo = reactive(info)

console.log(isReactive(reactiveInfo))

</script>

<template>
    <div>
        <p>
            {{ reactiveState.count }}
        </p>
    </div>
</template>