一个简单的 Loops
我们先来实现一个简单的需求,使用 React 打印 数组 中的数据并显示
相信聪明的你早已想到了解决方案,以下是我的实现
import React from ‘react’
const List = () => {
const arr = [1, 2, 3, 4]
return (
<ul>
{
arr.map((item, index) => <li key={index}>{item}</li>) // 需要带上 key 属性
}
</ul>
)
}
export default List
使用 ES6 的 map 方法遍历并返回 <li></li> 结构
我们再来看看使用 loops 的版本
使用 loops 前我们首先要安装 react-loops npm install react-loops 或者 yarn add react-loops
import React from ‘react’
import {For} from ‘react-loops’ // 引入 react-loops
const List = () => {
const arr = [1, 2, 3, 4]
return (
<div>
<For of={arr} as={item => <li>{item}</li>} /> // 可省略 key 属性
</div>
)
}
export default List
你会发现使用 loops 后,只是替换了之前的 map 方法,以一个 <For /> 标签的形式来遍历数据,of 属性中写入需要遍历项,as 则类似于 map 方法中的回调函数。但其实内部做了很多优化和封装。值得注意的是,在使用第一种方法时,为了 diff 算法优化,我们必须带上 key 属性,否则浏览器会报警告错误。
而使用 loops 后,其内部会为每项自动注入 key 属性,省去我们手动操作或是遗漏带来的麻烦。
为什么会有 Loops
增强代码语义化
loops 译为 ’ 循环 ’,顾名思义是为了优化 React 中的循环操作。它以其独特的 <For /> 标签的形式,使团队开发的代码风格更加统一化,并具有良好的可读性。而 loops 的出现,本身是受到了 Angular(ng-repeat) 和 Vue(v-for) 指令语法的启发,熟悉 Angular 或 Vue 的朋友们应该会有既视感。三大框架的发展本质就是相互学 (抄) 习(袭)和启发的过程。
让 For 标签一统循环界江湖
我们知道在 JS 中有许多可遍历结构,除数组之外,还有伪数组、对象、Map、Set… 可迭代结构,这个时候我们需要针对不同存储结构来进行遍历方法的选择。比如我们现在修改一下之前的需求
使用 React 打印 对象 中的数据并显示
此处思考一分钟,聪明的你可以想想如何实现。
我们知道对象身上是没有 map 方法的,这时我们需要先将对象的 key 转为一个数组的集合,再使用 map 方法来进行操作。
import React from ‘react’
const List = () => {
const obj = {
name: ‘z’,
age: 20
}
return (
<ul>
{
// Object.keys(obj) => [‘name’, ‘age’]
Object.keys(obj).map(item => <li key={item}>{obj[item]}</li>) // 需要带上 key 属性
}
</ul>
)
}
export default List
又或者我们可以使用 for in 循环来遍历这个对象得出遍历结果并打印。可以发现遍历的方法有很多种,而 loops 就是为了统一循环风格而生,可遍历项均可以使用一个 <For /> 标签来实现,值得注意的是,在遍历 对象 时,我们需要用 <For /> 标签上的 in 属性,这个后面详细说明。
import React from ‘react’
import {For} from ‘react-loops’
const List = () => {
const obj = {
name: ‘z’,
age: 20,
}
return (
<div>
// 遍历对象时,需要使用 in 属性
<For in={obj} as={item => <li>{item}</li>} /> // 可省略 key 属性
</div>
)
}
export default List
怎样使用 Loops
在使用 loops 时,分为两种情况
遍历数组、伪数组、Iterables(可迭代对象 Map、Set 等)
遍历对象
For-of Loops
在使用 of 属性时,我们可以接收数组、伪数组、Iterables(可迭代对象 Map、Set 等)
const arr = [1, 2, 3, 4] // 可以接收数组
const arrLike = {0: ‘z’, 1: ‘h’, length: 2} // 可接收伪数组
const setLoop = new Set([1, 2, 3, 4]) // 可接收 Set
const mapLoop = new Map([[‘name’, ‘z’], [‘age’, 20]) // 可接收 Map
return (
<div>
<For of={arr} as={item => <li>{item}</li>} />
</div>
)
而 as 属性则类似于 map 方法的回调函数,当然你也可以不用 as 属性,这时需要将回调函数嵌套在 <For></For> 中
return (
<For of={arr}>
{item => <li>{item}</li>}
</For>
)
当然 as 这个回调函数在 map 方法之上又做了一层封装,其第二个参数非常特别,它给我们提供了一些与遍历有关的属性。
return (
<div>
// metadata 为回调函数的第二个参数
<For of={arr} as={(item, metadata) => {
console.log(metadata) // 打印输出 metadata
return <li>{item}</li>
}} />
</div>
)
我们先打印输出下第二个参数
index — 遍历项标记值,从 0 开始,类似于 map((item, index) => {}) 中的 index
isFirst — 是否为第一项
isLast — 是否为最后一项
key — 遍历项的键,数组为其下标,对象为对象的 key
length — 遍历项数目
有了这些属性,我们可以通过解构的方式来取出需要使用的数据。例如我们要手动添加 key 时,便可以从第二个参数中取出
return (
<div>
// {key} 解构出 key 的值
<For of={arr} as={(item, {key}) => <li key={key}>{item}</li>} />
</div>
)
For-in Loops
在我们需要遍历一个对象时,需要使用 For-in 的结构 <For in={obj} /> 还是引用上述例子
const List = () => {
const obj = {
name: ‘z’,
age: 20,
}
return (
<div>
// 遍历对象时,需要使用 in 属性
<For in={obj} as={item => <li>{item}</li>} />
</div>
)
}
原因其实很简单,我们可以类比于 for in 循环
const obj = {
name: ‘z’,
age: 20,
}
for(let key in obj) {
…
}
如果我们尝试用 For-of 来遍历对象(伪数组除外),会发现浏览器报错意思就是 For-of 只对数组、伪数组、Iterables(可迭代对象 Map、Set 等)有效。我们再尝试看看源码是如何判断
function For(props) {
…
// 如果使用的是 of 属性
var list = props.of;
if (!list) {
return null;
}
// 判断是否为数组
if (!Array.isArray(list)) {
// 不是数组就判断是否为集合 (伪数组 和 Iterable(Map、Set) )
if (!iterall.isCollection(list)) {
// 不是数组,也不是集合,则抛出异常
throw new TypeError(
“<For> `of` expects an Array, Array-like, or Iterable collection”
);
}
// 是数组,将用新数组存放
var array = [];
iterall.forEach(list, function(item) {
array.push(item);
});
list = array;
}
…
}
// 判断是否为集合
function isCollection(obj) {
// 如果是对象,但对象只能是 伪数组 和 Iterable(Map、Set)
return Object(obj) === obj && (isArrayLike(obj) || isIterable(obj));
}
结尾
通过这几个小例子的学习,可以发现 loops 确实是非常简单而且好用的 react 遍历工具。是轮子就有它存在的意义,如果你厌倦了手写 JS 遍历,不妨也来尝尝 loops,或许你就会喜欢上这个小而巧的库。
react-loops 地址:react-loops demo 地址:github