React 编程思想 #1
看太多语法,都不如简单尝试一下,跟着官方文档做了一下 DEMO,文档写的真不错,就是没翻译完,一大半都还是英文(×_×),本篇其实大部分也是在重复文档内容,不过加上了自己的尝试。
从原型开始
React 可以改变你对所看到的设计以及所构建的应用程序的看法。以前你看到的是一片森林,使用 React 后,你将欣赏到每一棵树。React 简化了你对设计系统(design system)和 UI 状态的看法。在本教程中,我们将带领你使用 React 构建一个可搜索的数据表产品,并领略整个思考的过程。
以上来自 React 的官方文档(https://react.bootcss.com/learn/thinking-in-react#),形容的可以说是非常好了。React 的编程思想就是如何从控件(树)到应用(森林)。
首先假设开发界面时已经拥有了 API 返回的数据(或者模拟出来的):
[
{ category: "Fruits", price: "$1", stocked: true, name: "Apple" },
{ category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit" },
{ category: "Fruits", price: "$2", stocked: false, name: "Passionfruit" },
{ category: "Vegetables", price: "$2", stocked: true, name: "Spinach" },
{ category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin" },
{ category: "Vegetables", price: "$1", stocked: true, name: "Peas" }
]
同时也有了设计师设计的原型,原型中除了对数据的显示,还要有搜索栏和单选框实现过滤的需求:
有了这两个东西,就可以进行代码的设计了。首先,从构建 UI 开始,按照 React 的思想,需要将 UI 细分成多个组件,文档中的分法是分成了五个组件:
这五个组件是:
- FilterableProductTable (灰色部分)包含了整个应用程序。
- SearchBar (蓝色部分)接收用户输入。
- ProductTable (紫色部分)根据用户的输入显示和筛选列表。
- ProductCategoryRow (绿色部分)显示每个类别的标题。
- ProductRow (黄色部分)每行显示一个产品。
根据组件之间的包含关系,可以区分出它们的层级:
- ——FilterableProductTable
- ————SearchBar
- ————ProductTable
- ——————ProductCategoryRow
- ——————ProductRow
划分了界面的组件和组件关系后,就可以进行编码了。
静态实现
首先进行界面的静态实现,即简单显示内容,不包含交互操作。React 构建的应用,画面由组件转换为 HTML 代码渲染出来(不知道的话先看入门把 https://react.bootcss.com/learn),因此,可以将一个个组件理解为一段段的 HTML 代码块。父组件包含子组件,就好像 HTML 标签之间的嵌套。这里的思想和 DOM 的思想是一样的(我是这么认为的,不一定对哈)。
对于简单的应用,自顶向下构建组件更加简单,而复杂的应用自底向上构建组件更加简单。这个 DEMO 自然是简单的应用,因此直接从最大的组件 FilterableProductTable
开始构建:
function FilterableProductTable({products}) {
return (
<div>
<SearchBar />
<ProductTable products={products} />
</div>
);
}
分析一下这段代码,整体看这段代码是一个 JS 函数,返回了一段 HTML 代码,这就是一个组件了;其中,包含了两个子组件 SearchBar
和 ProductTable
,与上面划分的组件结构一致;这个函数的参数是 {products}
,即商品列表,在后面,又将这个参数传递给了 ProductTable
,这也是 React 的一个特点:数据自顶向下流动,即数据由父组件传递给子组件。
多说一句,这里参数传递的方式是 {products}
,加上了花括号,因为如果没有花括号,React 会认为这是一整个 props 属性,如这样:
// 不加花括号,传递的是 props,后续还要在 props 中获取 products
function FilterableProductTable(props) {
return (
<div>
<SearchBar />
<ProductTable products={props.products} />
</div>
);
}
继续其他组件的构建,这里就不多说了,最后构建出的整个 JS 文件如下:
function ProductCategoryRow({ category }) {
return (
<tr>
<th colSpan="2">
{category}
</th>
</tr>
);
}
function ProductRow({ product }) {
const name = product.stocked ? product.name :
<span style={{ color: 'red' }}>
{product.name}
</span>;
return (
<tr>
<td>{name}</td>
<td>{product.price}</td>
</tr>
);
}
function ProductTable({ products }) {
const rows = [];
let lastCategory = null;
products.forEach((product) => {
if (product.category !== lastCategory) {
rows.push(
<ProductCategoryRow
category={product.category}
key={product.category} />
);
}
rows.push(
<ProductRow
product={product}
key={product.name} />
);
lastCategory = product.category;
});
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
function SearchBar() {
return (
<form>
<input type="text" placeholder="Search..." />
<label>
<input type="checkbox" />
{' '}
Only show products in stock
</label>
</form>
);
}
function FilterableProductTable({products}) {
return (
<div>
<SearchBar />
<ProductTable products={products} />
</div>
);
}
const PRODUCTS = [
{category: "Fruits", price: "$1", stocked: true, name: "Apple"},
{category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit"},
{category: "Fruits", price: "$2", stocked: false, name: "Passionfruit"},
{category: "Vegetables", price: "$2", stocked: true, name: "Spinach"},
{category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin"},
{category: "Vegetables", price: "$1", stocked: true, name: "Peas"}
];
function App() {
return <FilterableProductTable products={PRODUCTS} />;
}
可以看到,组件之间的关系和之前划分的组件结构一致,父组件包含着子组件,子组件之间也可能是兄弟,和 HTML 标签之间的关系是一致的。
在 SearchBar
组件中,显示了搜索框和单选框,没有其他的子组件,但也仅仅是显示,还没有交互的功能;ProductTable
组件是一个表格,其中,包含了多个 ProductRow
和 ProductCategoryRow
组件,这两个组件即商品行和商品目录行,其内容是根据参数 products
进行动态渲染的。
最后,构建了一个 APP 组件,包含了最大的组件 FilterableProductTable
并第一次传递了参数 PRODUCTS
,这就是应用(森林)了。
构建完应用后,还需要将其渲染到页面上,参考代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello Qiyuanc!</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel" src="./reactTest2.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
ReactDOM.render(<App />, document.getElementById('example'))
</script>
</body>
</html>
reactTest2.js 文件就是上面构建的应用所在的文件,通过引入 React 和这个 JS 文件,可以使用 ReactDOM.render 方法将其渲染到页面上的占位符 example 上(这就是基础了,不多说)。
这里踩到个坑:在 VSCode 中启动 HTML 页面,会报错 CORS(跨域访问错误),这是由于 HTML 页面是通过 file:// 访问的,JS 文件也是通过 file:// 访问的,触发了浏览器的安全保护策略。在研究了半天后,发现可以通过搭建服务器,转换为 http:// 访问解决。但因为一些特殊原因,我这用不了 npm,装不了 http-server,很搞。还好最后在 VSC 里发现了个插件 Live Server,可以直接转换为本地服务器启动,最终也是起起来了。
最后实现的效果就是这样啦:
在本地跑起来感觉还是不一样的。
标签:category,stocked,name,思想,price,编程,React,组件 From: https://www.cnblogs.com/qiyuanc/p/Front6.html