什么是JSX
JSX是一种Javascript的扩展语法,看起来就像是在写HTML标签:
const element = <h1 className="gretting">Hello World!</h1>;
但其实它只是一个语法糖, 下面以 React 中的JSX为例看看它的真面目.
React中的JSX
babel
提供了编译JSX的插件, 我们来看看效果:
![19bcacb4ac02b5969de9e496d2de6eae.png](https://i-blog.csdnimg.cn/blog_migrate/f761a477d34dff9998b6632a70b95c41.jpeg)
可以看到我们的JSX会被编译成一个 React.createElement
的函数调用. 而它会返回一个用来描述DOM的对象, 也就是我们常说的 虚拟DOM
, 大致结构如下:
// 简化后的返回对象大致如下:
{
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
}
};
现在就明白为什么使用 React 开发时, 每个使用到JSX的文件都需要引入 React 了吧~
React.createElement
有三个参数:
React.createElement(
type, // dom类型,如上面例子中的div, 或者React组件(函数式或者类组件)或者react fragment
config, // dom属性,比如id,class,事件
children // 子节点,字符串或者React.createElement生成的一个对象
)
以一个class组件为例:
class Hello extends React.Component {
render() {
return <div>Hello {this.props.toWhat}</div>;
}
}
ReactDOM.render(
<Hello toWhat="World" />,
document.getElementById('root')
);
相当于:
class Hello extends React.Component {
render() {
return React.createElement('div', null, `Hello ${this.props.toWhat}`);
}
}
ReactDOM.render(
React.createElement(Hello, {toWhat: 'World'}, null),
document.getElementById('root')
);
动手实现一个JSX渲染器
babel支持通过 /** @jsx xxx */
来自定义处理JSX的方法(默认情况下是React.createElement), 我们接下来自己实现一个 h
函数~
我们期望的效果如下:
// 处理前:
/** @jsx h */
let foo = <div id="foo">Hello!</div>;
// 处理后
var foo = h('div', {id:"foo"}, 'Hello!');
开始实现 h
函数:
function h(nodeName, attributes, ...args) {
const children = args.length ? [].concat(...args) : null
return {
nodeName,
attributes,
children
}
}
调用结果如下:
const vDom = h('div', {
id: 'foo'
}, 'hello')
// 返回
{
nodeName: 'div',
attributes: {
id: 'foo'
},
children: ['hello']
}
我们接下来实现一个 render
方法, 用来将 h
函数生成的vDom转换成真正的DOM元素:
function render(vnode) {
// 字符串则生成文本节点
if (typeof vnode === 'string') {
return document.createTextNode(vnode)
}
// 根据传入的type生成DOM元素
const dom = document.createElement(vnode.nodeName)
// 拷贝attributes
const attrs = vnode.attributes || {}
Object.keys(attrs).forEach((key) => dom.setAttribute(key, attrs[key]) )
// 递归处理子元素
if (vnode.children) {
vnode.children.forEach((child) => dom.appendChild(render(child)))
}
return dom
}
完成了, 可以看到整个逻辑非常清晰简单. 我们接下来去测试一下效果~
![e46d3a887050bc0762b7c665352a0973.png](https://i-blog.csdnimg.cn/blog_migrate/a570aae401c8c891441b10005cda1c83.jpeg)
没毛病~ 至此我们就实现了一个简单的 JSX 渲染器
附上在线demo
参考
WTF is JSX
React Without JSX