Vue3中的MVVM--ref、reactive函数使用、Vue3响应式原理

1,540 阅读2分钟

这是我参与更文挑战的第19天,活动详情查看: 更文挑战

一、ref函数的使用

ref()函数可以将数据转为响应式数据, 一般用于定义一个基本类型的响应式数据.

1.1 新建ref路由

自己写vue3模板页面太麻烦了, vscode自定义代码片段, 搜vue

image.png

image.png 然后补上这段代码

"vue3": {
		"prefix": "vue3",
		"body": [
			"<template>",
			"  <div>",
			"",
			"  </div>",
			"</template>",
			"",
			"<script lang=\"ts\">",
			"import { defineComponent } from 'vue';",
			"",
			"export default defineComponent({",
			"  name: '',",
			"});",
			"</script>",
			"",
			"<style scoped>",
			"",
			"</style>"
		],
		"description": "vue3初始化"
	}

然后在vscode中一键生成vue3模板

image.png

回归ref, 为什么要用ref, vue3中数据不是响应式的, 需要使用ref函数 新建一个ref路由 home.vue中

 <button @click="goPage('/ref')">去ref页</button>
 import { useRouter } from 'vue-router';
 let router = useRouter()
 let goPage = (path:string) => {
      router.push(path)
    }

ref.vue中

<template>
  <div>
      {{count}} <button @click="incrementCount">+</button>
  </div>
</template>

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

export default defineComponent({
  name: '',
  setup() {
      let count = 0
      let incrementCount = () => {
          count++
          console.log(count);
      }
      return {
          count,
          incrementCount,
      }
  }
});
</script>

<style scoped>

</style>

可以看见未使用ref, 数据是自增了, 但是视图未变

2021-06-21-21-22-59.gif

使用ref后, 数据就是双向的了

 let count =ref(0) 
      let incrementCount = () => {
          count.value++
          console.log(count.value);
      }

2021-06-21-21-26-08.gif

二、 reactive函数的使用

reactive()函数和ref()函数类似,也是将数据变成响应式数据,当数据发生改变时,模板视图也会自动更新。不同的是ref()用于基本数据类型,而reactive()用于引用数据类型,比如对象或数组。

例如添加用户列表

不用reactive是非响应式

<template>
  <div>
      <ul>
          <li v-for="(item,index) in userList" :key="index">{{item.name}}</li>
      </ul>
      <button @click="addUser">添加用户</button>
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  name: "reactive",
  setup() {
    let userList = [
      {
        id: 1,
        name: "张三",
      },
      {
        id: 2,
        name: "李四",
      },
      {
        id: 3,
        name: "王五",
      },
    ];
    let addUser = () => {
        let user = {
            id: userList.length + 1,
            name: '机器人' + userList.length + 1
        }
        userList.push(user)
        console.log(userList);
        
    }
    return {
        userList,
        addUser
    }
  },
});
</script>

<style scoped>
</style>

2021-06-21-21-42-07.gif 加了reactive, 数据变成proxy代理,变成响应式了

import { defineComponent,reactive } from "vue";
 let userList = reactive([
      {
        id: 1,
        name: "张三",
      },
      {
        id: 2,
        name: "李四",
      },
      {
        id: 3,
        name: "王五",
      },
    ]) ;

2021-06-21-21-44-52.gif ps: 其实ref也可以操作应用类型, 使用ref后数据.value.push就可以了, 因为底层会判断是基础数据类型还是引用数据类型, 如果是引用数据类型的话, 还是会走reactive的方法, 所以官方推荐引用类型使用reactive

三、响应式的原理

Vue3的响应式原理是通过proxy(代理)对对象属性值进行读写、添加、删除进行劫持,通过Reflect(反射)动态对被代理的对象的属性进行特定的操作,由于proxy和Reflect不支持IE浏览器,这也是Vue3不支持IE浏览器的主要原因之一。 新建一个html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script>
        let user = {
            id: 1,
            name: '张三'
        }
        let proxyUser = new Proxy(user,{
            // 目标对象 属性名
            get(target,prop) {
                console.log(target,prop);
            }
        })
        proxyUser.name
    </script>
</head>
<body>
    
</body>
</html>

image.png 获取里面的值

 let proxyUser = new Proxy(user,{
            get(target,prop) {
                console.log(target,prop);
                return target[prop]
            }
        })
        let result = proxyUser.name
        console.log(result,'属性值');

image.png 优化: 建议使用reflect反射获取值

 get(target,prop) {
                console.log(target,prop);
                // return target[prop]
                return Reflect.get(target,prop)
            }

image.png

通过set去设置里面的值

  // 设置值
            set(targe,prop,val) {
                // targe[prop] = val  // 不推荐
                Reflect.set(targe,prop,val)
            }
            
            
        let result = proxyUser.name
        console.log(result,'属性值原来');
        proxyUser.name = '李四'
        let result2 = proxyUser.name
        console.log(result2,'属性值改变');
        console.log(user,'被代理对象');

被代理的对象也改变了. image.png

删除值

  // 删除值
            deleteProperty(target,prop) {
                // delete target[prop] // 不推荐
                Reflect.deleteProperty(target,prop)
            }
            
    delete proxyUser.name
        console.log(user,'删除后被代理对象');

image.png 完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script>
        let user = {
            id: 1,
            name: '张三'
        }
        let proxyUser = new Proxy(user,{
            // 获取值
            get(target,prop) {
                console.log(target,prop);
                // return target[prop]
                return Reflect.get(target,prop)
            },
            // 设置值
            set(targe,prop,val) {
                // targe[prop] = val  // 不推荐
                Reflect.set(targe,prop,val)
            },
            // 删除值
            deleteProperty(target,prop) {
                // delete target[prop] // 不推荐
                Reflect.deleteProperty(target,prop)
            }
        })
        let result = proxyUser.name
        console.log(result,'属性值原来');
        proxyUser.name = '李四'
        let result2 = proxyUser.name
        console.log(result2,'属性值改变');
        console.log(user,'被代理对象');
        delete proxyUser.name
        console.log(user,'删除后被代理对象');
    </script>
</head>
<body>
    
</body>
</html>