1.setup

1.setup:Vue3的新配置项,值为一个函数.

2.setup时所有Composition API(组合APi)'表演舞台'
3.组件中所用到的:数据、方法等等,均要配置在setup中
4.setup函数的两种返回值:

  • 若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用
  • 若返回一个渲染函数,则可以自定义渲染内容.

5.注意点:

  • 1.尽量不要与Vue2配置混用

    • Vue2配置(data、methods、computed...)中可以访问setup中的属性、方法
    • 但在setup中不能访问到Vue2配置(data、methods、computed...)
    • 如果有重名,setup优先
  • 2.setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性.

6.setup的语法糖 在<script></script>标签里面 加上一个setup 可以不用return
直接在script中写我们的代码

<script setup>
import { reactive } from 'vue'
const obj = reactive({
    person:'张三'
})
</script>

2.ref函数

  • 作用:定义一个响应式数据
  • 语法:const xxx=ref(initValue)

    • 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)
    • JS中操作数据:xxx.value
    • 模板中读取数据,不需要.value,直接<div>{{xxx}}</div>
  • 备注

    • 接收的数据可以是:基本类型,也可以是对象类型
    • 基本类型的数据:响应式依然是靠Object.defineProperty()getset完成的
    • 对象类型的数据:内部求助了Vue3的一个新函数---reactive函数

3.reactive函数

  • 作用:定义一个对象类型的响应式数据(基本类型别用他,用r ef函数)
  • 语法:const 代理对象=reactive(被代理对象)接收一个对象(或数组),返回一个代理器对象(proxy对象)
  • reactive定义的响应式数据时深层次的
  • 内部基于ES6的Proxy实现,通过代理对象操作对象内部数据都是响应式的

4.Vue3中的响应式原理

Vue2.x的响应式

  • 实现原理:

    • 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截、数据劫持
    • 数组类型:通过重写更新数组的一系列方法实现拦截.(对数据的变更方法进行了包裹).
Object.defineProperty(data,'count',{
get(){},
set(){},
})
  • 存在问题:

    • 新增属性、删除属性,界面不会更新.
    • 直接通过下标修改数组,界面不会自动更新.(.$set .$delete去处理)

Vue3的响应式


    new Proxy(data, {
      // 拦截读取属性值
      get(target, prop) {https://www.baiyeovo.cn/admin/write-post.php?cid=290#wmd-editarea
        return Reflect.get(target, prop)
      },
      // 拦截设置属性值或添加新属性
      set(target, prop, value) {
        return Reflect(target, prop, value)
      },
      //拦截删除属性
      deleteProperty(target, prop) {
        return Reflect.deleteProperty(target, prop)
      }
    })

    proxy.name = 'tom'

4.reactive对比ref

  • 从定义数据角度对比:

    • ref用来定义:基本数据类型
    • reactive用来定义:对象(或数组)类型数据
    • 备注:ref也可用来定义对象(或数组)类型数据,它内部会自动通过reactive转为代理对象.
  • 从原理角度对比

    • ref通过Object.defineProperty()getset来实现响应式(数据劫持).
    • reactive通过使用Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据.
  • 从实用角度来对比

    • ref定义数据:操作数据需要 .value,读取数据时模板中直接读取不需要.value
    • reactive定义的数据:操作数据与读取数据,均不需要.value

5.setup的两个注意点

  • setup执行的时机

    • beforeCreated之前执行一次,thisundefined.
  • setup的参数

    • props:值为对象,包含:组件外部传递过来,且组件内部 声明接收了的属性
    • context:上下文对象

      • attrs:值为对象,包含:组件外部传递过来,但是没有在props配置中声明的属性,相当于this.$attrs.
      • slot:收到的插槽内容,相当与this.$slot.
      • emit:分发自定义事件的函数,相当于this.$emit.

6.计算属性与监视

  • 与Vue2.x中的computed配置功能一致
  • 写法
import {computed} from 'vue'
  setup() {
    //数据
    let person = reactive({
      firstName: '张',
      lastName: '三'
    })
    //计算属性--简写(没有考虑计算属性被修改的情况 )
    // person.fullNAme=computed(()=>{
    //   return person.firstName+ '-'+ person.lastName
    // })
    //计算属性--完整写法(考虑读和写 )
    person.fullNAme = computed({
      get() {
        return person.firstName + '-' + person.lastName
      },
      set(value) {
        const nameArr = value.split('-')
        person.firstName = nameArr[0]
        person.lastName = nameArr[1]
      }
    })
    return {
      person
    }
  }
  • 与Vue2.x中watch配置功能一致
  • 两个小坑:

    • 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置无效)
    • 监视reactive定义的响应式数据中的某个属性时:deep配置有效

总结:

监测基本数据类型(有oldValue)  监测reactive定义的数据(没有oldValue)  
    //情况一:监测ref所定义的一个响应式数据
    watch(sum,(newVal,oldVal)=>{
      console.log('sum变了',newVal,oldVal)
    },{immediate:true})

    //情况二:监视ref所定义的多个响应式数据
       watch([sum,msg],(newVal,oldVal)=>{
    console.log('person变了',newVal,oldVal)
       },{immediate:true})

    // 情况三:监视reactive所定义的一个响应式数据,
    // 1.注意:此处无法正确获取oldValue
    // 2.注意:强行开启了深度监视(deep配置无效)
    watch(person,(newVal,oldVal)=>{
      console.log('person变了',newVal,oldVal)
    },{deep:false})

    //情况四:监视react所定义的一个响应式数据中的某个属性
    watch(()=>person.age,(newVal,oldVal)=>{
      console.log('person的age变了',newVal,oldVal)
    })

    // 情况五:监视reactive所定义的一个响应式数据中的某些属性
    watch([()=>person.name,()=>person.age],(newVal,oldVal)=>{
      console.log('person的jname或age变化了',newVal,oldVal)
    })
    //特殊情况
    watch(()=>person.job,(newVal,oldVal)=>{
      console.log('person的job.变化了',newVal,oldVal)
    },{deep:true}) //此处由于监视的时reactive所定义的对象中的某个属性,所以deep配置有效

v-model语法糖

在vue2.0中v-mode语法糖简写的代码
<Son :value="msg" @input="msg=$event" />

在vue3.0中v-model语法糖有所调整:
<Son :modelValue="msg" @update:modelValue="msg=$event" />

总结vue3.0封装组件支持v-model的时候,父传子:modelValue 子传父 @update:modelValue

补充vue2.0的 xxx.sync 语法糖解析 父传子 :xxx 子传父 @update:xxx 在vue3.0 使用 v-model:xxx 代替。

<template>
  <div class="container">
    <h2>子组件 {{modelValue}} <button @click="fn">改变数据</button></h2>
  </div>
</template>
<script>
export default {
  name: 'Son',
  props: {
    modelValue: {
      type: Number,
      default: 0
    }
  },
  setup (props, {emit}) {
    const fn = () => {
      // 改变数据
      emit('update:modelValue', 100)
    }
    return { fn }
  }
}
</script>

watchEffect 函数

  • watch的套路是:既要指明监听的属性,又要指明监听的函数
  • watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性
  • watchEffect有点像computed:

    • computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值
    • watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值
watchEffect(()=>{
const x1=sum.value
const x2=person.age
console.log('watchEffect配置的回调执行了');
})

Vue3生命周期函数

Vue3.0 中可以继续使用Vue2.x中的生命周期钩子,但有两个被更名:

  • beforeDestroy改名为 beforeUnmount
  • destoryed改名为Unmounted
  • Vue3.0也提供了Composition API形式的生命周期钩子,与Vue2.x钩子对应关系如下:

    • beforecreated===>setup()
    • created===>setup()
    • beforeMount===>onBeforeMount
    • mounted===>onMonunt
    • beforeUpdate===>onBeforeUpdate
    • Updated===>onUpdate
    • beforeDestroy===>onBeforeUnmount
    • destoryed===>onUnmounted

自定义hook函数

  • 什么是hook ---本质是一个函数,把setup中使用的composition API 进行了封装
  • 类似于Vue2.x中的mixin
  • 自定义hook的优势:复用代码,让setup中的逻辑更清楚易懂

toRef

  • 作用:toRef是函数,转换响应式对象中某个属性为单独响应式数据,并且值是关联的。
  • 语法:`const name=toRef(person,'name')
  • 应用:要将响应式对象中的某个属性单独提供给外部使用
  • 扩展:toRefstoRef功能一致,但可以创建多个ref对象,语法:toRefs(person)

toRefs

  • toRefs是函数,转换响应式对象中所有属性为单独响应式数据,对象成为普通对象,并且值是关联的

其他Composition API

1.shallowReactive与shallowRef

  • shallowReactive:只处理对象最外层的响应式(浅响应式)
  • shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理
  • 什么时候使用?

    • 如果有一个对象数据,解构比较深,但变化时只是最外层属性变化===>shallowReactive
    • 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换===>shallowRef

2.readonly与shallowReadonly

  • readonly:让一个响应式数据变为只读(深只读)
  • shallowReadonly:让一个响应式数据变为只读(浅只读)
  • 应用场景:不希望数据被修改时

3.toRaw与markRaw

  • toRaw:

    • 作用:将一个由reactive生成的响应式对象转为普通对象
    • 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新
  • markRaw

    • 作用:标记一个对象,使其永远不会再成为响应式对象
    • 应用场景:
      1.有些值不应该被设置为响应式的,例如复杂的第三方类库等.
      2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能.

4.customRef

  • 作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制
  • 实现防抖效果:
<template>
  <div class="div">
    <input type="text" v-model="keyWord" />
    <h3>{{ keyWord }}</h3>
  </div>
</template>

<script>
import { customRef } from 'vue'
export default {
  setup() {
    //自定义一个ref--名为:myRef
    function myRef(value,delay) {
      let timer=null
      // console.log('myRef',value)
      return customRef((track, trigger) => {
        return {
          get() {
            console.log(`有人从myRef这个容器中读取了数据,我把${value}給它了`)
            track() //通知vue追踪calue的变化(提前和get商量一下,让他认为这个value是有用的)
            return value
          },
          set(newValue) {
            console.log(`有人从myRef这个容器中该为了:${newValue}`)
            clearTimeout(timer)
            timer = setTimeout(() => {
              value = newValue
              trigger() //通知vue重新解析模板
            },delay)
          }
        }
      })
    }
    let keyWord = myRef('hello',500)

    return {
      keyWord
    }
  }
}
</script>

<style scoped>
.div {
  width: 100px;
  height: 100px;
}
</style>

5.provide与inject

  • 作用:实现祖孙组件间通信
  • 套路:父组件有一个provide,子组件有一个inject选项来开始使用这些数据
  • 具体写法:
    1.祖组件中:

    setup(){
    .....
    let car=reactive({name:'奔驰',price:'40万'})
    provide('car',car)
    .....
    }

    2.孙组件中:

    setup(props,context){
    .....
    const car=inject('car')
    return{
    car}
    .......

6.响应式数据的判断

  • isRef:检擦一个值是否为一个ref对象
  • isReactive:检查一个对象是否由reactive创建的响应式代理
  • isReadonly:检查一个对象是否由readonly创建的只读代理
  • isProxy:检查一个对象是否由reactive或者readonly方法创建的代理

新的组件

1.Fragment

  • 在vue2中:组件必须有一个根标签
  • 在vue3中:组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中
  • 好处:减少标签层级,减少内存占用

2.Teleport

  • 什么时Teleport? ----Teleport是一种能够将我们组件html结构移动到指定位置的技术
<teleport top="移动位置">
   <div v-if="isShow" class="mask">
     <div class="dialog">
        <h3>我是一个弹窗</h3>
        <button @click="isShow=false">关闭弹窗</button>
     </div>
    </div>
<teleport>

3.Suspense

  • 等待异步组件渲染时一些后备内容,获得更好的用户体验

    • 异步引入组件
    import {defineAsyncComponent} from 'vue'
    const Child =defineAsyncComponent(()=>import(./component/Child.vue))
    • 使用Suspense包裹组件,并配置好defaultfallback
    <template>
      <div>
     <h3>我是App组件</h3>
     <Suspense>
       <template #default>
         <Child></Child>
       </template>
    
       <template v-slot:fallback>
         <h3>加载中....</h3>
       </template>
     </Suspense>
      </div>
    </template>

Vue3自定义指令的生命周期

app.directive('directiveName', {
    // 在绑定元素的 attribute 或事件监听器被应用之前调用, 在指令需要附加须要在普通的 v-on 事件监听器前调用的事件监听器时,这很有用
    created() {},
    // 当指令第一次绑定到元素并且在挂载父组件之前调用
    beforeMount() {},
    // 在绑定元素的父组件被挂载后调用
    mounted() {},
    // 在更新包含组件的 VNode 之前调用
    beforeUpdate() {},
    // 在包含组件的 VNode 及其子组件的 VNode 更新后调用
    updated() {},
    // 在卸载绑定元素的父组件之前调用
    beforeUnmount() {},
    // 当指令与元素解除绑定且父组件已卸载时, 只调用一次
    unmounted() {},
});

vue3获取dom元素

获取单个元素

<template>
  <div ref="myRef">获取单个DOM元素</div>
</template>

<script>
import { ref, onMounted } from 'vue';

export default {
  setup() {
    const myRef = ref(null);

    onMounted(() => {
      console.dir(myRef.value);
    });
 
    return {
      myRef
    };
  }
};
</script>

获取多个dom元素(一般用于获取数组)

<template>
  <div>获取多个DOM元素</div>
  <ul>
    <li v-for="(item, index) in arr" :key="index" :ref="setRef">
      {{ item }}
    </li>
  </ul>
</template>

<script>
import { ref, nextTick } from 'vue';

export default {
  setup() {
    const arr = ref([1, 2, 3]);

    // 存储dom数组
    const myRef = ref([]);

    const setRef = (el) => {
      myRef.value.push(el);
    };

    nextTick(() => {
      console.dir(myRef.value);
    });

    return {
      arr,
      setRef
    };
  }
};
</script>

发表评论