从零开始一步一步写一个简单的Virtual DOM实现 2 :Props&Event处理

第一篇地址

第二部分原文:write-your-virtual-dom-2-props-events

完整的示例代码地址

Props

首先我们要回顾下前文讲的一个有些偏差的小点,假设我们在JSX中只写一个最简单的Div:

Babel会自动将该JSX转化为如下的DOM表达式:

{ type: ‘’, props: null, children: [] }

注意,这里的props默认是null,我们在之前的文章中并没有关注到这个属性,而本部分则是要讲解Virtual DOM中Props的用法。一般来说,无论在哪种编程环境下都要尽量避免Null的出现,因此我们首先来改造下h函数,使得其能够默认返回一个空的Object,而不是Null:

function h(type, props, …children) { return { type, props: props || {}, children };}

Setting Props:设置Props

接触过React的同学对于Props肯定不会陌生,而设置Props也就跟使用普通的HTML标签属性很类似:

而最终会转化为如下的表达式:

{  type: ‘ul’,  props: { className: ‘list’, style: ’list-style: none;’ }  children: []}

props对象中的每个键即为属性名,而值为属性值,一般来说我们只需要简单的调用一个setAttribute方法来讲这个Props中的键值对设置到DOM元素上即可:

function setProp($target, name, value) { $target.setAttribute(name, value);}

这个函数用于将单个的Prop值设置到DOM元素上,而对于props对象,我们要做的就是依次遍历:

function setProps($target, props) { Object.keys(props).forEach(name => {   setProp($target, name, props[name]); });}

你应该还记得那个用于创建元素的createElement方法吧,我们需要将setProps方法放置到元素成功创建之后:

function createElement(node) { if (typeof node === ‘string’) {   return document.createTextNode(node); } const $el = document.createElement(node.type); setProps($el, node.props); node.children   .map(createElement)   .forEach($el.appendChild.bind($el)); return $el;}

不要急,这还远远不够。React的初学教程中一直强调className与class的区别,在我们的setProps中也需要对于这些JS的保留字做一个替换,譬如:

另外,还有比较常见的就是对于DOM的布尔属性,譬如checked、disabled等等的处理:

在真实的DOM节点上,如果是出现了false的情况,我们并不希望checked属性会出现,那么我们的Props函数就要能智能地进行判断:

function setBooleanProp($target, name, value) { if (value) {   $target.setAttribute(name, value);   $target[name] = true; } else {   $target[name] = false; }}

最后呢,要做的就是对于自定义的,即非标准的HTML属性进行一个过滤,这些属性只应该出现在JS对象上,而不应该出现在真实的DOM对象上:

function isCustomProp(name) { return false;}
function setProp($target, name, value) { if (isCustomProp(name)) {   return; } else if (name === ‘className’) {   $target.setAttribute(‘class’, value); } else if (typeof value === ‘boolean’) {   setBooleanProp($target, name, value); } else {   $target.setAttribute(name, value); }}

总结一下,本部分完整的JSX代码为:

/  @jsx h */function h(type, props, ...children) {  return { type, props: props || {}, children };}function setBooleanProp($target, name, value) {  if (value) {    $target.setAttribute(name, value);    $target[name] = true;  } else {    $target[name] = false;  }}function isCustomProp(name) {  return false;}function setProp($target, name, value) {  if (isCustomProp(name)) {    return;  } else if (name === 'className') {    $target.setAttribute('class', value);  } else if (typeof value === 'boolean') {    setBooleanProp($target, name, value);  } else {    $target.setAttribute(name, value);  }}function setProps($target, props) {  Object.keys(props).forEach(name => {    setProp($target, name, props[name]);  });}function createElement(node) {  if (typeof node === 'string') {    return document.createTextNode(node);  }  const $el = document.createElement(node.type);  setProps($el, node.props);  node.children    .map(createElement)    .forEach($el.appendChild.bind($el));  return $el;}//--------------------------------------------------const f = (    item 1);const $root = document.getElementById('root');$root.appendChild(createElement(f));

Diffing Props:Props变化比较

现在我们已经创建了带有Props属性的元素,下一个需要考虑的就是应该如何应用到我们上文提到的Diff算法中。首先我们要来看下如何从真实的DOM中移除某些Props:

function removeBooleanProp($target, name) { $target.removeAttribute(name); $target[name] = false;}function removeProp($target, name, value) { if (isCustomProp(name)) {   return; } else if (name === ‘className’) {   $target.removeAttribute(‘class’); } else if (typeof value === ‘boolean’) {   removeBooleanProp($target, name); } else {   $target.removeAttribute(name); }}

然后我们需要写一个updateProp函数,来根据新旧节点的Props的变化进行恰当的真实DOM节点的修改,共有以下几种情况:

  1. 新节点移除了某个旧节点的Prop

超级产品经理

{/* this node will always be updated */}text

);

const g = (

 alert('hi!')}>item 11.       {/* this node will always be updated */}text

);

const $root = document.getElementById('root');
const $reload = document.getElementById('reload');

updateElement($root, f);
$reload.addEventListener('click', () => {
updateElement($root, g, f);
});

到这里我们就完成了一个最简单的Virtual DOM算法,不过其与真正能够投入实战的Virtual DOM算法还是有很大距离,进一步阅读推荐:1. 深度剖析:如何实现一个 Virtual DOM 算法1. 我的前端故事----React算法又是个什么鬼?!1. A Virtual DOM and diffing algorithm:一个比较复杂的Virtual DOM算法的实现1. simple-virtual-dom:一个简单的Virtual DOM的实现1. how-to-write-your-own-virtual-dom1. Virtual DOM Benchmark#name、props、function、target#

版权声明

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处。如若内容有涉嫌抄袭侵权/违法违规/事实不符,请点击 举报 进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部