JavaScript 是词汇,而 React 是辅助工具。
责任编辑力求用最简约的形式让你进阶 React,大前提是你已介绍 JavaScript 和 HTML。0x01 从 JavaScript 到 React
许多要要懂的 JavaScript 基本原理
假如你在自学 React 的与此同时,也在自学 JavaScript,所以这儿详列了许多要要懂的 JavaScript 基本原理,来协助你更快地自学 React。
函数 和 斜线抒发式第一类字符串或其常见形式重构抒发式模板字符串前提操作符JS 模组化和引入求出形式
责任编辑将不能深入细致传授有关 JavaScript 各方面的科学知识,你无须十分通晓 JavaScript 就可以自学 React,但以内的许多基本原理是最适宜新手掌控的 JavaScript 的关键科学知识。
总之,你也能埃唐佩县那些基本基本原理间接步入上面段落的自学,当碰到不认知的难题再再来翻看这儿的基本原理。
图形 UI
要认知 React 怎样组织工作,具体来说要弄清楚应用程序是怎样说明你的标识符并转换成 UI 的。
当采用者出访两个页面时,伺服器将回到几段 html 标识符到应用程序,它可能看上去是这样的:
应用程序写作了 html 标识符,并将它形式化为 DOM。
什么是 DOM
DOM 是两个 html 原素的无性抒发,它是并行你的标识符和 UI 间的公路桥,它整体表现为两个父女关系的树型内部结构。
你能采用 JavaScript 或 DOM 的内建形式来监听采用者事件,操作 DOM 包括,查询、插入、更新、删除界面上特定的原素,DOM 操作不仅允许你定位到特定的原素上,并且允许你修改它的内容及样式。
小问答:你能通过操作 DOM 来修改页面内容吗?采用 JavaScript 及 DOM 形式来更新 UI
让我们一起来尝试怎样采用 JavaScript 及 DOM 形式来添加两个 h1 标签到你的项目中去。
打开我们的标识符编辑软件,然后创建两个新的 index.html 的文件,在文件中加入以下标识符:
<!– index.html –>
<html>
<body>
<div></div>
</body>
</html>
然后给定 div 标签两个特定的 id ,便于后续我们能定位它。
<!– index.html –>
<html>
<body>
<div id=“app”></div>
</body>
</html>
要在 html 文件中编写 JavaScript 标识符,我们需要添加 script 标签
<!– index.html –>
<html>
<body>
<div id=“app”></div>
<script type=“text/javascript”></script>
</body>
</html>
现在,我们能采用 DOM 提供的 getElementById 形式来通过标签的 ID 定位到指定的原素。
<!– index.html –>
<html>
<body>
<div id=“app”></div>
<script type=“text/javascript”>
const app = document.getElementById(app);
</script>
</body>
</html>
你能继续采用 DOM 的一系列形式来创建两个 h1 标签原素,h1 原素中能包含任何你希望展示的文本。
<!– index.html –>
<html>
<body>
<div id=“app”></div>
<script type=“text/javascript”>
// 定位到 id 为 app 的原素
const app = document.getElementById(app);
// 创建两个 h1 原素
const header = document.createElement(h1);
// 创建两个文本节点
const headerContent = document.createTextNode(
Develop. Preview. Ship. ,
);
// 将文本节点添加到 h1 原素中去
header.appendChild(headerContent);
// 将 h1 原素添加到 id 为 app 的原素中去 app.appendChild(header);
</script>
</body>
</html>
至此,你能打开应用程序来预览一下目前的成果,不出意外的话,你应该可以看到一行采用 h1 标签的大字,写道:Develop. Preview. Ship.
HTML vs DOM
此时,假如你打开应用程序的标识符审查功能,你会注意到在 DOM 中已经包含了刚才创建的 h1 标签,但源标识符的 html 中却并没有。换言之,你所创建的 html 标识符中与实际展示的内容是不同的。
这是因为 HTML 标识符中展示的是初始化的页面内容,而 DOM 展示的是更新后的页面内容,这儿尤指你通过 JavaScript 标识符对 HTML 所改变后的内容。
采用 JavaScript 来更新 DOM,是十分有用的,但也往往比较繁琐。你写了如下所以多内容,仅仅用来添加一行 h1 标签。假如要编写两个大许多的项目,或者团队开发,就感觉有些杯水车薪了。
<!– index.html –>
<script type=“text/javascript”>
const app = document.getElementById(app);
const header = document.createElement(h1);
const headerContent = document.createTextNode(Develop. Preview. Ship.);
header.appendChild(headerContent);
app.appendChild(header);
</script>
以内这个例子中,开发者花了大力气来“指导”计算机该怎样做事,但这似乎并不太友好,或者有没有更友好的形式让计算机迅速认知我们希望达到的样子呢?
命令式 vs 声明式编程
以内就是两个很典型的命令式编程,你一步一步的告诉计算机该怎样更新采用者界面。但对于创建采用者界面,更快的形式是采用声明式,因为那样能大大加快开发效率。相较于编写 DOM 形式,最好有种形式能声明开发者想要展示的内容(本例中就是那个 h1 标签以及它包含的文本内容)。
换句话说就是,命令式编程就像你要吃两个披萨,但你得告诉厨师该怎样一步一步做出那个披萨;而声明式编程就是你告诉厨师你要吃什么样的披萨,而无须考虑怎么做。
而 React 正是那个“懂你”的厨师!
React:两个声明式的 UI 库
作为两个 React 开发者,你只需告诉 React 你希望展示什么样的页面,而它会自己找到形式来处理 DOM 并指导它正确地展示出你所要的效果。
小问答:你觉得以下哪句话更像声明式?
A:我要吃一盘菜,它要先放花生,然后放点鸡肉丁,接着炒一下…
B:来份宫保鸡丁0x02 快速进阶 React
要想在项目中采用 React,最简单的形式就是从外部 CDN(如:http://unpkg.com)引入两个 React 的包:
react:React 核心库react-dom:提供用于操作 DOM 特定形式的库<!– index.html –>
<html>
<body>
<div id=“app”></div>
<script src=“https://unpkg.com/react@17/umd/react.development.js”></script>
<script src=“https://unpkg.com/react-dom@17/umd/react-dom.development.js”></script>
<script type=“text/javascript”>
const app = document.getElementById(app);
</script>
</body>
</html>
这样就无须采用纯 JavaScript 来间接操作 DOM 了,而要采用来自 react-dom 中的 ReactDOM.render() 形式来告诉 React 在 app 标签中间接图形 h1 标签或其文本内容。
<!– index.html –>
<html>
<body>
<div id=“app”></div>
<script src=“https://unpkg.com/react@17/umd/react.development.js”></script>
<script src=“https://unpkg.com/react-dom@17/umd/react-dom.development.js”></script>
<script type=“text/javascript”>
const app = document.getElementById(app);
ReactDOM.render(<h1>Develop. Preview. Ship. </h1>, app);
</script>
</body>
</html>
但当你在应用程序中运行的时候,它会报两个语法错误:
因为标识符中的 <h1>Develop. Preview. Ship. </h1> 并不是 JavaScript 标识符,而要 JSX。
什么是 JSX
JSX 是一种 JS 的语法扩展,它使得你能用类 HTML 的形式来描述界面。你无须自学 HTML 和 JavaScript 之外的新的符号和语法等,只需要遵守以下三条规则即可:
回到两个单根节点的原素,如:<!– 你能采用 div 标签 –>
<div>
<h1>Hedy Lamarrs Todos</h1>
<img
src=“https://i.imgur.com/yXOvdOSs.jpg”
alt=“Hedy Lamarr”
className=“photo”
/>
<ul>
…
</ul>
</div>
<!– 你也能采用空标签 –>
<>
<h1>Hedy Lamarrs Todos</h1>
<img
src=“https://i.imgur.com/yXOvdOSs.jpg”
alt=“Hedy Lamarr”
className=“photo”
/>
<ul>
…
</ul>
</>
关闭所有标签<!– 诸如 img 要自关闭 <img />,而包围类标签要成对出现 <li></li> –>
<>
<img
src=“https://i.imgur.com/yXOvdOSs.jpg”
alt=“Hedy Lamarr”
className=“photo”
/>
<ul>
<li>Invent new traffic lights</li>
<li>Rehearse a movie scene</li>
<li>Improve the spectrum technology</li>
</ul>
</>
采用驼峰命名(camalCase)形式<!– 如 stroke-width 要写成 strokeWidth,而 class 由于是 react 的关键字,因此替换为 className<img
src=”https://i.imgur.com/yXOvdOSs.jpg” alt=”Hedy Lamarr”
className=”photo”
/>
JSX 并不是开箱即用的,应用程序默认情况下是无法说明 JSX 的,所以你需要两个编译器(compiler),诸如 Babel,来将 JSX 标识符转换为普通的应用程序能认知的 JavaScript。
在项目中添加 Babel
复制粘贴以下脚本到 index.html 文件中:
<script src=“https://unpkg.com/@babel/standalone/babel.min.js”></script>
另外,你还需要告诉 Babel 需要转换哪些标识符,为需要转换的标识符添加类型 type=”text/jsx”
<html>
<body>
<div id=“app”></div>
<script src=“https://unpkg.com/react@17/umd/react.development.js”></script>
<script src=“https://unpkg.com/react-dom@17/umd/react-dom.development.js”></script>
<!– Babel Script –>
<script src=“https://unpkg.com/@babel/standalone/babel.min.js”></script>
<script type=“text/jsx”>
const app = document.getElementById(app);
ReactDOM.render(<h1>Develop. Preview. Ship. </h1>, app);
</script>
</body>
</html>
现在能再次回到应用程序中刷新页面来确认是否能成功展示了。
采用声明式的 React,你只编写了以下标识符:
<script type=“text/jsx”>
const app = document.getElementById(“app”)
ReactDOM.render(<h1>Develop. Preview. Ship. </h1>, app)
</script>
而命令式标识符如此前编写的:
<script type=“text/javascript”>
const app = document.getElementById(app);
const header = document.createElement(h1);
const headerContent = document.createTextNode(Develop. Preview. Ship. );
header.appendChild(headerContent);
app.appendChild(header);
</script>
相比较后不难发现,你节省了很多重复冗余的组织工作。
这就是 React,一款富含可重用标识符,为你节省时间和提高效率的工具。
目前,你还无须过多关注究竟 React 用了什么神奇的魔法实现这样的功能。总之假如你感兴趣的话,能参考 React 的官方文档中的 UI Tree 和 render 两个段落。React 核心基本原理
在真正上手 React 项目之前,还有三个最关键的 React 核心基本原理需要认知
组件(Components)参数(Props)状态(State)在后续的段落中我们将逐一自学以内三个核心基本原理。
0x03 采用组件(Components)来建立界面
两个采用者界面能被分割成更小的部分,我们称之为“组件”。它是自包含、可重用的标识符块,你能把它想象成乐高玩具,独立的砖块能拼装成更大的组合内部结构。假如你要更新界面的某一部分,你能仅更新特定的组件或“砖块”。
模组化让你的标识符更具有可维护性,因为你能轻易地添加、修改、删除特定的组件而无须改动程序的其他部分。React 组件其实就是用 JavaScript 编写的,接下来我们将自学怎样编写两个从 JavaScript 原生到 React 的组件。
创建组件
在 React 中,组件就是抒发式,我们在 script 中插入两个 header 形式:
<script type=“text/jsx”>
const app = document.getElementById(“app”)
function header() {}
ReactDOM.render(<h1>Develop. Preview. Ship. </h1>, app)
</script>
组件抒发式回到两个界面原素(即我们前面所提到过的单根节点的原素),能采用 JSX 语法,如:
<script type=“text/jsx”>
const app = document.getElementById(“app”)
function header() {
return (<h1>Develop. Preview. Ship. </h1>)
}
ReactDOM.render(, app)
</script>
然后将 header 传入 ReactDOM.render 的第两个参数中去:
ReactDOM.render(header, app)
但假如你现在刷新浏览器预览效果的话,将会报错,因为还需要做两件事。
具体来说,React 组件要以大写字母开头:
// 首字母大写
function Header() {
return <h1>Develop. Preview. Ship. </h1>;
}
ReactDOM.render(Header, app);
其次,你在采用 React 组件时,也需要采用 JSX 的语法格式,将组件名称用 <> 扩起来:
function Header() {
return <h1>Develop. Preview. Ship. </h1>;
}
<br/>ReactDOM.render(<Header />, app);
嵌套组件
两个应用程序通常包含多个组件,有的甚至是组件嵌套的组件。例如我们来创建两个 HomePage 组件:
function Header() {
return <h1>Develop. Preview. Ship. </h1>;
}
function HomePage() {
return <div></div>;
}
ReactDOM.render(<Header />, app);
然后将 Header 组件放入 HomePage 组件中:
function Header() {
return <h1>Develop. Preview. Ship. </h1>;
}
function HomePage() {
return (
<div>
{/* 嵌套的 Header 组件 */}
<Header />
</div>
);
}
ReactDOM.render(<HomePage />, app);
组件树
你能继续以这种形式嵌套 React 组件,以形成两个更大的组件。
比如上图中,你的顶层组件是 HomePage,它上面包含了两个 Header,两个 ARTICLE 和两个 FOOTER。然后 HEADER 组件下又包含了它的子组件等等。
这样的模组化使得你能在项目的许多其他地方重用组件。
0x04 参数(Props)与数据展示
假如你重用 Header 组件,你将显示相同的内容两次。
function Header() {
return <h1>Develop. Preview. Ship. </h1>;
}
function HomePage() {
return (
<div><br/> <Header />
<Header />
</div>
);
}
办呢?
普通的 HTML 原素允许你通过设置对应标签的某些关键属性来修改其实际的展示内容,比如修改 <img> 的 src 属性就能修改图片展示,修改 <a> 的 href 就能改变超文本链接的目标地址。
同样的,你能通过传入某些属性值来改变 React 的组件,这被称为参数(Props)。
与 JavaScript 抒发式类似,你能设计两个组件接收许多自定义的参数或者属性来改变组件的行为或展示效果,并且还允许通过父组件传递给子组件。
⚠️ 注意:React 中,数据流是顺着组件数传递的。这被称为单向数据流。采用参数
在 HomePage 组件中,你能传入两个自定义的 title 属性给 Header 组件,就如同你传入了两个 HTML 属性一样。
// function Header() {
// return <h1>Develop. Preview. Ship. </h1>
// }
function HomePage() {
return (
<div>
<Header title=“Hello React” />
</div>
);
}
// ReactDOM.render(<HomePage />, app)
然后,Header 作为子组件能接收那些传入的参数,可在组件抒发式的第两个入参中获得。
function Header(props) {
return <h1>Develop. Preview. Ship. </h1>
}
你能尝试打印 props 来查看它具体是什么东西。
function Header(props) {
console.log(props) // { title: “Hello React” }
return <h1>Hello React</h1>
}
由于 props 是两个 JS 第一类,因此你能采用第一类重构来展开获得第一类中的具体键值。
function Header({ title }) {
console.log(title) // “Hello React”
return <h1>Hello React</h1>
}
现在你就能采用 title 变量来替换 h1 标题中的文本了。
function Header({ title }) {
console.log(title) // “Hello React”
return <h1>title</h1>
}
但当你打开应用程序刷新页面时,你会发现页面上展示的是标题文本是 title,而不是 title 变量的值。这是因为 React 不能对纯文本进行解析,这就需要你额外地对文本展示做许多处理。
在 JSX 中采用变量
要在 JSX 中采用你定义的变量,你需要采用花括号 {} ,它允许你在其中编写 JavaScript 抒发式
function Header({ title }) {
console.log(title) // “Hello React”
return <h1>{ title }</h1>
}
通常,它支持如下几种形式:
输出第一类属性 { props.title } 模板字符串 {`Hello ${title}`}抒发式回到值 { getTitle() }三元抒发式 { title ? title : “Hello” }这样你就能根据参数输出不同的标题文本了:
function Header({ title }) {
return <h1>{title ? title : Hello React!}</h1>;
}
function Page() {
return (
<div>
<Header title=“Hello JavaScript!” />
<Header title=“Hello World!” />
</div>
);
}
通过列表进行迭代
通常我们会有一组数据需要展示,它以列表形式呈现,你能采用字符串形式来操作数据,并生成在样式上统一的不同内容。
例如,在 HomePage 中添加一组名字,然后依次展示它们。
function HomePage() {
const names = [Mike, Grace, Margaret];
return (
<div>
<Header title=“Develop. Preview. Ship. “ />
</div>
);
}
然后你能采用 Array 的 map 形式对数据进行迭代输出,并采用斜线抒发式来将数据映射到每个迭代项目上。
function HomePage() {
const names = [Mike, Grace, Margaret];
return (
<div>
<Header title=“Develop. Preview. Ship. “ />
<ul>
{names.map((name) => (
<li>{name}</li>
))}
</ul>
</div>
);
}
现在假如你打开应用程序查看,会看到两个有关缺少 key 属性的警告。这是因为 React 需要通过 key 属性来唯一识别字符串上的原素来确定最终需要在 DOM 上更新的项目。通常我们会采用 id,但本例子中你能间接采用 name,因为它们的值也是唯一不同的。
function HomePage() {
const names = [Mike, Grace, Margaret];
return (
<div>
<Header title=“Develop. Preview. Ship. “ />
<ul>
{names.map((name) => (
<li key={name}>{name}</li>
))}
</ul>
</div>
);
}
0x05 采用状态(State)来增加交互性
具体来说,我们看下 React 是怎样通过状态和事件处理来协助我们增加交互性的。
我们在 HomePage 组件中添加两个“喜欢”按钮:
function HomePage() {
const names = [Mike, Grace, Margaret];
return (
<div>
<Header title=“Develop. Preview. Ship. “ />
<ul>
{names.map((name) => (
<li key={name}>{name}</li>
))}
</ul>
<button>Like</button>
</div>
);
}
监听事件
要让按钮在被点击的时候做些什么时,你能在按钮上添加 onClick 事件属性:
function HomePage() {
// …
return (
<div>
{/* … */}
<button onClick={}>Like</button>
</div>
);
}
在 React 中,属性名称都是驼峰命名式的,onClick 是许多事件属性中的一种,还有许多其他的事件属性,如:输入框会有 onChange ,表单会有 onSubmit 等。
处理事件
你能定义两个抒发式来处理以内许多事件,当它被触发的时候。事件处理抒发式能在回到语句之前定义,如:
function HomePage() {
// …
function handleClick() {
console.log(“I like it.”)
}
return (
<div>
{/* … */}
<button onClick={}>Like</button>
</div>
)
}
接着你就能在 onClick 中调用 handleClick 形式了。
function HomePage() {
// …
function handleClick() {
console.log(I like it.);
}
return (
<div>
{/* … */}
<button onClick={handleClick}>Like</button>
</div>
);
}
状态和钩子
React 里有一系列钩子抒发式(Hooks),你能利用钩子抒发式在组件中创建状态,你能把状态认知为在界面上随时间或者行为变化的许多逻辑信息,通常情况下是由采用者触发的。
你能通过状态来存储和增加采用者点击喜欢按钮的次数,在这儿我们能采用 React 的 useState 钩子抒发式。
function HomePage() {
React.useState();
}
useState 回到两个字符串,你能采用字符串重构来采用它。
function HomePage() {
const [] = React.useState();
// …
}
该字符串的第两个值是状态值,你能定义为任何变量名称:
function HomePage() {
const [likes] = React.useState();
// …
}
该字符串的第二个值是状态修改抒发式,你能定义为以 set 为前缀的抒发式名,如 setLikes。
function HomePage() {
const [likes, setLikes] = React.useState();
// likes 存储了喜欢被点击的次数;setLikes 则是用来修改该次数的抒发式
// …
}
与此同时,你能在定义的时候给出 likes 的初始值
function HomePage() {
const [likes, setLikes] = React.useState(0);
}
然后你能尝试查看你设置的初始值是否生效
function HomePage() {
// …
const [likes, setLikes] = React.useState(0);
return (
// …
<button onClick={handleClick}>Like({likes})</button>
);
}
最后,你能在每次按钮被点击后调用 setLikes 形式来更新 likes 变量的值。
function HomePage() {
// … const [likes, setLikes] = React.useState(0);
function handleClick() {
setLikes(likes + 1);
}
return (
<div>
{/* … */}
<button onClick={handleClick}>Likes ({likes})</button>
</div>
);
}
点击喜欢按钮将会调用 handleClick 形式, 然后调用 setLikes 形式将更新后的新值传入该抒发式的第两个入参中。这样 likes 变量的值就变成了新值
状态管理
本段落仅对状态做了简单的介绍,举例了 useState 的用法,你能在后续的自学中介绍到更多的状态管理和数据流处理的形式,更多的内容能参考官网的添加交互性 和 状态管理 两个段落进行更深入细致的自学。
小问答:请说出参数(Props)和状态(State)的区别?0x06 长路漫漫,继续前行
到此为止,你已介绍了 React 的三大核心基本原理:组件、参数和状态。对那些基本原理的认知越深刻,对今后开发 React 应用就越有协助。自学的旅程还很漫长,途中若有困惑能随时回看责任编辑,或写作以下主题文章进行更深入细致的自学:
Render and Commit (reactjs.org)Referencing Values with Refs (reactjs.org)Managing State (reactjs.org)Passing Data Deeply with Context (reactjs.org)React APIs (reactjs.org)
React 自学资源
React 的自学资源层出不穷,《React 文档》,它涵盖了所有你需要自学的主题。
最好的自学形式就是实践。你能顺着责任编辑的 HomePage 组件开始一点一点搭建自己的网站,总之已经有越来越多的 React 框架、脚手架出现,希望你在今后的 React 开发之旅越来越顺利。