jsx2html
場景
將下面的jsx轉換爲html
const items = ['one', 'two', 'three'];const SearchData = ({ data = [] }) => { let list = null; if (data.length) { list = data.map((l, k) => (<p key={k}>{l}</p>)); } else { list = (<p>暫無數據</p>); } return ( <div className="mod-search-data"> <div> <h3>匹配的員工:</h3> <div>{list}</div> </div> </div> ); };const jsx = ( <div className="foo"> <p>列表展示 {items.length} 項:</p> <div> <p>SEARCH DATA:</p> <SearchData data={items} /> </div> </div>);
方案
方案一:自己遍歷
所有的jsx你拿到的時候,都已經是 Babel 幫你轉義過的了。所以,你其實拿到的是轉義後的對象。所以你只需要將這對象轉成你想要的結果。我們知道 Props 除了 key、ref、children 這幾個特殊的,剩下的都對應到 dom 的 attribute。
做法
1、獲取 displayName 作爲 Tag
2、處理 props 作爲 attribute
3、如果有children,則重複1、2、3、4,遍歷 children,作爲子元素
4、拼裝 html 字符串
代碼
function getDisplayName(ele) { if (typeof ele.type === 'string') { return ele.type; } return ele.type.name || ele.type.displayName || 'No Name'; }function getAttrs(attrs) { return Object.keys(attrs).map(attr => (attr === 'children' ? '' : `${attr}="${attrs[attr]}"`)).join(''); }function transfer(ele) { if (typeof ele === 'string' || typeof ele === 'number') { return ele; } const props = ele.props || {}; const children = React.Children.toArray(props.children || []); const html = children.map(transfer); const tag = getDisplayName(ele); return `<${tag} ${getAttrs(props)}>${html.join('')}</${tag}>`; }console.log(transfer(jsx));// 如果函數式的組件你也需要解析的話,則需要執行這個函數// congsole.log(transfer(SearchData({items: data})))
類庫
方案二:通過 Babel 直接轉
我們可以指定 Babel 在編譯的時候調用某個函數。我們可以通過這個函數來生成我們需要的操作。
做法
方法一 配置 .babelrc
json { "plugins": [ ["transform-react-jsx", { "pragma": "dom" // default pragma is React.createElement }] ] }
但是這種方法修改全部的 babel 轉換行爲。非常不推薦
方法二
在代碼中加一個註釋/** @jsx h */,告訴 Babel ,用 h 函數處理 Babel 編譯後的行爲。參考WTF is JSX
代碼
/** @jsx h */function getDisplayName(ele) { if (typeof ele === 'string') { return ele; } return ele.name || ele.displayName || 'No Name'; }function h(name, attrs, ...children) { const html = Array.isArray(children) ? children.join('') : children; console.log('###################################'); console.log('name:', name); console.log('attrs:', attrs); console.log('children:', children); const attr = Object.keys(attrs || {}).map(a => `${a}='${attrs[a]}'`).join(' '); return `<${name} ${attr}>${html}</${name}>`; }console.log(jsx);
類庫
jsx2json
與上面的情況了類似,如果我們要將那部分 jsx 轉換爲 json 格式的怎麼辦呢?答案很簡單,不用刻意去轉(?!)。因爲 Babel 已經幫你轉過了。你需要做的是把 Babel 編譯後的 json 轉成你想要的格式。此外,剛纔的兩種方案也是生效的。不同的是,之前的返回值是一段 html 文本,現在需要返回 json 格式的。我們以上面的方案二舉例:
/** @jsx h */function h(name, attrs, ...children) { /* // 函數式的組件(functional component)請根據需要轉換 if (typeof name === 'function') { return name(attrs); } */ return { tag: name, attrs, children, }; } console.log(jsx);
html2jsx
場景
將下面的 html 轉換爲 jsx:
const html = ` <div className='foo' id="18" data-node="12"> <h1>Hi!</h1> <p>Here is a list of 3 items:</p> <ul> <li>one</li> <li>two</li> <li>three</li> </ul> </div>`;
方案
將 html 轉爲 jsx,實際上就是用 React.createElement 將 html 的結構重新生成一下,做到without jsx
做法
1、將 html 片段轉成 dom
2、讀取 dom 的 attributes, 處理特殊的 attribute,作爲 ReactElement 的 props
3、讀取 dom 的 tagName, 作爲 ReactElement 的 type
4、如果 dom 有 children,則重複 2、3、5步,將返回值作爲 ReactElement 的 children
5、返回 React.createElement(type, props, children)