在編寫組件時,最令我感到困惑的是,當我在創建純函數型組件時,為何在代碼中未明確調用 React 卻仍需導入 React。經過一番深入研究,我發現這涉及到 JSX 轉譯的相關設定。然而,出於節省時間和提高效率的考慮,我選擇安裝了 babel-plugin-react-require 插件,以實現自動引入 React 的功能。本文將分享我的研究過程和一些反思。
比如說我們有一個 App.js 的文件,這裡相等於 Hello World 的設定。
import React from "react";
const App = () => (
<div>Hello World!!!</div>
);
export default App;
原因
在 Babel 轉譯我們的 App.js 的時候,會把 JSX 語法糖轉換為 React.createElement
方法。
var App = function App() {
return React.createElement(
"div",
null,
"Hello World!!!"
);
};
你可以在 Babel REPL 來看到 Babel 是如何轉譯。
自動引入 React
對於 React 系統的前端項目來說,我們能不能直接寫純函數組件,而不需要在頂部引進 React 語句:import React from 'react';
?
答案也是可以的,我們可以透過 babel-plugin-react-require
來自動辨認無狀態組件,然後如果是的話,自動引進 React。
安裝方法
我們可以透過 npm 直接安裝
npm install babel-plugin-react-require --save-dev
然後在 .babelrc
加入 react-require
{
"plugins": [
"react-require"
]
}
值得注意的是, react-require
需要在 transform-es2015-modules-commonjs
之前引入,因為插件需要 ES2015 模塊化語句來導入 React
到域裡面。
例子
如果我們寫了一個純函數組件,代碼如下:
export default function Component() {
return (
<div />
);
}
babel-plugin-react-require
插件會自動將上面的代碼轉譯為下面的代碼:
import React from 'react';
export default function Component() {
/* 下面的代碼交給 Babel 繼續編譯 */
return (
React.createElement('div')
);
}
自定義轉譯使用方法
那麼我們可以再想一下,如果用的不是 React,而是其他 React 生態圈裡面的方法的話。比如說 deku,它是一個用來使用純函數和虛擬 DOM 渲染界面的工具庫。它也是 React 生態圈裡面的一份子。
我們能不能改寫轉譯後的方法,不使用 React.createElement
方法?
答案是可以的。
@babel/plugin-transform-react-jsx 允許我們在文件的頂部加入 /** @jsx 方法名稱 */
,或者是在 .babelrc
全局修改。
比如說這樣的代碼:
/** @jsx dom */
var { dom } = require("deku");
var profile = <div>
<img src="avatar.png" className="profile" />
<h3>{[user.firstName, user.lastName].join(' ')}</h3>
</div>;
會轉譯為下面的代碼:
/** @jsx dom */
var dom = require("deku").dom;
var profile = dom("div", null,
dom("img", { src: "avatar.png", className: "profile" }),
dom("h3", null, [user.firstName, user.lastName].join(" "))
);
React 碎片
那麼 React 16.2.0 新增的 Fragments 呢?我們可以透過 /** @jsxFrag 函數名稱 */
來達到轉譯後的修改目的。
輸入:
/** @jsx dom */
/** @jsxFrag DomFrag */
var { dom, DomFrag } = require("deku");
var descriptions = items.map(item => (
<>
<dt>{item.name}</dt>
<dd>{item.value}</dd>
</>
));
輸出:
/** @jsx dom */
/** @jsxFrag DomFrag */
var { dom, DomFrag } = require("deku");
var descriptions = items.map(item => dom(
DomFrag,
null,
dom("dt", null, item.name),
dom("dd", null, item.value)
));
你可以打開 Babel REPL - React Fragment 來實時調試代碼。
安裝方法
你需要安裝 @babel/plugin-transform-react-jsx
來轉換 JSX 語法,如果沒有安裝的話,那麼你可以透過 npm 安裝它。
npm install --save-dev @babel/plugin-transform-react-jsx
全局修改
在 .babelrc
寫入下面的片段:
{
"plugins": ["@babel/plugin-transform-react-jsx"]
}
我們可以透過全局修改 pragma
來修改 React.createElement,
{
"plugins": [
["@babel/plugin-transform-react-jsx", {
"pragma": "dom", // React.createElement
"pragmaFrag": "DomFrag", // React.Fragment
"throwIfNamespace": false, // 如果 XML 名稱空間的標籤名稱被使用的話,拋出異常
"useBuiltIns": false // 使用 Object.assign 而不是 Babel 內建的擴展工具 (extend)
}]
]
}
參考資料
- Why import React from “react” in a functional component?
- @babel/plugin-transform-react-jsx
- anthonyshort/deku - Github
- vslinko/babel-plugin-react-require - Github