写jsx_实现一个JSX渲染器

什么是JSX

JSX是一种Javascript的扩展语法,看起来就像是在写HTML标签:

const element = <h1 className="gretting">Hello World!</h1>;

但其实它只是一个语法糖, 下面以 React 中的JSX为例看看它的真面目.

React中的JSX

babel提供了编译JSX的插件, 我们来看看效果:

19bcacb4ac02b5969de9e496d2de6eae.png

可以看到我们的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

没毛病~ 至此我们就实现了一个简单的 JSX 渲染器

附上在线demo

参考

WTF is JSX

React Without JSX

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值