🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] * * * * * ## 1 源代码文件 ### 1-1 扫描入口 ~~~ src/dom/ready/modern.js ~~~ ### 1-2 扫描实现 ~~~ src/dom/ready/scan.js ~~~ ## 2 流程分析 ### 2-1 注册scan到domready ~~~ src/dom/ready/modern.js avalon.ready(function(){ scan(document.body) }) ~~~ ### 2-2 扫描入口 ~~~ src/dom/ready/scan.js function scan(nodes) {} ~~~ ### 2-3 扫描流程 >[success] 下面代码全在src/dom/ready/scan.js * * * * * >[info] 1 获取节点$id ~~~ var $id = hasController(elem) ~~~ * * * * * > hasController()在下面定义 分析可知获取elem的的ms-controller属性。 `<div ms-controller="test"></div>` 中 $id是test * * * * * >[info] 2 获取$id对应vm ~~~ var vm = avalon.vmodels[$id] ~~~ > avalon生成的vm存储在全局数组avalon.vmodles > 以vm的$id索引vm对象。 > 这里在avalon.vmodels数组中获取$id对应vm * * * * * >[info] 3 解析渲染 ~~~ if (vm && !vm.$element) {} ~~~ > 查找$id对应的vm成功, > 并且vm.$element不存在 > 进行扫描处理 ~~~ cleanWhitespace(elem) ~~~ > 删除空格,加速diff操作 ~~~ var str = elem.outerHTML avalon(elem).removeClass('ms-controller') vm.$element = elem ~~~ > 获取节点的outerHTML内容, > 移除节点的class属性ms-controller > 将elem绑定到vm.$element,对应判断条件的!vm.$element ~~~ var now = new Date() - 0 ~~~ > 内容解析开始计时 ~~~ var vtree = avalon.lexer(str) ~~~ > 解析str为虚拟dom树 ~~~ avalon.log('create primitive vtree', new Date - now) ~~~ > 控制台输出解析耗时 ~~~ now = new Date() ~~~ > 生成渲染开始计时 ~~~ vm.$render = avalon.render(vtree) ~~~ > 生成渲染函数 ~~~ avalon.log('create template Function ', new Date - now) ~~~ > 控制台输出渲染耗时 ~~~ avalon.rerenderStart = new Date ~~~ > 控制台渲染开始时间 ~~~ elem.vtree = vtree ~~~ > 保存虚拟dom树到elem.vtree ~~~ avalon.batch($id, true) ~~~ > vm与视图同步刷新 * * * * * >[info] 4 扫描处理 ~~~ else if (!$id) {} ~~~ > 本节点没有$id, ~~~ scan(elem.childNodes) ~~~ > 继续扫描子节点 ## 3 其他代码 ### 3-1 递归删除空白 ~~~ var notWhitespace = /\S/ function cleanWhitespace(target) { var keep for (var i = 0; i < target.childNodes.length; i++) { var node = target.childNodes[i] if ((node.nodeType == 3) && (!notWhitespace.test(node.nodeValue))) { keep = target.removeChild(node) i-- }else if (node.nodeType === 1) { cleanWhitespace(node) } } if (target.childNodes.length == 0 && keep) { target.appendChild(keep) } } ~~~ > target: 待扫描节点 * * * * * > 遍历target子节点, > 根据节点node的类型nodeType进行不同处理 > nodeType为3(text节点)并且文本节点为空,删除 > 这里的i--对应removeChild。 > nodeType为1(element节点)则递归进行处理 > 如果目标target删除后子节点长度为0 > 则将子节点追加到target ### 3-2 全局scan接口 ~~~ module.exports = avalon.scan = function (a) { if (!a || !a.nodeType) { avalon.warn('[avalon.scan] first argument must be element , documentFragment, or document') return } if (getController(a)) { avalon.warn('[avalon.scan] first argument must has "ms-controller" attribute') return } scan([a]) } ~~~ > a:待扫描节点 * * * * * > module.exports module导出功能 见基础原理的 js模块 > 第一个if检测a是否为element节点。 > 第二个if检测a节点内是否包含ms-controller属性 > 最后调用scan([a]) 进入扫描 ### 3-3 ms-controoler属性操作 ~~~ function hasController(a) { return a.getAttribute('ms-controller') } ~~~ > a:待扫描节点a * * * * * > 获取a节点的ms-controller属性 > `<div ms-controller="test"></div>` * * * * * ~~~ function getController(a) { if (a.getAttribute && hasController(a)) { return true } //document.all http://www.w3help.org/zh-cn/causes/BX9002 var all = a.getElementsByTagName ? a.getElementsByTagName('*') : a.querySelectorAll('*') for (var i = 0, node; node = all[i++]; ) { if (hasController(a)) { return true } } return false } ~~~ > a:待扫描节点a * * * * * > 检测a节点中是否包含ms-controoler属性 > 递归遍历a节点及其子节点 ## 4 总结 ### 4-1 意义 scan()是模板扫描的入口 ### 4-2 思路 scan()中**递归扫描**目标节点及其子节点 将扫描结果的保存到对应节点elem的vtree中 ### 4-3 其他操作 scan()中 包含对VM创建结果数组`avalon.vmodel`的操作 包含 节点解析入口 `avalon.lexer()` 包含 渲染生成入口 `avalon.render()` 包含 同步刷新入口 `avalon.batch()` 具体分析见框架流程的 [主:VM创建](http://www.kancloud.cn/zmwtp/avalon2/136861) [附:节点解析](http://www.kancloud.cn/zmwtp/avalon2/137551) [附:渲染生成](http://www.kancloud.cn/zmwtp/avalon2/137552) [主:同步刷新](http://www.kancloud.cn/zmwtp/avalon2/136862) 4-4 参考链接 [avalon1与avalon2的异同点](http://www.cnblogs.com/rubylouvre/p/5358820.html) [avalon2学习教程01](https://segmentfault.com/a/1190000004882326)