3

1.JSX和虚拟DOM

 1 year ago
source link: http://muyunyun.cn/blog/zv9zkp1d/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

JSX 和 虚拟 DOM





const element = (
<div className="title">
hello<span className="content">world!</span>
</div>

JSX 是一种语法糖, 经过 babel 转换结果如下, 可以发现实际上转化成 React.createElement() 的形式:

扩展: babel 执行机制





var element = React.createElement(
"div",
{ className: "title" },
"hello",
React.createElement(
"span",
{ className: "content" },
"world!"

打印 element, 结果如下:





attributes: {className: "title"}
children: ["hello", t] // t 和外层对象相同
key: undefined
nodeName: "div"

因此, 我们得出结论: JSX 语法糖经过 Babel 编译后转换成一种对象, 该对象即所谓的虚拟 DOM, 使用虚拟 DOM 能让页面进行更为高效的渲染。

我们按照这种思路进行函数的构造:





const React = {
createElement
function createElement(tag, attr, ...child) {
return {
attributes: attr,
children: child,
key: undefined,
nodeName: tag,
const element = (
<div className="title">
hello<span className="content">world!</span>
</div>
console.log(element) // 打印结果符合预期
// attributes: {className: "title"}
// children: ["hello", t] // t 和外层对象相同
// key: undefined
// nodeName: "div"

虚拟 DOM 转化为真实 DOM

上个小节介绍了 JSX 转化为虚拟 DOM 的过程, 这个小节接着来实现将虚拟 DOM 转化为真实 DOM (页面上渲染的是真实 DOM)。

我们知道在 React 中, 将虚拟 DOM 转化为真实 DOM 是使用 ReactDOM.render 实现的, 使用如下:





import ReactDOM from 'react-dom'
ReactDOM.render(
element, // 上文的 element, 即虚拟 dom
document.getElementById('root')

接着来实现 ReactDOM.render 的逻辑:





const ReactDOM = {
render
* 将虚拟 DOM 转化为真实 DOM
* @param {*} vdom 虚拟 DOM
* @param {*} container 需要插入的位置
function render(vdom, container) {
if (_.isString(vdom) || _.isNumber(vdom)) {
container.innerText = container.innerText + vdom // fix <div>I'm {this.props.name}</div>
return
const dom = document.createElement(vdom.nodeName)
for (let attr in vdom.attributes) {
setAttribute(dom, attr, vdom.attributes[attr])
vdom.children.forEach(vdomChild => render(vdomChild, dom))
container.appendChild(dom)
* 给节点设置属性
* @param {*} dom 操作元素
* @param {*} attr 操作元素属性
* @param {*} value 操作元素值
function setAttribute(dom, attr, value) {
if (attr === 'className') {
attr = 'class'
if (attr.match(/on\w+/)) { // 处理事件的属性:
const eventName = attr.toLowerCase().substr(2)
dom.addEventListener(eventName, value)
} else if (attr === 'style') { // 处理样式的属性:
let styleStr = ''
let standardCss
for (let klass in value) {
standardCss = humpToStandard(klass) // 处理驼峰样式为标准样式
value[klass] = _.isNumber(+value[klass]) ? value[klass] + 'px' : value[klass] // style={{ className: '20' || '20px' }}>
styleStr += `${standardCss}: ${value[klass]};`
dom.setAttribute(attr, styleStr)
} else { // 其它属性
dom.setAttribute(attr, value)

至此, 我们成功将虚拟 DOM 复原为真实 DOM, 展示如下:

a898514bd3d08df4366e5ceb7843cddf.jpg-400

另外配合热更新, 在热更新的时候清空之前的 dom 元素, 改动如下:





const ReactDOM = {
render(vdom, container) {
container.innerHTML = null
render(vdom, container)

JSX 经过 babel 编译为 React.createElement() 的形式, 其返回结果就是 Virtual DOM, 最后通过 ReactDOM.render() 将 Virtual DOM 转化为真实的 DOM 展现在界面上。流程图如下:

5e451855ccc9017708b57164f9e221c6.jpg-400

如下是一个 react/preact 的常用组件的写法, 那么为什么要 import 一个 React 或者 h 呢?





import React, { Component } from 'react' // react
// import { h, Component } from 'preact' // preact
class A extends Component {
render() {
return <div>I'm componentA</div>
render(<A />, document.body) // 组件的挂载

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK