diff算法
虚拟dom
虚拟 DOM (Virtual DOM,简称 VDOM) vue提供的渲染函数render相比于模板template更加灵活,因为你可以完全地使用 JavaScript 来构造你想要的 vnode 虚拟dom是一个js对象,因为真实dom对象,占用内存比较大内置有很多我们不常用的属性。所以用虚拟dom提高渲染性能。 如图所示,虚拟dom中有标签名字,props属性名字,子节点对象等等;
运行时渲染器将会遍历整个虚拟 DOM 树,并据此构建真实的 DOM 树。这个过程被称为挂载 (mount)。
如果我们有两份虚拟 DOM 树,渲染器将会有比较地遍历它们,找出它们之间的区别,并应用这其中的变化到真实的 DOM 上。这个过程被称为更新 (patch),又被称为“比对”(diffing) 或“协调”(reconciliation)
diff算法
查出新旧虚拟dom的差异(比对两个js对象的差异),最小化的更新视图;
当数据方式改变后,触发setter->触发Dep.notify->通知订阅者(数据使用位置)->使用patch方法接收新旧虚拟dom
- 如果不是同类标签直接全部替换;
- 如果是同类标签的话直接调用patchVnode方法比对:
-
判断新旧节点是否相同,如果相等就不需要比对
-
如果不相等(比对原则以新虚拟节点为准)
- 比对文本节点
- 旧的没有子节点,新的有子节点,直接添加新的子节点
- 旧的有子节点,新的没有,直接删除旧子节点
- 如果都有子节点的情况执行updateChildren方法
-
updateChildren方法内部
比对原则:最小化更新视图,同级比对,减少比对次数,最大化提升性能;
采用首位指针法
注意:同一个父元素下的子元素必须具有唯一的 key。重复的 key 将会导致渲染异常。
propmise
console.log('script start')
let promise1 = new Promise(function (resolve) {
console.log('promise1')
resolve('resolve')
console.log('promise1 end')
}).then(function (res) {
console.log('then',res)
console.log('promise2')
})
setTimeout(function(){
console.log('settimeout')
})
console.log('script end')
// 输出顺序: script start->promise1->promise1 end->script end->then resolve ->promise2->settimeout
class类封装继承
关于构造函数与class原型链的区别
寄生组合式继承
class Animal {
constructor(name){
this.name = name;
this.type = "动物";
}
says(say){
console.info(this.type + "【" + this.name + "】" + "说 " + say);
}
}
let dog = new Animal("狗狗");
dog.says("汪汪汪");//动物【狗狗】说 汪汪汪
class Bird extends Animal {
constructor(name){
super(name);
this.type = "小鸟";
}
}
let pigeon = new Bird("鸽子");
pigeon.says("我是一只小鸟");//小鸟【鸽子】说 我是一只小鸟
call、apply和bind的区别
相同点:
作用相同,都是动态修改this指向;都不会修改原先函数的this指向。
异同点:
(1)执行方式不同:
call和apply是改变后页面加载之后就立即执行,是同步代码。
bind改变后不会立即执行;而是返回一个新的函数,新函数的this会永久改变。
(2)传参方式不同:
call和bind传参是一个一个逐一传入,不能使用剩余参数的方式传参。
apply可以使用数组的形式传入的,只要是数组方式就可以使用剩余参数的方式传入。
function fn (a, b) {
console.log(this, a + b);
};
fn(1, 2); //window
const obj = {
name: '张三',
};
fn.call(obj, 3, 4); //obj
fn.apply(obj, [3, 4]); //obj
let newFn = fn.bind(obj); //obj
newFn(2, 4)//6
递归转化 reduce
扁平转树状
var menu_list = [
{
id: '1',
menu_name: '设置',
menu_url: 'setting',
parent_id: 0
}, {
id: '1-1',
menu_name: '权限设置',
menu_url: 'setting.permission',
parent_id: '1'
}, {
id: '1-1-1',
menu_name: '用户管理列表',
menu_url: 'setting.permission.user_list',
parent_id: '1-1'
},
{
id: '2',
menu_name: '用户管理',
menu_url: 'setting',
parent_id: 0
}, {
id: '2-1',
menu_name: '权限设置',
menu_url: 'setting.permission',
parent_id: '2'
},
{
id: '3',
menu_name: '用户管理',
menu_url: 'setting',
parent_id: 0
}
]
const arrData = JSON.parse(JSON.stringify(menu_list))
const result = arrData.reduce((prev, curr, i, arr) => {
curr.children = arr.filter((v => v.parent_id === curr.id))
if(curr.children.length===0) delete curr.children
if (curr.parent_id === 0) prev.push(curr)
return prev
}, [])
console.log('转化前', menu_list)
console.log('转化后', result)
传统的递归
/** *
*
* 将列表型的数据转化成树形数据 => 递归算法
* 找二级的上级(其实就是一级)
* 拿到一级的id和二级的pid来比较
* 如果相等,说明该二级就是该一级的下一级
* 再根据一级的id找二级
* ***/
function listToTree (list, id = '') {
let arr = []
list.forEach(item => {
//注意数据pid和id的数据类型是否一致
if (item.pid === id) {
// 找到之后 就要去找 item 下面有没有二级
item.children = listToTree(list, item.id)
arr.push(item) // 将内容加入到数组中
}
})
return arr
}
// 这种方法其实跟递归思路差不多
function listToTree (arr) {
//第一次过滤,先把一级过滤出来
const data = arr.filter((value) => {
//第二次过滤,把子级以及后代都过滤出来
value.children = arr.filter((item) => value.id === item.pid)
// 这里的''是父级pid
return value.pid === ''//判断第一层
})
return data
}
//改进版 不用递归
function listToTree (list) {
// 第一种
const arr = []
list.forEach(item => {
if (item.parent_id === 0) {
arr.push(item)
} else {
const p = list.find(i => i.id === item.parent_id)
if (!p.children) {
p.children = []
}
p.children.push(item)
}
})
return arr
}
树状转扁平
var menu_list = [
{
id: 1,
pid: 0,
name: '沃尔玛',
childrens: [
{
id: 2,
pid: 1,
name: '生鲜区',
childrens: [
{ id: 4, pid: 2, name: '鱼' },
{ id: 5, pid: 2, name: '牛肉' }
]
},
{
id: 3,
pid: 1,
name: '日用品区',
childrens: [
{ id: 6, pid: 3, name: '卫生纸' },
{ id: 7, pid: 3, name: '牙刷' }
]
}
]
}
]
const arrData = JSON.parse(JSON.stringify(menu_list))
const result = arrData.reduce(function (prev, curr, i, arr) {
prev.push({
id: curr.id,
name: curr.name,
pid: curr.pid
})
curr.childrens && curr.childrens.forEach(v => {
v.pid = curr.id//如果字级没有pid 让字级id和父级id关联起来
arguments.callee(prev, v)
});
return prev
}, [])
console.log('转化前', menu_list)
console.log('转化后', result)
function treeToArray(tree) {
return tree.reduce((res, item) => {
const { children, ...i } = item
return res.concat(i, children && children.length ? treeToArray(children) : [])
}, [])
}
去重
new set去重
//去重引用数据类型数组
const list =[
{ name: "张三", age: 18, address: "北京" },
{ name: "李四", age: 20, address: "天津" },
{ name: "张三", age: 18, address: "北京" },
{ name: "张三", age: 18, address: "北京" },
]
const strings = list.map((item) => JSON.stringify(item))
const removeDupList = [...new Set(strings)]
const uniResult = removeDupList.map((item) => JSON.parse(item))
console.log('uniResult',uniResult)
//去重基本数据类型数组
const member = [1,1,1,1,1]
const uniArr = [...new Set(member)]
console.log('uniArr',uniArr)
使用filter和Map
function uniqueFunc(arr, uniId){
const res = new Map();
return arr.filter((item) => !res.has(item[uniId]) && res.set(item[uniId], 1));
}
const arr =[
{ name: "张三", age: 18, address: "北京" },
{ name: "李四", age: 20, address: "天津" },
{ name: "张三", age: 18, address: "北京" },
{ name: "张三", age: 18, address: "北京" },
]
let map = new Map();
let newArr = arr.filter((v) => !map.has(v.name) && map.set(v.name, 1));
console.log(newArr)
reduce去重
通过对象访问属性的方法
var arr = [{
key: '01',
value: '乐乐'
}, {
key: '02',
value: '博博'
}, {
key: '03',
value: '淘淘'
},{
key: '04',
value: '哈哈'
},{
key: '01',
value: '乐乐'
}];
// 方法1:利用对象访问属性的方法,判断对象中是否存在key
var result = [];
var obj = {};
for(var i =0; i<arr.length; i++){
if(!obj[arr[i].key]){
result.push(arr[i]);
obj[arr[i].key] = true;
}
}
console.log(result); // [{key: "01", value: "乐乐"},{key: "02", value: "博博"},{key: "03", value: "淘淘"},{key: "04", value: "哈哈"}]
// 方法2:利用reduce方法遍历数组,reduce第一个参数是遍历需要执行的函数,第二个参数是prev的初始值
var obj = {};
arr = arr.reduce(function(prev, curr) {
obj[curr.key] ? '' : obj[curr.key] = true && prev.push(curr);
return prev;
}, []);
console.log(arr); // [{key: "01", value: "乐乐"},{key: "02", value: "博博"},{key: "03", value: "淘淘"},{key: "04", value: "哈哈"}]
选项
const originArr =[
{
id:'1',
},
{
id:'2',
},
{
id:'3',
},
{
id:'4',
},
]
const selectArr= [
{
id:'1',
},
{
id:'4',
},
{
id:'2',
},
]
// 可选的项目
function options () {
const arrData = selectArr.reduce((prev, curr) => {
return prev.filter(element => element.id !== curr.id) //prev为上一次的处理结果 继续filter
}, originArr)
return arrData
}
options()