VUE3响应式原理

vue3是基于ES6新增的proxy代理实现的
vue3官网

一:setup组件

setup() 函数在组件创建 created() 之前执行。

setup() 函数接收两个参数 props 和 context。

第一个参数 props,它是响应式的,当传入新的 prop 时,它将被更新。

第二个参数 context 是一个普通的 JavaScript 对象,它是一个上下文对象,暴露了其它可能在 setup 中有用的值。

注意:在 setup 中你应该避免使用 this,因为它不会找到组件实例。setup 的调用发生在 data property、computed property 或 methods 被解析之前,所以它们无法在 setup 中被获取。

以下实例使用组合 API 定义一个计数器:

实例(src/APP.vue)
<template>
    <div>
        <p>计数器实例: {{ count }}</p>
        <input @click="myFn" type="button" value="点我加 1">
    </div>
</template>

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

export default {
    setup(){
        //定义初始值为0的变量,要使用ref方法赋值,直接赋值的话变量改变不会更新 UI
        let count = ref(0);

        // 定义点击事件 myFn
        function myFn(){
            console.log(count);
            count.value += 1;
        }
       
       // 组件被挂载时,我们用 onMounted 钩子记录一些消息
        onMounted(() => console.log('component mounted!'));

        // 外部使用组合API中定义的变量或方法,在模板中可用。
        return {count,myFn} // 返回的函数与方法的行为相同
    }
}
</script>

在 Vue 3.0 中,我们可以通过一个新的 ref 函数使任何响应式变量在任何地方起作用,如下所示:

import { ref } from 'vue'
let count = ref(0);

ref() 函数可以根据给定的值来创建一个响应式的数据对象,返回值是一个对象,且只包含一个 .value 属性。
在 setup() 函数内,由 ref() 创建的响应式数据返回的是对象,所以需要用 .value 来访问。

import { ref } from 'vue'

const counter = ref(0)

console.log(counter) // { value: 0 }
console.log(counter.value) // 0

counter.value++
console.log(counter.value) // 1

二:ref 创建响应式数据

使用ref可以创建一个包含响应式数据的引用对象(reference对象,简称ref对象),可以是基本类型、也可以是对象。

语法

// 创建
const xxx = ref(value)

// 使用
xxx.value

// 在模板中
<div>{{xxx}}</div>

三:reactive 创建响应式数据

定义一个对象类型的响应式数据,内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据进行操作

返回一个对象的响应式代理。

类型

// 定义一个引用类型的响应式数据list 默认是
    const list = reactive(["vue","react","angular"])

四:computed 计算属性

计算属性关键词: computed。
computed 属性默认只有 getter ,不过在需要时你也可以提供一个 setter :

var twiceNum = computed(()=>num.value*2)

computed vs methods
我们可以使用 methods 来替代 computed,效果上两个都是一样的,但是 computed 是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值。而使用 methods ,在重新渲染的时候,函数总会重新调用执行。

methods: {
  reversedMessage2: function () {
    return this.message.split('').reverse().join('')
  }
}

以说使用 computed 性能会更好,但是如果你不希望缓存,你可以使用 methods 属性。

五:watch 监听

watch() 默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。

第一个参数是侦听器的源。这个来源可以是以下几种:

一个函数,返回一个值
一个 ref
一个响应式对象
…或是由以上类型的值组成的数组

watch(num,function(nval,oval){
      console.log("num由",oval,"变化为",nval)
    })

watch() 和 watchEffect() 享有相同的刷新时机和调试选项:

watch(source, callback, {
  flush: 'post',
  onTrack(e) {
    debugger
  }
})   

六:watchEffect 监听回调

和watch的区别是,watch既要指明监视的属性,也要指明监视的回调。而watchEffect,不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性,不用写返回值。

watchEffect(()=>{
      // 只要在改回调函数中引用的变量,都会监听到变化
      console.log(num.value,list[1],'变化了');
    })

七:生命周期( onMounted)

vue3相对于vue2的改变

  • beforeDestroy 改名为 beforeUnmount
  • destroyed 改名为 unmounted
  • beforeCreate => setup
  • created => setup
  • beforeMount => onBeforeMount
  • mounted => onMounted
  • beforeUpdate => onBeforeUpdate
  • updated => onUpdated
  • beforeUnmount => onBeforeUnmount
  • unmounted => onUnmounted

语法

setup() {
    onMounted(()=>{
      console.log("组件已经挂载完毕");
      // myp.value就是dom节点
      myp.value.addEventListener("click",()=>alert(myp.value.innerText))
    })
}

八:vue3与vue2区别

1. 响应式原理不同:

vue2响应式原理采用 Object.defineProperties 监听对象的getter与setter
Vue3 pxoy代理的方式监听对象

2. 启动方式

vue2

new Vue({
 store,
 router,
 render:h=>h(App)
}).$mount("#app")

vue3

Import {createApp} from 'vue'

createApp(App).use(store).use(router).mount("#app")

3. 全局方法定义

vue2
Vue.prototype.$http = axios;

Vue3
var app = createApp(App)
app.config.globalProperies.$http = axios;

4. template 根组件

Vue2
有且只有一个根组件
vue3
随意

5. 生命周期

vue2
创建前后 beforeCreate ,created
挂载前后 beforeMount mounted
更新前后beforeUpdate,updated
销毁前后beforeDestroy,destroyed
vue3
创建前后 beforeCreate ,created
挂载前后 beforeMount mounted
更新前后beforeUpdate,updated
卸载切换 beforeUnmount,unmounted

获取鼠标坐标方法

在这里插入图片描述
定义/src/utils/Mouse.js
代码如下

import { reactive, onMounted, onBeforeUnmount } from "vue";
function GetMouse() {
  var page = reactive({
    x: null,
    y: null,
  });
  //定义获取鼠标x轴,y轴偏移值
  function Getpage(event) {
    page.x = event.pageX;
    page.y = event.pageY;

    var p=document.getElementById('p')
    p.innerHTML = ("x:"+page.x) + "," + ("y:"+page.y);
    p.style.top = page.y + "px";
    p.style.left = page.x + "px";
    p.style.position = "absolute";
  }

  //dom挂载完毕执行方法
  onMounted(() => {
    document.addEventListener("mousemove", Getpage);
    
  });
  onBeforeUnmount(() => {
    document.removeEventListener("mousemove", Getpage);
  });

  return page
}
//导出
export default GetMouse;

定义AboutView页面
代码如下

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <p>{{ winsize }}</p>
    <p>{{mouse}}</p>
    <p id="p"></p>
  </div>
</template>
<script>
import useWinSize from "@/utils/useWinSize";
import GetMouse from "@/utils/Mouse.js"
export default {
  setup() {
    const winsize = useWinSize()
    const mouse=GetMouse()
    
    return { winsize,mouse }
  }
}
</script>
<style>
#p{
  font-size: 12px;
  margin-top: -7px;
  margin-left: 2px;
}
</style>

效果图:
在这里插入图片描述

Logo

前往低代码交流专区

更多推荐