删除构建步骤释放对通常过于强制的情况做出反应。
您是否曾经想不使用React没有构建步骤?是否曾经发现自己愿意使用React,但是您没有使用路线等构建单页应用程序?您是否只需要一个具有高度交互性的页面上的组件/小部件?
那么,为什么React首先需要一个构建步骤/转板器?您不能只是加载反应CDN并启动并运行吗?好吧,是的,但是您通常会使用模板,这就是JSX的来源 do 需要编译器。
没有JSX的反应
As it states on the React docs site,
JSX并不需要使用React。当您不想在构建环境中设置汇编时,使用无JSX的React尤为方便。
上面的链接页面继续显示以下JSX的示例,
class Hello extends React.Component {
render() {
return <div>Hello {this.props.toWhat}</div>;
}
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Hello toWhat="World" />);
以及编译的输出的外观
class Hello extends React.Component {
render() {
return React.createElement("div", null, `Hello ${this.props.toWhat}`);
}
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(React.createElement(Hello, { toWhat: "World" }, null));
即使是provides a playground编写JSX,并查看最终的JavaScript输出将是什么。漂亮整洁。
太好了,我们不需要建立步骤。但是对我来说,与之合作并不是超级实用的。现代JavaScript框架的伟大之处是,它们让您与HTML动态合作。在React.createElement
上裸露金属对我来说并不像是一个很棒的开发人员体验。
有一些库的选择可以轻松使用React.createElement
,例如react-hyperscript。它提供了一些用于创建元素的速记,但是对我来说,从您将要输出的HTML中感觉有点太抽象了。
var h = require("react-hyperscript");
var React = require("react");
var AnotherComponent = require("./another-component");
module.exports = React.createClass({
render: function render() {
return h("div.example", [
h("h1#heading", "This is hyperscript"),
h("h2", "creating React.js markup"),
h(AnotherComponent, { foo: "bar" }, [
h("li", [h("a", { href: "http://whatever.com" }, "One list item")]),
h("li", "Another list item"),
]),
]);
},
});
但对我来说,(我认为许多React开发人员),写作HTML感觉最自然。您的组件最终使HTML呈现,因此您的模板以HTML般的结构编写是有道理的,因此JSX的受欢迎程度。
HTM模板
然而,有一个库更接近JSX (类似于HTML的感觉),但不需要构建步骤。 htm。 HTM使用标记模板来利用模板字面作为本机JavaScript模板字符串。如果您没有使用tagged templates玩,我鼓励您检查一下,这是一个非常强大的功能,最近已成为JavaScript的一部分。
如果您打算在任何时间内使用HTM,我强烈鼓励VSCode extension强调您的HTML字符串为HTML。它不仅为您的JavaScript内部的HTML提供了语法突出显示,而且还可以按照您的期望将其格式化。
。
标记的模板允许您将模板字符串发送到函数,以在字符串上运行其他处理/操作。您可以阅读有关tagged templates here的更多信息。但是重要的是要知道它们在all major browsers中得到了支持。
让我们看看一些例子
好吧,让我们看一下超级简单的应用程序将使用HTM而不是JSX的样子。在此示例组件中,我们将只有一个带有名称和爱好的标题,然后才能更新这些值。
要开始,让我们首先加载我们需要将cdn的javascript加载到我们的html文件中,然后启动我们的应用程序。
<head>
<body>
<div id="app">
<!-- the application will be rendered here -->
</div>
<script
crossorigin
src="https://unpkg.com/react@18/umd/react.production.min.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"
></script>
<script type="module">
import htm from "https://unpkg.com/htm?module";
const html = htm.bind(React.createElement);
import App from "./App.js";
ReactDOM.render(html`<${App} />`, document.getElementById("app"));
</script>
</body>
</head>
因此,我们已将React和Reactdom加载到我们的HTML文件中,然后将HTM和我们的<App/>
组件加载为模块。
(不幸的是,不幸的是没有ESM版本可以从CDN加载),因此我们将它们仅在页面上的单独脚本标签中加载,然后将它们从窗口对象中引用。
将html绑定到React.createElement
htm是一个通用库,因此,为了使其与React一起使用,我们必须将其绑定到React.Createlement。这本质上将CreateElement设置为我们的HTML字符串的标记模板函数。
import htm from "https://unpkg.com/htm?module";
const html = htm.bind(React.createElement);
然后我们渲染我们的应用程序组件
ReactDOM.render(html`<${App} />`, document.getElementById("app"));
让我们看一下我们的应用程序组件。您会注意到这与您使用JSX所写的内容有多相似。
import { useState, createElement } from "react";
import htm from "htm";
import Hobbies from "./Hobbies.js";
import "../index.css";
const html = htm.bind(createElement);
function App() {
const [name, setName] = useState("Dave");
const [hobbies, setHobbies] = useState("bonsai, sewing, running");
return html`<div class="container">
<article>
<header>
<hgroup>
<h1>Hello there!</h1>
<h2>
My name is <mark>${name}</mark>, and my hobbies include
<hr />
${hobbies
.split(",")
.map((hobby) => html`<kbd>${hobby.trim()}</kbd>`)}
</h2>
</hgroup>
</header>
<label>
Name
<input value=${name} onChange=${(ev) => setName(ev.target.value)} />
</label>
<label>
Hobbies
<${Hobbies} hobbies=${hobbies} setHobbies=${setHobbies} />
</label>
</article>
</div>`;
}
export default App;
App组件依次呈现看起来像这样的爱好组件。我本可以将它们组合在一起,只有一个组件,但是我想举一个加载子组件的示例,因为我们看到了<${Hobbies}>
import { createElement } from "react";
import htm from "htm";
const html = htm.bind(createElement);
function Hobbies({ hobbies, setHobbies }) {
return html`<textarea
value=${hobbies}
onChange=${(ev) => setHobbies(ev.target.value)}
></textarea>`;
}
export default Hobbies;
HTM和JSX之间的差异
尽管正如我们所讨论的那样,它看起来与JSX非常相似,进而又是HTML。我想指出的是一些关键区别。
插值组件名称
正如您可能已经看到的那样,您必须在渲染时“ interpaly” 组件名称,而不是像JSX中的自定义DOM元素一样使用它们。
在JSX中
<label>
Hobbies
<Hobbies hobbies={hobbies} setHobbies={setHobbies}></Hobbies>
</label>
htm等效
<label>
Hobbies
<${Hobbies} hobbies=${hobbies} setHobbies=${setHobbies} />
</label>
您可以在课堂上使用课程而不是className!
这对我来说很大。一直困扰着我,您必须将className
用于JSX中的类属性。我不确定为什么会像这样打扰我,但是每次我在JSX中的元素中添加一堂课时,我都会考虑一下。
<div className="my-class"></div>
vs
<div class="my-class"></div>
返回HTML时,您必须调用HTML方法
例如,当我们在循环中列出我们的爱好时,我们正在返回HTML,因此我们必须调用HTML方法。
${hobbies.split(",")
.map((hobby) => html`<kbd>${hobby.trim()}</kbd>`)}
引号是可选的
围绕元素属性的引号动态或静态是完全可选的
<div class="foo"></div>
自关闭标签
htm会让您在任何事物上使用自关闭标签。
<div />
关闭组件标签
由于HTM要求您在渲染时插入组件名称,因此它具有方便的功能,可让您与<//>
关闭组件,而不必再次重做名称。
<${Footer}>footer content<//>
传播道具
如果您想将组件的道具分配给元素属性,通常是root ,在JSX中,您将这样做。
<div {...props}>
在htm中,您会做同样的事情,只有字符串插值外部的传播操作员。
<div ...${props}>
布尔属性
我真的很喜欢这个功能,如果您曾经在诸如checked
或selected
之类的布尔属性上挣扎,也可能会。
const checked = true;
return html`<input type="checkbox" checked=${checked} /> `;
多个根元素
JSX不允许您具有多个根元素。 htm做!如果您发现自己将自己的组件包裹在React.Fragment
中,以便您可以按照自己的方式构造自己的dom,那么您会喜欢的。
在JSX中
return <React.Fragment>
<div>root item one</div>
<div>root item two</div>
</React.Fragment>
htm
return html`<div>root item one</div>
<div>root item two</div>`;
HTML评论
您可以利用模板中的普通HTML注释。在JSX
中不可能预编译设置
我知道您选择HTM而不是JSX的巨大原因是将构建步骤抛在后面。话虽如此,我最近在一个出于其他原因需要建立步骤的项目上使用了HTM。在几个小的上对HTM感到满意(无构建)项目,我想继续使用它。
我错过的无编译设置的主要内容之一是热模块重新加载。一旦您使用HMR,它就会上瘾,很难返回每小时400次重新加载:)
我将举一个快速示例,说明如何将HTM集成到Berierplate create-react-app
应用程序中。
npm i create-react-app react-htm
npm i htm --save-dev
npm serve
在您的输入文件中,index.js
将文件更改为以下内容。
import { createRoot } from "react-dom/client";
import { createElement } from "react";
import htm from "htm";
import App from "./App.js";
const html = htm.bind(createElement);
const root = createRoot(document.getElementById("root"));
root.render(html`<${App} />`);
然后,在App.js
文件中,将组件更改为以下
import logo from "./logo.svg";
import "./App.css";
import { createElement } from "react";
import htm from "htm";
const html = htm.bind(createElement);
function App() {
return html`<div class="App">
<header class="App-header">
<img src="${logo}" class="App-logo" alt="logo" />
<p>Edit <code>src/App.js</code> and save to reload.</p>
<a
class="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>`;
}
export default App;
您现在正在使用htm为模板运行应用程序。您的构建和HMR将完全按照您的习惯工作。我希望您喜欢深入研究JSX的替代方案,并在未来的情况下发现它很有用。