在 React 之前前端有三个里程碑意义的 library/framework
React 是一个声明式、高效、灵活的用于构建用户界面的 JavaScript library,React 核心理念在于将一些简短、独立的代码片段组合成复杂的 UI 界面,这些代码片段被称作 “Component”。React 不是 MVC 框架,更像是其中 V,仅仅负责用户交互视图的渲染。
React 带来了三个颠覆性理念,在接下来的章节中将一一介绍:
使用 Create React App [1]可以快速初始化一个 React Web 项目。
$ npx create-react-app learn-react --template typescript
$ cd learn-react
$ npm start
执行 npm start后浏览器会在 http://localhost:3000 打开项目首页。
React 提供了 React Developer Tools[2],集成到了 Chrome Dev Tools,借此可以查看 React 组件树及其对应 Props、State。
app.tsx
import React, { useState } from 'react';
function Button(props: { count: number }): JSX.Element {
const [count, setCount] = useState(props.count);
return (
<button
onClick={() => {
setCount((c) => c + 1);
}}
>
{count}
</button>
);
}
function App() {
const [count, setCount] = useState(0);
return (
<div className="App">
<Button count={5} />
</div>
);
}
export default App;
index.tsx
import React from 'react';
import * as ReactDOMClient from 'react-dom/client';
import App from './app';
const rootElement = document.querySelector('body') as Element;
const root = ReactDOMClient.createRoot(rootElement);
root.render(<App />);
打开 Chrome Dev Tools 可以看到多了一个 Components选项卡
接下来边学习边做一个 Todo 项目体验一下 React。
在开始编写 React 程序之前需要了解一下 JSX。JSX 是 React 对 JavaScript 的语法拓展,用来在 JavaScript 文件内通过类HTML标签(HTML-like markup)表达页面的视图与交互逻辑。
<div className="container">
<CustomComponent
onClick={() => {alert('Hello')}}
>
Hello {props.name}!
</CustomComponent>
</div>
Web 页面由 HTML 内容、CSS 样式、JavaScript 交互构成,长期以来 Web 开发者将三者放在独立的文件中做分离,这实际上是按照技术实现的分离。
传统页面内容主要由 HTML 定义,JavaScript 逻辑是点缀,随着现代网页交互性增强,页面内容很大程度是由 JavaScript 逻辑动态生成,同时渲染逻辑本质上与其他 UI 逻辑内在耦合,比如在 UI 中需要绑定处理事件、在某些时刻状态发生变化时需要通知到 UI,以及需要在 UI 中展示准备好的数据。
因此 React 使用 JSX 把渲染逻辑和 HTML 标签集成到一起。
这样开发者关注的不是 HTML 模板、JavaScript 渲染逻辑这样的技术实现,而是诸如 Sidebar、Form 这样的页面功能单元。
返回 JSX 的函数就是 React 最简单的组件,可以和 HTML 标签一样嵌套使用。React 使用 props参数向组件传递数据,提升组件的复用性。
/**
* JSX 语法隐式调用 React.createElement
* 所以虽然代码中没有调用 React 的语句,仍然需要引入
*/
import React from 'react';
interface IButton {
/** 按钮展示文案 */
text: string;
/** 点击按钮跳转链接 */
link?: string;
/** 点击按钮自定义事件 */
onClick?: (event?: Event) => void
}
function Button(props: IButton) {
const { text, link, onClick } = props;
const redirectHandler = () => {
location.href = link;
};
return (
<div
className="button"
onClick={onClick | redirectHandler}
>
{text}
</div>
);
}
export default Button;
在使用组件时候,通过其标签的属性组装成 props 对象,传递给组件,语法和 HTML attribute 类似,但值可以是任意的 JavaScript 对象。
import React from 'react';
/**
* 导入 ./button.tsx 中 export 的默认内容,命名为 Button 使用
* .tsx 拓展名可以省略
*/
import Button from './button';
interface IDialog {
title: string;
content: Element;
showClose: boolean;
}
function Dialog(props: IDialog) {
const { title, content, showClose = false, children } = props;
const hideDialog = () => {
// ...
}
return (
<div>
<div className="dialog-title"> {title} </div>
<div className="dialog-body"> {content | children} </div>
{/* 没错,Button props 定义的属性,就是这样通过标签属性开放出来的 */}
<Button
title="取消"
onClick={hideDialog}
/>
<Button
title="确认"
onClick={() => { }}
/>
</div>
);
}
export default Dialog;
组件写好后通过 react-dom [3]将组件渲染到页面。
import React from 'react';
import ReactDOM from 'react-dom/client';
import Dialog from './dialog';
// 把组件渲染到页面 id 为 root 的元素中
const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement);
root.render(
<Dialog
title="demo dialog"
content="this is a dialog"
showClose={false}
/>
);
React 组件有几个约定:
1. 必须有根节点
如同上面写的几个简单 demo,JSX 必须有 root 节点,即使多个同级元素没有父节点,也需要用虚拟节点 <></> 来包裹。
{/* 非法的 JSX */}
<div id="box1"></div>
<div id="box2"></div>
{/* 合法的 JSX */}
<>
<div id="box1"></div>
<div id="box2"></div>
</>
2. 所有标签需要闭合
在 HTML 中标签并不一定需要闭合。
<meta charset="UTF-8">
<br>
<img src="https://g.alicdn.com/logo.png">
在 JSX 中可以混合 HTML 原生标签,但所有标签必须闭合。
<>
<meta charset="UTF-8" />
<br/>
<img src="https://g.alicdn.com/logo.png"/>
</>
3. 和 HTML 属性差异
function HelloWorldComponent(props) {
const divStyle = {
// 可以很方便设置动态样式
backgroundImage: 'url(' + props.imgUrl + ')',
// 但静态样式应该尽量通过 className 设置类,通过 css file 解决
// 不推荐 color: 'blue' 这种静态样式直接在 JSX 的写法
color: 'blue',
};
return (
<div style={divStyle}>
Hello World!
</div>
);
}
4. 自动转义 content
为了防止 XSS 攻击,JSX 会对直接设置的文本进行转义。
const content = `
这里应该展示一张图片<br>
<img src="https://sc02.alicdn.com/kf/HTB1gUuPUkzoK1RjSZFl761i4VXaw.png" />
`;
<div>
{content}
</div>
页面效果:
在安全性有保障的时候,可以通过 dangerouslySetInnerHTML禁用转义效果,展示 raw HTML
const content = `
这里应该展示一张图片<br>
<img src="https://sc02.alicdn.com/kf/HTB1gUuPUkzoK1RjSZFl761i4VXaw.png" />
`;
<div dangerouslySetInnerHTML={{ __html: content }}/>
JSX 中使用 {} 包裹 JavaScript 表达式处理动态逻辑,属性 value、子元素都可以,最常见的几个用法:
<div style={{ color: 'red' }}></div>
// 等同于
const styleObj = { color: 'red' };
<div style={styleObj}></div>
本文系作者在时代Java发表,未经许可,不得转载。
如有侵权,请联系nowjava@qq.com删除。