首先,讓我們看下在 Javascript 中如何轉(zhuǎn)化列表:
如下代碼,我們使用 map() 函數(shù)讓數(shù)組中的每一項(xiàng)翻倍,我們得到了一個(gè)新的數(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ù)組中的每個(gè)元素返回 <Text> 標(biāo)簽,最后我們得到一個(gè)數(shù)組 listItems:
const numbers = [...Array(100).keys()] // [0, 1, 2, ..., 98, 99]
const listItems = numbers.map((number) => {
return <Text className='li'> 我是第 {number + 1} 個(gè)數(shù)字</Text>
})
這段代碼生成了一個(gè) 1 到 100 的數(shù)字列表。
但是在上面的代碼,你會得到一個(gè)報(bào)錯(cuò):提醒你當(dāng)循環(huán)一個(gè)數(shù)組時(shí)應(yīng)該提供 keys。Keys 可以在 DOM 中的某些元素被增加或刪除的時(shí)候幫助 Nerv/小程序 識別哪些元素發(fā)生了變化。因此你應(yīng)當(dāng)給數(shù)組中的每一個(gè)元素賦予一個(gè)確定的標(biāo)識。
const numbers = [...Array(100).keys()] // [0, 1, 2, ..., 98, 99]
const listItems = numbers.map((number) => {
return <Text
key={String(number)}
className='li'
>
我是第 {number + 1} 個(gè)數(shù)字
</Text>
})
taroKey 適用于循環(huán)渲染原生小程序組件,賦予每個(gè)元素唯一確定標(biāo)識,轉(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} 個(gè)數(shù)字
</g-list>
)
})
數(shù)組元素中使用的 key 在其兄弟之間應(yīng)該是獨(dú)一無二的。然而,它們不需要是全局唯一的。當(dāng)我們生成兩個(gè)不同的數(shù)組時(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 的取值必須同時(shí)滿足三個(gè)條件:
最好的 key 就是數(shù)組里的 ID(通常由后端生成),他能同時(shí)滿足以上三個(gè)條件,同時(shí)也不需要自己去生成。如果沒有 ID,你能保證數(shù)組的元素某個(gè)鍵值字符串都是不同的(例如 item.title),那么使用那個(gè)字符串鍵值也可以。如果源數(shù)據(jù)沒有提供很好的 key 值,或者需要遍歷的數(shù)組生成的。那么你最好在數(shù)據(jù)創(chuàng)建或者修改之后給他添加一個(gè)好的 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 ...
}
}
每一個(gè)在渲染結(jié)果上一致組件的應(yīng)該對應(yīng)一個(gè)相同的 key。因此使用數(shù)組的 index 或在數(shù)組渲染時(shí)隨機(jī)生成一個(gè) key 值(但你在創(chuàng)建數(shù)組時(shí)可以這么做)都是反優(yōu)化,極端情況下甚至可能導(dǎo)致渲染出錯(cuò)。
在 React 中,JSX 是會編譯成普通的 JS 的執(zhí)行,每一個(gè) JSX 元素,其實(shí)會通過 createElement 函數(shù)創(chuàng)建成一個(gè) JavaScript 對象(React Element),因此實(shí)際上你可以這樣寫代碼 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ù)生成的模板當(dāng)做一個(gè)數(shù)組來處理。當(dāng)你需要這么做時(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>
})
更多建議: