這篇的切入點是給有學過或用過 React 的大家,接下來希望能提供一系列環繞 React 的教學文。
也是本人在深入學習 React 相關繼續的紀錄。
Overview
本篇會實做 React.createElement
and ReactDOM.render
兩個 function 來了解 jsx。
大家在寫 React 一定脫離不了 Babel, 基本上就是直接使用 @babel/preset-env
包含所有的 Feature。
但是, 我們不一樣! 要用就要用最差的,只拿我們用得到的。
$ yarn add @babel/cli @babel/core @babel/node @babel/plugin-transform-react-jsx
扒開 JSX 的皮吧! createElement
預設的 JSX transform 到底做了什麼?
先定義 .babelrc
// .babelrc
{
"plugins": ["@babel/plugin-transform-react-jsx"]
}
我們來一步一步拆解,用一些簡單的 jsx 範例,會有什麼結果呢?
範例一
const HelloWorld = <div>Hello World!</div>;
執行 babel index.js
後, 會輸出
const HelloWorld = React.createElement("div", null, "Hello World!");
會發現其實就是轉成一個名為 React.createElement
的 Function 執行。
範例二
const HelloWorld = <div id="hello-world" disable={true}>Hello World!</div>;
第 2 個變數就是用一個 object
表示 html attribute
。
const HelloWorld = React.createElement(
"div",
{
id: "hello-world",
disable: true
},
"Hello World!"
);
範例三
const HelloWorld = (
<div>
<div>Hello</div>
<div>World</div>
!
</div>
);
第 3 個變數後,就是表示 element
的 children
。
const HelloWorld = React.createElement(
"div",
null,
React.createElement("div", null, "Hello"),
React.createElement("div", null, "World"),
"!"
);
範例四
const HelloWorld = (
<div id="id-parent">
<div>Hello</div>
<p class="text-center">
<a href="/" target="_blank">World</a>
</p>
!
</div>
);
那理所當然的輸出就是
const HelloWorld = React.createElement(
"div",
{
id: "id-parent"
},
React.createElement("div",null, "Hello"),
React.createElement(
"p",
{
"class": "text-center"
},
React.createElement(
"a",
{
href: "/",
target: "_blank"
},
"World"
)
),
"!"
);
然而,這個 tranform 的結果,可以丟到 render()
取得最後的結果
$ yarn add react react-dom
const React = require('react');
const render = require('react-dom/server').renderToStaticMarkup;
const HelloWorld = (
<div id="id-parent">
<div>Hello</div>
<p className="text-center">
<a href="/" target="_blank">World</a>
</p>
!
</div>
);
const staticString = render(HelloWorld);
console.log(staticString);
Exec: babel-node index.js
Output:
<div id="id-parent"><div>Hello</div><p class="text-center"><a href="/" target="_blank">World</a></p>!</div>
那麼,知道這些能幹麻?
可以自己實做 React.createElement
以及 render
!!
My JSX to JS
更改 .babelrc
的 pragma
那麼之後的 React.createElement
就會變為 myCreateElement
{
"plugins": [
["@babel/plugin-transform-react-jsx", {
"pragma": "myCreateElement" // default pragma is React.createElement
}]
]
}
1. Implement myCreateElement
// dom.js
const myCreateElement = (nodeName, attrs, ...childrens) => ({
nodeName,
attrs,
childrens
});
Sample Output:
{ nodeName: 'div',
attrs: {
id: 'id-parent'
},
childrens: [
{ nodeName: 'div', attrs: null, childrens: [Array] },
{ nodeName: 'p', attrs: [Object], childrens: [Array] },
'!'
]
}
2. Implement render
// dom.js
const render = (domTree) => {
if (typeof domTree === "string") return domTree;
const { nodeName, attrs, childrens } = domTree;
const attrsStr = Object
.entries(attrs || {})
.map(([key, val]) => ` ${key}="${val}"`)
.join('');
const childrenStrs = (childrens || []).map(c => render(c)).join('');
return `<${nodeName}${attrsStr}>${childrenStrs}</${nodeName}>`;
}
完整的 dom.js
const myCreateElement = (nodeName, attrs, ...childrens) => ({
nodeName,
attrs,
childrens
});
const render = (domTree) => {
if (typeof domTree === "string") return domTree;
const { nodeName, attrs, childrens } = domTree;
const attrsStr = Object
.entries(attrs || {})
.map(([key, val]) => ` ${key}="${val}"`)
.join('');
const childrenStrs = (childrens || []).map(c => render(c)).join('');
return `<${nodeName}${attrsStr}>${childrenStrs}</${nodeName}>`;
}
module.exports = {
myCreateElement,
render
}
然而丟回剛剛上面那段 code
-const React = require('react');
-const render = require('react-dom/server').renderToStaticMarkup;
+const { myCreateElement, render } = require('./dom');
const HelloWorld = (
<div id="id-parent">
<div>Hello</div>
<p className="text-center">
<a href="/" target="_blank">World</a>
</p>
!
</div>
);
const staticString = render(HelloWorld);
console.log(staticString);
Output:
<div id="id-parent"><div>Hello</div><p class="text-center"><a href="/" target="_blank">World</a></p>!</div>
登登! 有沒有忽然覺得自己很像稍微了解 JSX 跟 React 之間的互動了呢?
希望本篇能給需要的人幫助 :)
Summary
實做簡單版的 React.createElement
以及 render
來理解 JSX。
希望各位喜歡 :3