web前端面试高频考点——Vue3.x(Composition API的逻辑复用、Proxy实现响应式)
系列文章目录
内容 | 参考链接 |
---|---|
JavaScript 面试高频考点 | HTML、CSS、JavaScript、ES6、AJAX、HTTP 面试考点 |
Vue2.x 面试高频考点 | Vue2.x 面试高频考点 |
Vue3.x新增API | 生命周期,ref、toRef 和 toRefs 的理解和最佳使用方式 |
Vue3.x升级的重要功能 | emits属性、生命周期、多事件、Fragment、移出.async、异步组件写法、移出 filter、Teleport、Suspense… |
文章目录
- 系列文章目录
- 一、Composition API 如何实现逻辑复用
- 二、Vue3 如何实现响应式
- 1、Object.defineProperty 的缺点
- 2、Proxy 实现响应式
- 3、Reflect 作用
- 4、Proxy 实现响应式
一、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)
不积跬步无以至千里 不积小流无以成江海
点个关注不迷路,持续更新中…