一、Composition API 如何实现逻辑复用
- 抽离逻辑代码到一个函数
- 函数命名约定为 useXxx 格式(React Hooks 也是)
- 在 setup 中引用 useXxx 函数
useMousePosition.js 文件
- 鼠标移动事件,显示鼠标的位置
- 写在 js 文件中,可供逻辑复用
import { ref, onMounted, onUnmounted} from 'vue' function useMousePosition() { // 初始化坐标 const x = ref(0) const y = ref(0) // 更新坐标 function update(e) { x.value = e.pageX y.value = e.pageY } // 挂载:添加鼠标移动事件 onMounted(() => { console.log('useMousePosition mounted'); window.addEventListener('mousemove', update) }) // 销毁:删除鼠标移动事件 onUnmounted(() => { console.log('useMousePosition unMounted'); window.removeEventListener('mousemove', update) }) return { x, y } } export default useMousePosition
App.vue 父组件
- 点击按钮,进行组件的创建 / 销毁
<template> <MousePosition v-if="flag" /> <button @click="changeFlagHandler">change flag</button> </template> <script> import MousePosition from "./components/index.vue"; export default { data() { return { flag: true, }; }, methods: { // 实现组件的创建/销毁 changeFlagHandler() { this.flag = !this.flag; }, }, components: { MousePosition }, }; </script>
index.vue 子组件
- 解构出函数中定义的 x 和 y
<template> <p>mouse position {{ x }} {{ y }}</p> </template> <script> import useMousePosition from "./useMousePosition"; export default { name: "MousePosition", setup() { // 解构 x 和 y const { x, y } = useMousePosition(); return { x, y, }; }, }; </script>
CompositionAPI复用
二、Vue3 如何实现响应式
1、Object.defineProperty 的缺点
- 深度监听需要一次性递归(层级很深的话会影响性能)
- 无法监听新增属性 / 删除属性(Vue.set Vue.delete)
- 无法原生监听数组,需要特殊处理
2、Proxy 实现响应式
- target:就是定义的对象 data
- key:获取的键
- val:获取的值
- receiver:是 proxyData
示例:对象通过 Proxy 实现响应式测试
const data = { name: '杂货铺', age: 20 } const proxyData = new Proxy(data, { // 监听获取 get(target, key, receiver) { const result = Reflect.get(target, key, receiver) console.log('get', key); return result // 返回结果 }, // 监听设置 set(target, key, val, receiver) { const result = Reflect.set(target, key, val, receiver) console.log('set', key, val) console.log('result', result); // true return result // 是否设置成功 }, // 监听删除 deleteProperty(target, key) { const result = Reflect.deleteProperty(target, key) console.log('delete property', key); console.log('result', result); // true return result // 是否删除成功 } })
示例:数组通过 Proxy 实现响应式测试
const data = ['a', 'b', 'c'] const proxyData = new Proxy(data, { get(target, key, receiver) { // 只处理本身(非原型的)属性 const ownKeys = Reflect.ownKeys(target) // 获取对象的键 if (ownKeys.includes(key)) { console.log('get', key); // 监听 } const result = Reflect.get(target, key, receiver) console.log('get', key); return result // 返回结果 }, set(target, key, val, receiver) { // 重复的数据,不处理 const oldVal = target[key] if(val === oldVal) { return true } const result = Reflect.set(target, key, val, receiver) console.log('set', key, val) console.log('result', result); // true return result // 是否设置成功 }, deleteProperty(target, key) { const result = Reflect.deleteProperty(target, key) console.log('delete property', key); console.log('result', result); // true return result // 是否删除成功 } })
3、Reflect 作用
- 和 Proxy 能力一一对应
- 规范化、标准化、函数式
- 替代掉 Object 上的工具函数
4、Proxy 实现响应式
- 深度监听,性能更好(用到的时候再监听)
- 可监听 新增 / 删除 属性
- 可监听数组变化
- Proxy 能规避 Object.defineProperty 的问题
- Proxy 无法兼容所有浏览器,无法 polyfill(用于实现浏览器并不支持原生 API 的代码)
示例:使用 Proxy 实现响应式
- 深度监听不是一次性监听完,而是用到的时候才监听
// 创建响应式 function reactive(target = {}) { if (typeof target !== 'object' || target == null) { // 不是对象或数组 return target } // 代理配置 const proxyConf = { get(target, key, receiver) { // 只处理本身(非原型的)属性 const ownKeys = Reflect.ownKeys(target) if (ownKeys.includes(key)) { console.log('get', key); // 监听 } const result = Reflect.get(target, key, receiver) // 深度监听 // 性能如何提升的? 什么时候 get 到,什么时候去做响应式 return reactive(result) // 返回结果 }, set(target, key, val, receiver) { // 重复的数据,不处理 const oldVal = target[key] if (val === oldVal) { return true } // 监听是已有的键还是新增的键 const ownKeys = Reflect.ownKeys(target) if (ownKeys.includes(key)) { console.log('已有的 key', key); } else { console.log('新增的 key', key); } const result = Reflect.set(target, key, val, receiver) console.log('set', key, val) return result // 是否设置成功 }, deleteProperty(target, key) { const result = Reflect.deleteProperty(target, key) console.log('delete property', key); console.log('result', result); // tru return result // 是否删除成功 } } // 生成代理对象 const observed = new Proxy(target, proxyConf) return observed } // 测试数据 const data = { name: '杂货铺', age: 21, info: { city: 'beijing' } } const proxyData = reactive(data)
不积跬步无以至千里 不积小流无以成江海
点个关注不迷路,持续更新中…