1、hook
Vue3 的 hook函数 相当于 vue2 的 mixin,不同在于 hook 是函数,其使用目的是为了复用代码,让setup中的逻辑更加清楚易懂。
使用示例:
在 src 目录下建立一个 hooks 文件夹,声明一个用于存放需要复用的代码的 js 文件,如下:
文件内容如下:
import {reactive,onMounted,onBeforeUnmount} from 'vue' export default function (){ //实现鼠标“打点”相关的数据 let point = reactive({ x:0, y:0 }) //实现鼠标“打点”相关的方法 function savePoint(event){ point.x = event.pageX point.y = event.pageY console.log(event.pageX,event.pageY) } //实现鼠标“打点”相关的生命周期钩子 onMounted(()=>{ window.addEventListener('click',savePoint) }) onBeforeUnmount(()=>{ window.removeEventListener('click',savePoint) }) return point }
上面的自定义 hook 实际上相当于封装了一个给页面绑定点击事件,并且记录了点击事件的鼠标位置的方法,可复用于各个组件之间。
在组件中引入并使用自定义 hook ,代码如下:
<template> <h2>当前求和为:{{sum}}</h2> <button @click="sum++">点我+1</button> <hr> <h2>当前点击时鼠标的坐标为:x:{{point.x}},y:{{point.y}}</h2> </template> <script> import {ref} from 'vue' import usePoint from '../hooks/usePoint' export default { name: 'Demo', setup(){ //数据 let sum = ref(0) let point = usePoint() //返回一个对象(常用) return {sum,point} } } </script>
由此上面组件即复用了自定义 hook 的方法,给页面绑定了点击事件,并且记录了点击的鼠标。自定义hook的作用类似于vue2中的mixin技术,自定义Hook的优势在于很清楚复用功能代码的来源,更清楚易懂。
2、toRef() 函数
toRef() 函数用来用来为源响应式对象上的某个 property 创建一个 ref 对象,其 value 值指向该响应式对象中的属性值。新创建的 ref 对象会保持对其源对象的对应 property 值的响应式连接。一般来说,当我们需要将响应式对象中的某个属性单独提供给外部使用时就可以使用 toRef() 函数。
比如下面的 person,如果直接返回 person.name,则该属性并不是响应式的,无法响应式地绑定在页面上。我们可以通过类似于 ref(person, 'name') 的写法来为响应式对象的某个属性创建 ref 对象,页面通过绑定该 ref 对象可以实现双向绑定的效果。
<template> <h4>{{person}}</h4> <h2>姓名:{{name2}}</h2> <h2>年龄:{{age}}</h2> <button @click="name2+='~'">修改姓名</button> <button @click="age++">增长年龄</button> </template> <script> import {ref,reactive,toRef,toRefs} from 'vue' export default { name: 'Demo', setup(){ //数据 let person = reactive({ name:'张三', age:18, job:{ j1:{ salary:20 } } }) const name2 = toRef(person,'name')//返回一个对象(常用) return { person, name2:name2, age:toRef(person,'age'), salary:toRef(person.job.j1,'salary') } } } </script>
3、toRefs() 函数
toRefs
与toRef
功能一致,不过 roRefs() 是一次性批量创建多个 ref 对象,并且返回的是一个对象。语法:toRefs(person)
代码示例:
<template> <h4>{{person}}</h4> <h2>薪资:{{job.j1.salary}}K</h2> <button @click="job.j1.salary++">涨薪</button> </template> <script> import {ref,reactive,toRef,toRefs} from 'vue' export default { name: 'Demo', setup(){ //数据 let person = reactive({ name:'张三', age:18, job:{ j1:{ salary:20 } } })//返回一个对象(常用) return { person, ...toRefs(person) } } } </script>
4、其他组合式api
shallowReactive(obj)(浅响应式):只有对象的最顶层属性有响应式。也就是说,如果修改对象的属性不是顶层的属性,则并不会有响应式的效果。
shallowRef():只处理基本数据类型的响应式,不进行对象的响应式处理。也就是说,该方法只接收基本数据类型的对象,如果是参数是对象类型,则返回的对象不具有响应式。
readonly()(深只读):接受一个对象 (可以是响应式对象或纯对象) 或 ref 并返回原始对象的只读代理。返回的只读对象如果对该对象属性进行修改,浏览器会报错。
shallowReadonly()(浅只读):跟 readonly() 一样,接受一个对象 (可以是响应式对象或纯对象) 或 ref 并返回原始对象的只读代理。但是该方法只是控制浅只读,也就是可以修改返回的代理对象的非顶层属性的值,修改后也有响应式的效果。
10、vue3的响应式原理
<script>
//目标对象
let obj = {
name: "张三",
age: 12,
bobby: ['钓鱼', '唱k']
};
//proxy把目标对象转化成代理对象
//参数1:目标对象 参数2:处理器对象,用来监听数据,及操作数据
let proxyObj = new Proxy(obj, {
//监听取值,第一个参数目标对象,第二个参数被获取的属性名
get(target, prop) {
console.log('触发了get操作');
// return target[prop];//不推荐
//使用Reflect为了优化Object的一些操作方法以及合理的返回Object操作返回的结果
return Reflect.get(target, prop);
},
//监听设置值
set(target, prop, val) {
console.log('触发了set操作');
// target[prop]=val;//不推荐
return Reflect.set(target, prop, val);
},
//监听删除delete
deleteProperty(target, prop) {
console.log('触发了delete操作');
// delete target[prop];不推荐
return Reflect.deleteProperty(target, prop);
}
});
console.log('初始代理对象值', proxyObj);
proxyObj.name = "李四"; //修改属性值
console.log('修改属性值后代理对象值', proxyObj, '修改后原对象值', obj);
proxyObj.sex = '男' //增加属性值
console.log('增加属性值后代理对象值', proxyObj, '修改后原对象值', obj);
proxyObj.bobby[0] = '踢球' //直接修改数组索引值
console.log('修改数组索引值后代理对象值', proxyObj.bobby[0], '修改后原对象值', obj.bobby[0]);
delete proxyObj.age; //删除属性值
console.log('删除属性后原对象值', obj);
</script>
上面通过 proxy 对 obj 对象的属性值的读写、添加、删除操作进行劫持,对于原对象的属性的增删改查操作就能监听得到,也就可以进行后面的一系列操作,以此来作为实现响应式的基础。
并且通过 proxy 代理来劫持对象属性,可以避免 vue2 中通过 Object.defineProperty() 无法监听对象属性的添加和删除操作,无法监听数组中直接通过索引来修改数组值的弊端。
执行结果如下:
(上面的代码不用 Reflect 直接通过返回或者设置原对象的属性实际也可以,Reflect 这个 API 在 es6 中被提出,目的主要是为了形成一个未来规范吧,把操作对象相关的方法统一到 Reflect。)
9.1、Proxy
proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。
var proxy = new Proxy(target, handler);