这是我参与11月更文挑战的第22天, 活动详情查看:2021最后一次更文挑战
承接上文:vue双向绑定
先说下vue里面用到es6的语法:proxy
在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。参考阮大神的 es6入门
question:能否监听对象属性的删除操作? 基于proxy实现一下响应式。
let observeStore = new Map();
function markObservable(target){
let handleName = Symbol('handle');
observeStore.set(handleName, []);
target.observe = function(handle){
observeStore.get(handleName).push(handle);
}
const proxyHandler = {
get(target,property,receiver){
if(typeof target[property] === 'object' && target[property] !== null){
return new Proxy(target[property],proxyHandler);
}
let success = Reflect.get(...arguments); // 对原来数据的一个映射
if(success){
observeStore.get(handleName).forEach(handle => handle('GET',property,target[property]))
}
return success;
},
set(target,prototype,receiver){
let success = Reflect.set(...arguments);
if(success){
observeStore.get(handleName).forEach(handler => handler('SET', property, value));
}
return success;
},
deleteProperty(target,property){
let success = Reflect.deleteProperty(...arguments);
if(success){
observeStore.get(handleName).forEach(handler => handler('DELETE', property));
}
return success;
}
};
// 创建proxy,拦截更改
return new Proxy(target,proxyHandler);
};
let user = {};
user = markObservable(user); // 已经是响应式的
user.observe((action,key,value) => {
console.log(`${action} key=${key} value=${value || ''}`);
});
user.name = 'John'; // SET key=name value=John
console.log(user.name); // GET key=name value=John // John
delete user.name; // DELETE key=name value
你了解虚拟DOM吗?能说一下它的优缺点吗?
什么是虚拟dom?其实就是js对象。
即:对于真实DOM的抽象,用嵌套对象表示,用属性来描述节点,最终通过一系列操作映射到真实dom上
虚拟dom优点
- 保证性能下限。在不通过手动优化的前提下,也能提供过得去的性能。
- 无需手动操作dom
- 跨平台
虚拟dom本质上其实就是一个js对象,它可以很方便的跨平台了,比如服务端渲染 比如 uniapp
缺点
- 首次渲染大量dom的对象,由于多了一层虚拟DOM的计算,会比innerHTML的插入速度慢
- 做一些针对性的优化时,真实dom的操作还是会更快一些。
question: 给你一段数据结构,将其转换为真实dom
const vnode = {
tag: 'DIV',
attrs: {
id: 'app'
},
children: [
{
tag: 'SPAN',
children: []
},
{
tag: 'SPAN',
children: [{
tag: 'A',
children: []
},
{
tag: 'A',
children: []
}]
}
]
}
function render (vnode) {
if(typeof vnode === 'number'){
vnode = String(vnode);
}
if(typeof vnode === 'string'){
return document.createTextNode(vnode);
}
const element = document.createElement(vnode.tag);
if(vnode.attrs){
Object.keys(vnode.attrs).forEach(attrKey => {
element.setAttribute(key,vnode.attrs[attrKey])
})
}
// 开始递归
if(vnode.children){
vnode.children.forEach(childNode => {
element.appendChild(render(childNode));
})
}
return element;
}