ref与reactive函数区别,面试之vue2与vue3响应式原理的区别

发布时间:2024年01月16日

setup

  1. 理解:Vue3.0中一个新的配置项,值为一个函数。

  2. setup是所有composition API “表演的舞台”。

  3. 组件中所用到的数据、方法等,均要配置在setup中。

  4. setup函数的两种返回值:

    1.若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。
    2.若返回一个渲染函数,则可以自定义渲染内容(了解)。
    
  5. 注意点:

     1. 尽量不要与vue2的配置项混合使用
    	Vue2配置(data 、methds 、computed)中可以访问到setup中的属性、方法。但在setup中不能访问到Vue配置的属性方法等(data 、methds 、computed)等。如果有重名,setup优先
     2. setup不能是一个asnyc函数,因为返回值不再是一个return对象,而是promise,模板看不到return对象中的属性
    

ref函数

  • 作用:定义一个响应式的数据

  • 语法:const xxx = ref(value)

    1.创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
    2.JS中操作数据:xxx.value。
    3.模板中读取数据:不需要.value直接{{xxx}}。
    

reactive函数

  • 作用:定义一个对象类型的响应式数据(基本类型不要用它,用ref函数)
  • 语法:const 代理对象 = reactive(源对象)__ 接受一个对象(或数组),返回一个代理对象(proxy对象
  • reactive定义的响应式数据是‘深层次的
  • 内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据进行操作
reactive对比ref
  • 从定义数据角度对比:

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

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

    • ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value
    • reactive定义的数据:操作数据与读取数据:均不需要.value
  • 一般将数据封装在一个data对象中,利用reactive函数将该对象变为响应式数据对象

Vue3与Vue2响应式原理:
Vue2:

实现原理:

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

存在问题:

  • 新增属性、删除属性、界面不会更新。
  • 直接通过数组下标修改数组,界面不会自动更新。
data() {
    return {
      person: {
        name: '张三',
        age: 18,
        hobby: ['学习', '唱歌']
      }
    }
  },
  methods: {
    //添加属性
    addSex() {
      // this.person.sex = '男' vue监测不到页面不更新
      this.$set(this.person, 'sex', '男') //$set()可以更新
      // Vue.set(this.person, 'sex', '男')
    },
    //删除属性
    deleteName() {
      // delete this.person.name //vue监测不到页面不更新
      this.$delete(this.person, 'name')
      // Vue.delete(this.person, 'name')
    },
    //数组下标方式修改属性
    updateHobby(){
      // this.person.hobby[0] = '打球' vue监测不到页面不更新
      // this.$set(this.person.hobby, 0, '打球')
      this.person.hobby.splice(0,1,'打球')
    }
  }
Vue3:

在Vue3中不存在“新增属性、删除属性, 界面不会更新”与“直接通过下标修改数组, 界面不会自动更新”的问题。

setup() {
    let person = reactive({
        name: '张三',
        age: 18,
        hobby: ['学习', '唱歌']
      })
    
    const addSex = () => {
      person.sex = '男'
    }
    const deleteName = () => {
      delete person.name
    }
    const updateHobby = () => {
      person.hobby[0] = '打球'
    }
    return{
      person,
      addSex,
      deleteName,
      updateHobby
    }

实现原理:

  • 通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等。

    //源数据
    let person = {
      name:'张三',
      age:18
    }
    //模拟Vue3中实现响应式
    //Proxy对属性的增删改查都可以监测得到
    const p = new Proxy(person,{
      //有人读取p的某个属性时调用
      get(target,propName){
        console.log(`有人读取了p身上的${propName}属性`)
        return target[propName]
      },
      
      //有人修改p的某个属性、或给p追加某个属性时调用
      set(target,propName,value){
        console.log(`有人修改了p身上的${propName}属性,我要去更新界面了!`)
        target[propName] = value
      },
      
      //有人删除p的某个属性时调用
      deleteProperty(target,propName){
        console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`)
        return delete target[propName]
      }
    })
    
  • 通过Reflect(反射):对被代理对象的属性进行操作。

    // Reflect(反射)
    const p = new Proxy(person,{
      //有人读取p的某个属性时调用
      get(target,propName){
        console.log(`有人读取了p身上的${propName}属性`)
        return Reflect.get(target,propName)
      },
        
      //有人修改p的某个属性、或给p追加某个属性时调用
      set(target,propName,value){
        console.log(`有人修改了p身上的${propName}属性,我要去更新界面了!`)
        Reflect.set(target,propName,value)
      },
        
      //有人删除p的某个属性时调用
      deleteProperty(target,propName){
        console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`)
        return Reflect.deleteProperty(target,propName)
      }
    })
    
    new Proxy(data, {
      // 拦截读取属性值
        get (target, prop) {
          return Reflect.get(target, prop)
        },
        // 拦截设置属性值或添加新属性
        set (target, prop, value) {
          return Reflect.set(target, prop, value)
        },
        // 拦截删除属性
        deleteProperty (target, prop) {
          return Reflect.deleteProperty(target, prop)
        }
    })
    proxy.name = 'tom'
    
setup的两个注意点
  • setup执行的时机

    • 在beforeCreate之前执行一次,this是undefined,在setup无法使用this。
  • setup的参数

    • props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
    • context:上下文对象
      • attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明接收的属性, 相当于 this.$attrs。存放没有被组件props配置项接收的数据,如果组件外部传递过来的数据都被组件props配置项接收,则该对象为空。
      • slots: 收到的插槽内容, 相当于 this.$slots。Vue3中具名插槽使用v-slot:插槽名
      • emit: 分发自定义事件的函数, 相当于 this.$emit。Vue3绑定的自定义事件在组件中需要使用emits配置项接收。
App.vue:
<template>
  <Demo @hello="showHelloMsg" msg="你好啊" school="尚硅谷">
    <!-- Vue3中具名插槽使用 `v-slot:插槽名` -->
    <template v-slot:qwe>
      <span>尚硅谷</span>
    </template>
    <template v-slot:asd>
      <span>尚硅谷</span>
    </template>
  </Demo>
</template>
<script>
  import Demo from './components/Demo'
  export default {
    name: 'App',
    components:{Demo},
    setup(){
      // 自定义事件的处理函数
      function showHelloMsg(value){
        alert(`你好啊,你触发了hello事件,我收到的参数是:${value}!`)
      }
      return {
        showHelloMsg
      }
    }
  }
</script>
Demo.vue:
<template>
	<h1>一个人的信息</h1>
	<h2>姓名:{{person.name}}</h2>
	<h2>年龄:{{person.age}}</h2>
	<button @click="test">测试触发一下Demo组件的Hello事件</button>
</template>
<script>
	import {reactive} from 'vue'
	export default {
		name: 'Demo',
		props:['msg','school'],
		emits:['hello'], // 绑定的自定义事件在组件中需要使用emits配置项接收
		setup(props,context){
			// console.log('---setup---',props)
			// console.log('---setup---',context)
			// console.log('---setup---',context.attrs) //相当与Vue2中的$attrs
			// console.log('---setup---',context.emit) //触发自定义事件的。
			console.log('---setup---',context.slots) //插槽
			//数据
			let person = reactive({
				name:'张三',
				age:18
			})
			//方法
			function test(){
				// 触发自定义事件
				context.emit('hello',666)
			}
			//返回一个对象(常用)
			return {
				person,
				test
			}
		}
	}
</script>
文章来源:https://blog.csdn.net/m0_61698324/article/details/135594765
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。