精读VUE3文档

vue一些计算属性及watch监听的一些内容

计算属性

getter 和 setter

const firstName = ref('Kate')
const lastName = ref('Jen')

const fullName = computed({
    // getter
    get(){
        return firstName.value + ' ' + lastName.value;
    },
    // setter
    set(newValue){
        // Es6结构赋值
        [firstName.value,lastName.value] = newValue.split(' ');
    }
})

fullName.value = "John Lee" // 此处会触发Setter函数.firstName和lastName也会相应更新

computed具有缓存的作用

计算属性会追踪依赖,如果依赖没有变动,将自动返回缓存中的数据,而调用方法每次重新渲染的的时候都会重新执行,会消耗更多的性能


watch 监听

1.watch监听源的几种形式

const num = ref(2)
const obj = ref({key:1})

watch(num,()=>{}) // 监听单个的 ref响应式数据
watch(()=>obj.key,(newVal)=>{}) // 监听一个getter函数
watch(()=>obj.value.key + num.value,(sum)=>{}) // 监听一个getter函数
watch([num,obj],(newVal1,nweVal2)=>{}) // 监听一个数组


2.watch深度监听和立即执行

const num = ref(2)
const obj = ref({key:1,deepKey:{num:3}})
const updateNum = ()=>{
    return num.value+obj.value.deepKey.num
}

watch(obj,()=>{
    // 达到深度监听
},{deep:true}) 


watch(obj,()=>{
    // 立即执行
},{immediate:true}) 


3.watchEffect 会在监听的依赖数据发生变化时直接调用,而不需要指定明确的监听源

const id = ref(1)
const data = ref()

watchEffect(async ()=>{
    const res = await fetch(`http://api.details/${id}`)
    data.value = res
})


4.watch 回调函数的触发机制, watchEffect 后置刷新 和 watchPostEffect


// 当响应数据发生变化时,dom和watch同时监听到。此时,watch的回调将会vue组件更新之前被调用。获取组件dom数据是组件更新之前的
watch(somRefProperty,()=>{
    let dom = document.querySelector('...') // 数据更新之前的dom 
})

// 后置刷新的方式。配置flush为post
watch(somRefProperty,()=>{
    let dom = document.querySelector('...') // 数据更新之后的dom 
},{flush:'post'})
watchEffect(()=>{
    let dom = document.querySelector('...') // 数据更新之后的dom 
},{flush:'post'})

// 后置刷新的watchEffect 可以简写为 watchPostEffect
watchPostEffect(()=>{
    let dom = document.querySelector('...') // 数据更新之后的dom 
})


组件

1.component元素 动态组件

import componentA from "./xx.vue"
import componentB from "./xx.vue"

const currentComponent = computed(()=>route.query.isA?componentA:componentB)

<component :is="currentComponent"></component>

<component is="div"></component>

<component is="a" href="/a/b"></component>


2. 组件元素 位置限制的问题处理

import customTr from './tr/vue'

<table>
    <custom-tr></custom-tr>  此处使用自定义组件,会被忽略掉
</table>   

// ---------------- 正确使用  ---------------
<table>
    <tr is="vue:custom-tr"></tr>  此处使用自定义组件,会被忽略掉
</table>   


3. 组件的引用和获取(ref)

<script setup>
import child from './child'
const childRef= ref(null)

onMounted(()=>{
    // 在挂载之后获取子组件中的数据
    const data = childRef.data
})
</script>
<template>
    <child ref="childRef"></child>
</template>


<script setup>
const data = ref('我是子组件的数据')
const fn = ()=>{
    alert()
}
// 注意:在使用了 script setup 组合是api形式的组件中,子组件的数据是私有的不允许父组件访问。
// 必须使用 defineExpose 将数据显式的暴露出去才能被父组件访问的到
defineExpose({
    data,
    fn
})
</script>
<template>
   <span>我是子组件,数据:{{data}}</span>
</template>


4. 组件透传-attribute

父组件在引用组件上属性(class,style,@click等)会被透传给子组件(子组件根节点可以使用$attrs接受到)

// 父组件
import child from './child'
<child class="test" @click="fn"></child>

//子组件会被渲染成这样(这个div是子组件的根节点)
<button class="test" @click="fn"></button>

// 子组件中使用$attrs
<button>{{$attrs.class}}</button>

//取消属性透传
defineOptions({
  inheritAttrs: false
})

//如果不是根节点怎么使用透传的属性?使用v-bind 配和 inheritAttrs: false 即可实现
<div class="child-components-wrap">
  <button class="btn" v-bind="$attrs">click me</button>
</div>
defineOptions({
  inheritAttrs: false
})

// 子组件有多个根节点-需要显示的绑定透传的attrs
<header></header>
<main v-bind="$attrs"></main>
<footer></footer> 


5. 组件js如何透传-attribute

<script setup>
    import { useAttrs } from 'vue'
    const attrs = useAttrs()
</script>


组合式函数

  • 组合式函数是vue3.0中新增的api,可以用来代替vue2.x中的mixin
const useMouse = () => {
  const state = ref({
    x: 0,
    y: 0
  })
  const update = (e: MouseEvent) => {
    state.value.x = e.pageX
    state.value.y = e.pageY
  }
  const onMouseMove = (e: MouseEvent) => {
    update(e)
  }
  return {
    state,
    update,
    onMouseMove
  }
}

// 使用useMouse
const { state, update, onMouseMove } = useMouse()

// 异步实现
// fetch.js
import { ref, watchEffect, toValue } from 'vue'

export function useFetch(url) {
  const data = ref(null)
  const error = ref(null)

  const fetchData = (dt) => {  
    //  toValue() 是一个在 3.3 版本中新增的 API。它的设计目的是将 ref 或 getter 规范化为值
    fetch(toValue(url))
      .then((res) => res.json())
      .then((json) => (data.value = json))
      .catch((err) => (error.value = err))
    }

  watchEffect(() => {
    // reset state before fetching..
    fetchData(url)
  })

  return { data, error }
}


自定义指令

自定义指令是vue2.x中使用的api,在vue3.0中进行了升级,使用更加简单

1.注册自定义指令

// 全局注册
const app = createApp({})
app.directive('focus',{
//
})


2.指令的钩子

const myDirective = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  /**
   * @el  指令所绑定的元素 这里可以直接操作DOM
   * @binding {value,oldValue,arg,  modifiers,instance:使用该指令的组件实例,dir}
   * @vnode 代表绑定元素的底层 VNode
   * @prevVnode 代表之前的渲染中指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。
  */
  created(el, binding, vnode, prevVnode) {
    // 下面会介绍各个参数的细节
  },
  // 在元素被插入到 DOM 前调用
  beforeMount(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件更新前调用
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载后调用
  unmounted(el, binding, vnode, prevVnode) {}
}

// 示例
<button v-click:double.stop="fn">按钮</button>
// binding 对象
{
    arg: 'double',
    modifiers: { stop: true },
    value: fn,
    oldValue: undefined,///* 上一次更新时 `fn` 的值 */
    instance: Vue 实例,
}


// 简化形式
<input v-color="color">按钮</input>
app.directive('color',(el,binding)=>{
     // 这会在 `mounted` 和 `updated` 时都调用
     el.style.color = binding.value
})

休息一下吧~