首先,讓我們看下在 Javascript 中如何轉(zhuǎn)化列表:
如下代碼,我們使用 map() 函數(shù)讓數(shù)組中的每一項翻倍,我們得到了一個新的數(shù)列 doubled 。
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2)
console.log(doubled)
代碼打印出 [2, 4, 6, 8, 10]。
在 Taro 中,把數(shù)組轉(zhuǎn)化為數(shù)列元素的過程是相似的。
下面,我們使用 JavaScript 中的 map() 方法遍歷 numbers 數(shù)組。對數(shù)組中的每個元素返回 <Text> 標簽,最后我們得到一個數(shù)組 listItems:
const numbers = [...Array(100).keys()] // [0, 1, 2, ..., 98, 99]
const listItems = numbers.map((number) => {
return <Text className='li'> 我是第 {number + 1} 個數(shù)字</Text>
})
這段代碼生成了一個 1 到 100 的數(shù)字列表。
但是在上面的代碼,你會得到一個報錯:提醒你當循環(huán)一個數(shù)組時應(yīng)該提供 keys。Keys 可以在 DOM 中的某些元素被增加或刪除的時候幫助 Nerv/小程序 識別哪些元素發(fā)生了變化。因此你應(yīng)當給數(shù)組中的每一個元素賦予一個確定的標識。
const numbers = [...Array(100).keys()] // [0, 1, 2, ..., 98, 99]
const listItems = numbers.map((number) => {
return <Text
key={String(number)}
className='li'
>
我是第 {number + 1} 個數(shù)字
</Text>
})
taroKey 適用于循環(huán)渲染原生小程序組件,賦予每個元素唯一確定標識,轉(zhuǎn)換為小程序的 wx:key。
const numbers = [...Array(100).keys()] // [0, 1, 2, ..., 98, 99]
const listItems = numbers.map((number) => {
return (
// native component
<g-list
taroKey={String(number)}
className='g-list'
>
我是第 {number + 1} 個數(shù)字
</g-list>
)
})
數(shù)組元素中使用的 key 在其兄弟之間應(yīng)該是獨一無二的。然而,它們不需要是全局唯一的。當我們生成兩個不同的數(shù)組時,我們可以使用相同的 key:
class App extends Componenet {
state = {
posts: [
{id: 1, title: 'Hello World', content: 'Welcome to learning Taro!'},
{id: 2, title: 'Installation', content: 'You can install Taro from npm.'}
]
}
render () {
const { posts } = this.state
const sidebar = (
<View>
{posts.map((post) =>
<Text key={post.id}>
{post.title}
</Text>
)}
</View>
)
const content = posts.map((post) => {
return <View key={post.id}>
<Text>{post.title}</Text>
<Text>{post.content}</Text>
</View>
})
return (
<View>
{sidebar}
<View className="divider" />
{content}
</View>
)
}
}
key 會作為給 Taro 的提示,但不會傳遞給你的組件。如果您的組件中需要使用和 key 相同的值,請將其作為屬性傳遞:
const content = posts.map((post) => {
return <View key={post.id} id={post.id} >
<Text>{post.title}</Text>
<Text>{post.content}</Text>
</View>
})
key 的取值必須同時滿足三個條件:
最好的 key 就是數(shù)組里的 ID(通常由后端生成),他能同時滿足以上三個條件,同時也不需要自己去生成。如果沒有 ID,你能保證數(shù)組的元素某個鍵值字符串都是不同的(例如 item.title),那么使用那個字符串鍵值也可以。如果源數(shù)據(jù)沒有提供很好的 key 值,或者需要遍歷的數(shù)組生成的。那么你最好在數(shù)據(jù)創(chuàng)建或者修改之后給他添加一個好的 key 值:
let todoCounter = 0
function createNewTodo(text) {
return {
completed: false,
id: todoCounter++,
text
}
}
class App extends Components {
state = {
todos: [],
inputText: ''
}
onNewTodo () {
this.setState({
todos: [...this.state.todos, createNewTodo(this.state.inputText)]
})
}
render () {
return ...
}
}
每一個在渲染結(jié)果上一致組件的應(yīng)該對應(yīng)一個相同的 key。因此使用數(shù)組的 index 或在數(shù)組渲染時隨機生成一個 key 值(但你在創(chuàng)建數(shù)組時可以這么做)都是反優(yōu)化,極端情況下甚至可能導(dǎo)致渲染出錯。
在 React 中,JSX 是會編譯成普通的 JS 的執(zhí)行,每一個 JSX 元素,其實會通過 createElement 函數(shù)創(chuàng)建成一個 JavaScript 對象(React Element),因此實際上你可以這樣寫代碼 React 也是完全能渲染的:
const list = this.state.list.map(l => {
if (l.selected) {
return <li>{l.text}</li>
}
}).filter(React.isValidElement)
你甚至可以這樣寫:
const list = this.state.list.map(l => {
if (l.selected) {
return {
'$$typeof': Symbol(react.element),
'props': {
children: l.text
},
'type': 'li'
}
}
}).filter(React.isValidElement)
但是 Taro 中,JSX 會編譯成微信小程序模板字符串,因此你不能把 map 函數(shù)生成的模板當做一個數(shù)組來處理。當你需要這么做時,應(yīng)該先處理需要循環(huán)的數(shù)組,再用處理好的數(shù)組來調(diào)用 map 函數(shù)。例如上例應(yīng)該寫成:
const list = this.state.list
.filter(l => l.selected)
.map(l => {
return <li>{l.text}</li>
})
更多建議: