题目描述
HTML可以被当作某种意义上的 序列化→浏览器解释(反序列化)HTML文本→ 然后构建DOM tree。
除了基于XML的方案,我们也可以试试JSON。如果打印出React中对element的表示,像这样:
const el = <div>
<h1> this is </h1>
<p className="paragraph"> a <button> button </button> from <a href="https://bfe.dev"><b>BFE</b>.dev</a>
</p>
</div>;
console.log(el)
得到如下数据结构:
{
type: 'div',
props: {
children: [
{
type: 'h1',
props: {
children: ' this is '
}
},
{
type: 'p',
props: {
className: 'paragraph',
children: [
' a ',
{
type: 'button',
props: {
children: ' button '
}
},
' from',
{
type: 'a',
props: {
href: 'https://bfe.dev',
children: [
{
type: 'b',
props: {
children: 'BFE'
}
},
'.dev'
]
}
}
]
}
}
]
}
}
题目链接
思路分析
要实现jsx
<=> virtual dom
,上来不太好判断难度,从处理数据生成html的流程更符合平时开发逻辑,就从 vdom
-> jsx
开始吧!
最直观的规律,virtual dom
是一棵树,我们需要确定父子关系
- 一个
type
对应 *个children
children
包含字符串节点和带标签的节点- 一个
type
对应 一个props
props
中除了children
其余都是元素上的属性
以上4条理顺了,就可以开始写代码了
function render(json) {
// textNode
if (typeof json === 'string') {
return document.createTextNode(json);
}
// element
const {type, props: {children, ...attrs}} = json;
// 出现 type 就创建一个节点
const element = document.createElement(type);
// 给节点加上属性
Object.entries(attrs).forEach(([attr, value]) => element[attr] = value);
// 把 children 添加到节点内
const childrenArr = Array.isArray(children) ? children : [children];
childrenArr.forEach(child => element.append(render(child)));
return element;
}
然后需要完成jsx
=> vdom
,第一反应是 vdom
数据结构(长相)和 jsx
是一样的,是对数据结构进行扩展
大致应该是这样
function virtualize(element) {
const result = {
type: element.tagName,
props: {}
};
// 先添加属性
element.attributes.forEach... props[k] = v;
// 再添加 children,循环节点递归(添加属性和children)
element.childNodes.forEach... props.children = newChildren
}
顺着这个思路把代码补全:
function virtualize(element) {
const result = {
type: element.tagName.toLowerCase(),
props: {}
}
// attrs
element.attributes.forEach(attr => {
const name = attr.name === 'class' ? 'className' : attr.name;
result.props[name] = attr.value;
});
// children
const children = [];
element.childNodes.forEach(node => {
if (node.nodeType === 3) {
children.push(node.textContent);
} else {
children.push(virtualize(node));
}
});
result.props.children = children.length === 1 ? children[0] : children;
return result;
}
AC代码
function render(json) {
// textNode
if (typeof json === 'string') {
return document.createTextNode(json);
}
// element
const {type, props: {children, ...attrs}} = json;
// 出现 type 就创建一个节点
const element = document.createElement(type);
// 给节点加上属性
Object.entries(attrs).forEach(([attr, value]) => element[attr] = value);
// 把 children 添加到节点内
const childrenArr = Array.isArray(children) ? children : [children];
childrenArr.forEach(child => element.append(render(child)));
return element;
}
function virtualize(element) {
const result = {
type: element.tagName.toLowerCase(),
props: {}
}
// attrs
element.attributes.forEach(attr => {
const name = attr.name === 'class' ? 'className' : attr.name;
result.props[name] = attr.value;
});
// children
const children = [];
element.childNodes.forEach(node => {
if (node.nodeType === 3) {
children.push(node.textContent);
} else {
children.push(virtualize(node));
}
});
result.props.children = children.length === 1 ? children[0] : children;
return result;
}
总结
这是个 vdom
和 jsx
的简单转换,再次加强递归的操作,给之后的 React.createElement
做个铺垫。
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!