version: 1.2.9
两个函数:h
和 app
。
h
是用来转化编译后的 jsx
的具体函数。
app
是用来连接 state
, actions
和 view
,生成dom节点,挂载到 container
,最终得到具体视图。
在逐渐深入了解下,深感此框架特别灵活
函数 h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 function h (name, attributes ) { var rest = [] var children = [] var length = arguments .length while (length-- > 2 ) rest.push (arguments [length]) while (rest.length ) { var node = rest.pop () if (node && node.pop ) { for (length = node.length ; length--; ) { rest.push (node[length]) } } else if (node != null && node !== true && node !== false ) { children.push (node) } } return typeof name === "function" ? name (attributes || {}, children) : { nodeName : name, attributes : attributes || {}, children : children, key : attributes && attributes.key } }
函数 app
有四个参数,分别是 state
, actions
, view
, container
。
state
: 表示应用的所有数据
actions
: 改变state的唯一方式是通过此动作
view
: 应用程序的视图,可以连接 state
和 actions
container
: 一个dom元素,需要在其上挂载应用
函数内部 首先定义了7个变量,分明是:
map <Function>
: 提取 Array.map
rootElement <Object>
: 根元素
oldNode <Object>
: 旧的节点
lifecycle <Array>
: 生命周期
skipRender <undefined>
: 跳过渲染
isRecycling <Boolean>
: 是否循环
globalState <Object>
: 全局状态
wiredActions <Object>
: 有线动作
接着执行函数 scheduleRender
1 2 3 4 5 6 7 8 function scheduleRender ( ) { if (!skipRender) { skipRender = true setTimeout (render) } }
因为使用了 setTimeout
,所以在定时器到期后执行 render
。(具体浏览器Event Loop)
最后返回 wiredActions
。
wiredActions
的获取是通过函数 wireStateToActions
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 function wireStateToActions (path, state, actions ) { for (var key in actions) { typeof actions[key] === "function" ? (function (key, action ) { actions[key] = function (data ) { ... ... ... } })(key, actions[key]) : wireStateToActions ( path.concat (key), (state[key] = clone (state[key])), (actions[key] = clone (actions[key])) ) } return actions }
辅助函数 clone
1 2 3 4 5 6 7 function clone (target, source ) { var out = {} for (var i in target) out[i] = target[i] for (var i in source) out[i] = source[i] return out }
函数 recycleElement
1 2 3 4 5 6 7 8 9 10 11 12 function recycleElement (element ) { return { nodeName : element.nodeName .toLowerCase (), attributes : {}, children : map.call (element.childNodes , function (element ) { return element.nodeType === 3 ? element.nodeValue : recycleElement (element) }) } }
具体为什么要使用setTimeout,返回wiredActions的意义是什么
定时器到期执行函数 render
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function render ( ) { skipRender = !skipRender var node = resolveNode (view) if (container && !skipRender) { rootElement = patch (container, rootElement, oldNode, (oldNode = node)) } isRecycling = false while (lifecycle.length ) lifecycle.pop ()() }
函数 resolve
1 2 3 4 5 6 7 8 function resolveNode (node ) { return typeof node === "function" ? resolveNode (node (globalState, wiredActions)) : node != null ? node : "" }
函数 patch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function patch (parent, element, oldNode, node, isSvg ) { if (node === oldNode) { } else if (oldNode == null || oldNode.nodeName !== node.nodeName ) { var newElement = createElement (node, isSvg) parent.insertBefore (newElement, element) if (oldNode != null ) { removeElement (parent, element, oldNode) } element = newElement } else if (oldNode.nodeName == null ) { element.nodeValue = node } else { ... } return element }
创建元素函数 createElement
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 function createElement (node, isSvg ) { var element = typeof node === "string" || typeof node === "number" ? document .createTextNode (node) : (isSvg = isSvg || node.nodeName === "svg" ) ? document .createElementNS ( "http://www.w3.org/2000/svg" , node.nodeName ) : document .createElement (node.nodeName ) var attributes = node.attributes if (attributes) { if (attributes.oncreate ) { lifecycle.push (function ( ) { attributes.oncreate (element) }) } for (var i = 0 ; i < node.children .length ; i++) { element.appendChild ( createElement ( (node.children [i] = resolveNode (node.children [i])), isSvg ) ) } for (var name in attributes) { updateAttribute (element, name, attributes[name], null , isSvg) } } return element }
更新属性函数 updateAttribute
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 function updateAttribute (element, name, value, oldValue, isSvg ) { if (name === "key" ) { } else if (name === "style" ) { if (typeof value === "string" ) { element.style .cssText = value } else { if (typeof oldValue === "string" ) oldValue = element.style .cssText = "" for (var i in clone (oldValue, value)) { var style = value == null || value[i] == null ? "" : value[i] if (i[0 ] === "-" ) { element.style .setProperty (i, style) } else { element.style [i] = style } } } } else { if (name[0 ] === "o" && name[1 ] === "n" ) { name = name.slice (2 ) if (element.events ) { if (!oldValue) oldValue = element.events [name] } else { element.events = {} } element.events [name] = value if (value) { if (!oldValue) { element.addEventListener (name, eventListener) } } else { element.removeEventListener (name, eventListener) } } else if ( name in element && name !== "list" && name !== "type" && name !== "draggable" && name !== "spellcheck" && name !== "translate" && !isSvg ) { element[name] = value == null ? "" : value } else if (value != null && value !== false ) { element.setAttribute (name, value) } if (value == null || value === false ) { element.removeAttribute (name) } } }
函数 eventListener
1 2 3 4 5 6 7 8 9 function eventListener (event ) { return event.currentTarget .events [event.type ](event) }
函数 removeElement
1 2 3 4 5 6 7 8 9 10 11 12 function removeElement (parent, element, node ) { function done ( ) { parent.removeChild (removeChildren (element, node)) } var cb = node.attributes && node.attributes .onremove if (cb) { cb (element, done) } else { done () } }
函数 removeChildren
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function removeChildren (element, node ) { var attributes = node.attributes if (attributes) { for (var i = 0 ; i < node.children .length ; i++) { removeChildren (element.childNodes [i], node.children [i]) } if (attributes.ondestroy ) { attributes.ondestroy (element) } } return element }
此上为初始化,创建虚拟dom,挂载节点,生成元素。
当执行一个操作,发起一个action时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 function wireStateToActions (path, state, actions ) { ... typeof actions[key] === "function" ? (function (key, action ) { actions[key] = function (data ) { var result = action (data) if (typeof result === "function" ) { result = result (getPartialState (path, globalState), actions) } if ( result && result !== (state = getPartialState (path, globalState)) && !result.then ) { scheduleRender ( (globalState = setPartialState ( path, clone (state, result), globalState )) ) } return result } })(key, actions[key]) : ... } }
函数 getPartialState
1 2 3 4 5 6 7 8 function getPartialState (path, source ) { var i = 0 while (i < path.length ) { source = source[path[i++]] } return source }
函数 setPartialState
1 2 3 4 5 6 7 8 9 10 11 function setPartialState (path, value, source ) { var target = {} if (path.length ) { target[path[0 ]] = path.length > 1 ? setPartialState (path.slice (1 ), value, source[path[0 ]]) : value return clone (source, target) } return value }
然后执行 scheduleRender
,更新虚拟dom,生成最新视图。 这时,rootElement
是一个虚拟dom。
函数 patch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 function patch (parent, element, oldNode, node, isSvg ) { if (node === oldNode) { } else if (oldNode == null || oldNode.nodeName !== node.nodeName ) { ... ... } else { updateElement ( element, oldNode.attributes , node.attributes , (isSvg = isSvg || node.nodeName === "svg" ) ) var oldKeyed = {} var newKeyed = {} var oldElements = [] var oldChildren = oldNode.children var children = node.children for (var i = 0 ; i < oldChildren.length ; i++) { oldElements[i] = element.childNodes [i] var oldKey = getKey (oldChildren[i]) if (oldKey != null ) { oldKeyed[oldKey] = [oldElements[i], oldChildren[i]] } } var i = 0 var k = 0 while (k < children.length ) { var oldKey = getKey (oldChildren[i]) var newKey = getKey ((children[k] = resolveNode (children[k]))) if (newKeyed[oldKey]) { i++ continue } if (newKey != null && newKey === getKey (oldChildren[i + 1 ])) { if (oldKey == null ) { removeElement (element, oldElements[i], oldChildren[i]) } i++ continue } if (newKey == null || isRecycling) { if (oldKey == null ) { patch (element, oldElements[i], oldChildren[i], children[k], isSvg) k++ } i++ } else { var keyedNode = oldKeyed[newKey] || [] if (oldKey === newKey) { patch (element, keyedNode[0 ], keyedNode[1 ], children[k], isSvg) i++ } else if (keyedNode[0 ]) { patch ( element, element.insertBefore (keyedNode[0 ], oldElements[i]), keyedNode[1 ], children[k], isSvg ) } else { patch (element, oldElements[i], null , children[k], isSvg) } newKeyed[newKey] = children[k] k++ } } while (i < oldChildren.length ) { if (getKey (oldChildren[i]) == null ) { removeElement (element, oldElements[i], oldChildren[i]) } i++ } for (var i in oldKeyed) { if (!newKeyed[i]) { removeElement (element, oldKeyed[i][0 ], oldKeyed[i][1 ]) } } } return element }
更新元素函数 updateElement
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 function updateElement (element, oldAttributes, attributes, isSvg ) { for (var name in clone (oldAttributes, attributes)) { if ( attributes[name] !== (name === "value" || name === "checked" ? element[name] : oldAttributes[name]) ) { updateAttribute ( element, name, attributes[name], oldAttributes[name], isSvg ) } } var cb = isRecycling ? attributes.oncreate : attributes.onupdate if (cb) { lifecycle.push (function ( ) { cb (element, oldAttributes) }) } }
得到 key
函数 getKey
1 2 3 function getKey (node ) { return node ? node.key : null }