Use JSX without React 2:编译JSX和Co
目录
- 1 前言
- 2 babel
- 3 webpack配置说明
- 1 webpack.config.js
- 2 .babelrc
- 4 解析JSX转义后的语法
- 5 完整项目
- 6 参考
前言
这篇文章是上一篇Use JSX without React 1: Virtual DOM的继续,如果你对Virtual DOM不够了解,建议先阅读上一篇。
这篇文章将介绍脱离React的情况下,使用webpack配置打包JSX转义成JavaScript,并将会简单实现几种常用的React组件。
cr:本文大部分参考Lessons learned using JSX without React
babel
转义JavaScript第一当然会想到到巴别塔,JSX语法插件需要用到的主要有
yarn add -D @babel/cli @babel/core @babel/plugin-syntax-jsx @babel/plugin-transform-react-jsx
个人比较喜欢使用webpack,所以这里以webpack工程化为例。为直接进入正题,这里就不介绍npm init
等初始化命令了,完整代码请戳这里👉https://github.com/1uokun/jsx-render/blob/master/examples/webpack.config.js
webpack配置说明
webpack.config.js
module: {
rules: [{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
extends: path.resolve(__dirname, './.babelrc'),
},
}]
},
.babelrc
配置{ "pragma": "dom" }
暴露一个dom(tag, attrs, ...children)
方法用于解析JSX转义JavaScript之后的DOM树
{
"presets": ["@babel/preset-env"],
"plugins": [
"@babel/plugin-syntax-jsx",
["@babel/plugin-transform-react-jsx", { "pragma": "dom" }]
]
}
解析JSX转义后的语法
根据上一篇文章我们得知,JSX转义后的语法为
"use strict";
var dom= React.createElement;
var ul = dom('ul', {id: 'list'}, [
dom('li', {class: 'item'}, ['Item 1']),
dom('li', {class: 'item'}, ['Item 2']),
dom('li', {class: 'item'}, ['Item 3'])
])
dom方法
不依赖React,我们就需要自己写dom()
方法替代React. createElement
,具体实现方法如下:
确定参数 tag
, attrs
, ...children
tag 存在的类型[1]有
DOM节点
const element = document.createElement(tag)
SVG
document.createElementNS('http://www.w3.org/2000/svg', tag)
function
tag类型为function
情况存在于函数名作为标签引用,也就是组件,组件类型也可分成既有组件和自定义组件
1.既有组件
什么是既有组件?就是类库里已经定义好的组件,比如React.Fragments
或者React.Portals
组件,既然是提前定义好的,在节点类型判断中就要考虑进去
2.自定义组件
可以是Dumb Component 哑巴组件,如<HeadLine />
也可以是Smart Component 智能组件,如<Dialog>{this.props.children}</Dialog>
if (typeof tag === 'function') {
tag.defaultProps = tag.defaultProps || {}
const result = tag(Object.assign({}, tag.defaultProps, attrs, { children }))
switch (result) {
case 'FRAGMENT':
return createFragmentFrom(children)
// createFragmentFrom为自定义函数,解析children
// Fragments和Portals组件具体作用请阅读React文档
case 'PORTAL':
tag.target.appendChild(createFragmentFrom(children))
return document.createComment("Portal Used")
default:
return result
}
}
attrs 存在的类型[2]有
style
// old: style={{color:'red',fontSize:'5px'}}
// new: { style: { color: 'red', fontSize: '5px' } },
function objectToStyleString(styles) {
return Object.keys(styles)
.map(prop => `${prop}: ${styles[prop]}`)
.join(';')
}
element.style.cssText = objectToStyleString(attrs[prop])
className
element.setAttribute('class', attrs[prop])
xlinkHref (SVG xlink:href)
element.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', attrs[prop])
dangerouslySetInnerHTML (替代innerHTML)[1]
element.innerHTML = attrs[prop].__html;
events(上一篇有介绍)
element.addEventListener(...)
实现
接下来就是我们熟悉的React语法
import dom from './dom.js' //每一个jsx文件都必须引入
function HeadLine(){
return <Head><div>头部信息</div><hr/></Head>
}
function Main(){
return (
<Fragments>
<HeadLine />
<div>...</div>
</Fragments>
)
}
完整项目
https://github.com/alecsgone/jsx-render
参考
- [1] DOM Elements – React
reactjs.org
- [2] Styling and CSS – React
reactjs.org
- [3] 不引入react的情况下使用JSX
唐霜
- [4] Lessons learned using JSX without React
medium.com
- [5] https://github.com/alecsgone/jsx-render
github