如果写过
Vue
相信一定不会忘记 Vue 的模版语法。@click
可以用于绑定事件,比如:00<div> 01 <button @click="clicking()">Click Me!</button> 02</div>
Vue 实例化组件的时候,会解析这些模版,然后完成这些事情。
当然本文目的不在讨论 Vue 的模版语法,而是讨论一个实现这样的事情的思路。
# 表征组件的对象 ↵
诸多框架均实现了 “网页是一颗组件树” 的抽象,上面许许多多的组件,一般而言都有模版和数据,然后框架做的事情,就是把数据渲染在模版上,把事件绑定好,最后插入到 DOM 树上。
因此,组件,其实是模版和数据的又一个实例,比如如下数据:
00var config = { 01 sayName(){ 02 console.log('you click me'); 03 console.log(`my name is ${this.name}`); 04 }, 05 name: 'wow', 06 template: ` 07 <div> 08 <h1 class="title">Hello</h1> 09 <button @click="sayName">sayName</button> 10 </div> 11 ` 12}
上述代表了一个简单的组件,上面有标题,和一个按钮,点击的时候,会执行 sayName (这里利用了 @click 来绑定事件)
而且我们假设,调用
render('#container', data)
将会把该组件渲染到 #container
里,同时,也会把模版里面响应的 @click
事件绑定上去。# tplParser ↵
因为是把组件渲染到
#container
的,所以还需要遍历 #container
,这是树的遍历问题,用递归解决是最便捷的。经过观察和考虑可以想到,要正确的解析
@click
只需做到:- 遍历所有结点的属性们
- 遍历单个属性们里面的属性
针对第一点,使用递归来遍历。
00function tplParser(root, config){ 01 Array.from(root.children).forEach(child => { 02 /* 03 * 这里对 child.attributes 进行处理 把 @click 取出来 04 */ 05 06 // Just Regard Child As A Tree's Root 07 tplParser(child, config); 08 }); 09}
由于当结点没有后代的时候, root.children 其实是空的,也意味着里面的
travel
不会被执行,递归收敛。针对第二点,利用 forEach 就可以了。
00var todoSomething = attr => { 01 // todoSomething with attr 02} 03Array.from(child.attributes).forEach(todoSomething);
# 完整的写法 ↵
00// root 是根节点 01// config 是传入配置 上面写着对应事件的 handler 02function tplParser(root, config){ 03 Array.from(root.children).forEach(child => { 04 // child 的属性 attributes 05 Array.from(child.attributes).forEach(attr => { 06 if (attr.name.startsWith('@') && attr.value in config){ 07 let eventName = attr.name.slice(1); 08 09 child.addEventListener( 10 // such as "click" 11 eventName, 12 // such as () => console.log('clicking!') 13 config[attr.value] 14 ); 15 } 16 }); 17 18 // Just Regard Child As A Tree's Root 19 tplParser(child, config); 20 }) 21}
# 下一步是什么 ↵
在完成了
@click
之后我立即想到了完成数据绑定的思路,具体请看我的下篇博文