前言
在編寫組件時, 最讓我迷惑的是在我編寫純函數組件, 為什麼沒有在代碼裡面調用 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 https://github.com/anthonyshort/deku#readme
- vslinko/babel-plugin-react-require - GitHub
来源: https://juejin.im/entry/5c75e80b51882540a239248c