Object.keys()
延申:Object.keys().map() 一道面试题应用考察 27题
传送门:https://www.jianshu.com/p/d35a406acdc6
一、语法:
参数:对象
返回值:对象可枚举属性的字符串数组
二、应用
处理对象,返回可枚举的属性数组
处理数组,返回索引值数组
let arr=[1,2,3,4,5]
处理字符串,返回索引值数组
let str="sszmq"
https://www.cnblogs.com/llcdxh/p/9476347.html
Object.defineProperty() 数据劫持
let Person={}
Object.defineProperty(Person,'name',{
value:'jack',//值
configurable:false, //是否可以再次配置
writable:true, //是否可以改变
enumerable:true //描述属性是否会出现在for in 或者Object.keys()得遍历中
})
https://www.jianshu.com/p/8fe1382ba135
vue的核心数据的监听,数组/对象深度遍历绑定,重写数组中的push/pop/shift/unshift/splice/sort/reverse七个方法
vue依赖收集做标记,如何做的标记还是不明白
vue数据依赖收集的精髓:
子对象进行依赖收集,其实就是将同一个watcher观察者实例放进了两个depend中,一个正在本身闭包中的depend,另一个是子元素的depend
数组的话则需要对每一个成员都进行依赖收集,如果是数组的成员还是数组,则递归
vue的双向数据绑定原理:
通过component 渲染 函数形成虚拟dom,然后进行虚拟dom中用到的data定义属性进行dep 数据依赖收集(发布者),做标记,添加观察者(watcher),移除观察者,通知所有订阅者进行执行回调,修改视图中的data,。当数据修改的时候就是执行setter就会触发dep发布者的notify通知所有订阅者,执行回调修改视图。
vue得虚拟dom
把真实得dom树抽象成一颗javascript对象构成得抽象树,如果每次修改了dom都去重绘整个视图层,很消耗性能,这个时候我们通过虚拟得dom和真实得dom树去进行比较,差异修改,修改以后经过diff算法得出一些需要修改得最小单位,再将这些小单位得视图进行更新。这样就减少了很多不需要得dom操作,大大提高了性能
diff算法
diff算法是通过同层得树节点进行比较而非对逐层搜索遍历得方式
patch得实现方法
/*createPatchFunction的返回值,一个patch函数*/
return function patch (oldVnode, vnode, hydrating, removeOnly, parentElm, refElm) {
/*vnode不存在则直接调用销毁钩子*/
if (isUndef(vnode)) {
if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
return
}
let isInitialPatch = false
const insertedVnodeQueue = []
if (isUndef(oldVnode)) {
// empty mount (likely as component), create new root element
/*oldVnode未定义的时候,其实也就是root节点,创建一个新的节点*/
isInitialPatch = true
createElm(vnode, insertedVnodeQueue, parentElm, refElm)
} else {
/*标记旧的VNode是否有nodeType*/
/*Github:https://github.com/answershuto*/
const isRealElement = isDef(oldVnode.nodeType)
if (!isRealElement && sameVnode(oldVnode, vnode)) {
// patch existing root node
/*是同一个节点的时候直接修改现有的节点*/
patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly)
} else {
if (isRealElement) {
// mounting to a real element
// check if this is server-rendered content and if we can perform
// a successful hydration.
if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {
/*当旧的VNode是服务端渲染的元素,hydrating记为true*/
oldVnode.removeAttribute(SSR_ATTR)
hydrating = true
}
if (isTrue(hydrating)) {
/*需要合并到真实DOM上*/
if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {
/*调用insert钩子*/
invokeInsertHook(vnode, insertedVnodeQueue, true)
return oldVnode
} else if (process.env.NODE_ENV !== 'production') {
warn(
'The client-side rendered virtual DOM tree is not matching ' +
'server-rendered content. This is likely caused by incorrect ' +
'HTML markup, for example nesting block-level elements inside ' +
'<p>, or missing <tbody>. Bailing hydration and performing ' +
'full client-side render.'
)
}
}
// either not server-rendered, or hydration failed.
// create an empty node and replace it
/*如果不是服务端渲染或者合并到真实DOM失败,则创建一个空的VNode节点替换它*/
oldVnode = emptyNodeAt(oldVnode)
}
// replacing existing element
/*取代现有元素*/
const oldElm = oldVnode.elm
const parentElm = nodeOps.parentNode(oldElm)
createElm(
vnode,
insertedVnodeQueue,
// extremely rare edge case: do not insert if old element is in a
// leaving transition. Only happens when combining transition +
// keep-alive + HOCs. (#4590)
oldElm._leaveCb ? null : parentElm,
nodeOps.nextSibling(oldElm)
)
if (isDef(vnode.parent)) {
// component root element replaced.
// update parent placeholder node element, recursively
/*组件根节点被替换,遍历更新父节点element*/
let ancestor = vnode.parent
while (ancestor) {
ancestor.elm = vnode.elm
ancestor = ancestor.parent
}
if (isPatchable(vnode)) {
/*调用create回调*/
for (let i = 0; i < cbs.create.length; ++i) {
cbs.create[i](emptyNode, vnode.parent)
}
}
}
if (isDef(parentElm)) {
/*移除老节点*/
removeVnodes(parentElm, [oldVnode], 0, 0)
} else if (isDef(oldVnode.tag)) {
/*Github:https://github.com/answershuto*/
/*调用destroy钩子*/
invokeDestroyHook(oldVnode)
}
}
}
/*调用insert钩子*/
invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)
return vnode.elm
}
当老节点和新节点相同得时候才会执行patchVnode,如果为同一个节点
就进行patchVnode,否则创建新得dom。
怎样得算相同得sameVnode
当节点得key,tag,iscomment,是否data包含了具体得一些数据信息,一个VnodeData类型,
当标签是input得时候,type也要相同
这里key,tag,iscomment是vue源码Vnode中讲到得,dom渲染属性,
patchVnode得规则是这样得
- 如果新旧vnode都是静态得,同时他们得key相同(代表同一节点)并且新得vnode时clone或者标记了once(v-once,只渲染一次,)那么只需要替换elm以及componentIntance即可
- 新老节点均有children子节点,则对子节点进行diff操作
- 如果老节点没有子节点而新节点存在子节点,先清空老节点DOM的文本内容,然后为当前DOM节点加入子节点
- 当新节点没有子节点而老节点有子节点的时候,则移除该DOM节点的所有子节点。
- 当新老节点都无子节点的时候,只是文本的替换。
vue得template得编译
这个时候我们就结合一下vue的生命周期图示
新建vue实例,初始化事件,注入,初始化data,然后去render,渲染,判断是否指定el选项,如果有在判断是否指定template,如果是就讲template编译到render函数中,如果没有就将el外部的html作为template编译。当没有指定el选项的时候执行$mount(el)函数
mount函数主要做了什么?
处理template,变成render函数,render最终会返回一个Vnode(虚拟dom节点)供页面渲染,或者在_update的时候,经过patch与之前的Vnode节点进行比较,得出差异然后将这些差异渲染到真实的dom中。
render函数不存在的时候才会编译tempate,否则优先使用render
template会被编译成语法树
我们可以看到最外层的div是这颗AST的根节点,节点上有许多数据代表这个节点的形态,比如static表示是否是静态节点,staticClass表示静态class属性(非bind:class)。children代表该节点的子节点,可以看到children是一个长度为4的数组,里面包含的是该节点下的四个div子节点。children里面的节点与父节点的结构类似,层层往下形成一棵AST。
再来看看由AST得到的render函数
with(this){
return _c( 'div',
{
/*static class*/
staticClass:"main",
/*bind class*/
class:bindClass
},
[
_c( 'div', [_v(_s(text))]),
_c('div',[_v("hello world")]),
/*这是一个v-for循环*/
_l(
(arr),
function(item,index){
return _c( 'div',
[_c('p',[_v(_s(item.name))]),
_c('p',[_v(_s(item.value))]),
_c('p',[_v(_s(index))]),
_c('p',[_v("---")])]
)
}
),
/*这是v-if*/
(text)?_c('div',[_v(_s(text))]):_c('div',[_v("no text")])],
2
)
}
代码中的_c,_l都是vue源码中定义的
/*处理v-once的渲染函数*/
Vue.prototype._o = markOnce
/*将字符串转化为数字,如果转换失败会返回原字符串*/
Vue.prototype._n = toNumber
/*将val转化成字符串*/
Vue.prototype._s = toString
/*处理v-for列表渲染*/
Vue.prototype._l = renderList
/*处理slot的渲染*/
Vue.prototype._t = renderSlot
/*检测两个变量是否相等*/
Vue.prototype._q = looseEqual
/*检测arr数组中是否包含与val变量相等的项*/
Vue.prototype._i = looseIndexOf
/*处理static树的渲染*/
Vue.prototype._m = renderStatic
/*处理filters*/
Vue.prototype._f = resolveFilter
/*从config配置中检查eventKeyCode是否存在*/
Vue.prototype._k = checkKeyCodes
/*合并v-bind指令到VNode中*/
Vue.prototype._b = bindObjectProps
/*创建一个文本节点*/
Vue.prototype._v = createTextVNode
/*创建一个空VNode节点*/
Vue.prototype._e = createEmptyVNode
/*处理ScopedSlots*/
Vue.prototype._u = resolveScopedSlots
/*创建VNode节点*/
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)