Skip to content

Latest commit

 

History

History
177 lines (148 loc) · 5.07 KB

4. 搜索页设计.md

File metadata and controls

177 lines (148 loc) · 5.07 KB

1. 分析

搜索页面组件分析

分为三个组件

  • SearchBox:搜索框组件
  • PopularSearch:搜索流行词组件
  • SearchHistory:搜索历史组件

2. 实现的功能

  1. 搜索框输入关键词,前端发送关键词请求,后端返回对应的查询数据
  2. 显示流行词数据
  3. 记录搜索历史

3. 设计重点

搜索页面存在着两个网络请求

getPopularKeywords: () => `http://localhost:3000/mock/keywords/popular.json`,
getRelatedKeywords: (text) => `http://localhost:3000/mock/keywords/related.json?keyword=${text}`

搜索页面state设计

数据表keywords

在搜索关键词state设计的时候遇到了比较麻烦的问题

因为在发送关键词请求getRelatedKeywords(text)的时候,后端返回的应该是这样一串数据

[
    { "id": "k-10", "keyword": "火锅", "quantity": 8710 },
    { "id": "k-11", "keyword": "火锅自助", "quantity": 541 },
    { "id": "k-12", "keyword": "火锅 三里屯", "quantity": 65 },
    { "id": "k-13", "keyword": "火锅 望京", "quantity": 133 },
    ...
]

如果将relatedKeywords的state设计成和popularKeywords一样

relatedKeywords: {
    isFetching: false,
    ids: [] // 保存id
},
relatedKeywords: {
    isFetching: false,
    ids: [] // 保存id
}

就无法缓存搜索结果,因为每次请求都得清空之前的关键字ids,然后保存新的关键字ids

就会导致用户下次再次进行某个相同关键字的搜索时,就需要重新发送请求,影响用户体验

所以设计的时候得嵌套一层

relatedKeywords: {
    '关键字1': {
        isFetching: false,
        ids: [] // 保存id
    },
    '关键字2': {
        isFetching: false,
        ids: [] // 保存id
    }
}

嵌套了一层,处理reducer就比较麻烦了,需要创建一个新的子reducer,但这样做前端就通过redux建立起了关键词索引

const relatedKeywords = (state = initState.relatedKeywords, action) => {
    switch (action.type) {
        case types.FETCH_RELATED_KEYWORDS_REQUEST:
        case types.FETCH_RELATED_KEYWORDS_SUCCESS:
        case types.FETCH_RELATED_KEYWORDS_FAILURE:
            return {
                ...state,
                [action.text]: relatedKeywordsByText(state[action.text], action)
            }
        default:
            return state
    }
}

// related的子reducer
const relatedKeywordsByText = (
    state = { isFetching: false, ids: [] },
    action
) => {
    switch (action.type) {
        case types.FETCH_RELATED_KEYWORDS_REQUEST:
            return { ...state, isFetching: true }
        case types.FETCH_RELATED_KEYWORDS_SUCCESS:
            return {
                ...state,
                isFetching: false,
                ids: state.ids.concat(action.response.ids)
            }
        case types.FETCH_RELATED_KEYWORDS_FAILURE:
            return { ...state, isFetching: false }
        default:
            return state
    }
}

这一点也印证了,尽量把state设计扁平化,才利于我们的开发

显示流行词数据,记录搜索历史 这两个功能相对而言就比较容易了

4. 页面渲染存在的一些问题

在学的过程中发现这个例子写出来其实是性能有问题的

在使用Chrome React工具的时候

发现在输入框输入内容的时候,

这两个组件会一直重复渲染

  • PopularSearch:搜索流行词组件
  • SearchHistory:搜索历史组件

代码大致是下面这样的

class Search extends Component {
    render() {
        const { 
            inputText,
            popularKeywords,
            relatedKeywords,
            historyKeywords
        } = this.props
        return (
            <div>
                <SearchBox 
                    inputText={inputText} 
                    onChange={this.handleChangeInput}
                    onClear={this.handleClearInput} 
                    onCancel={this.handleCancelInput}/>
                <PopularSearch 
                    data={popularKeywords}
                    onClickKeyword={ this.handleClickKeyword } />
                <SearchHistory 
                    data={historyKeywords}
                    onClaerHistory={this.handleClearHistory} />
            </div>
        )
    }
    // ...
}

export default connect(mapStateToProps, mapDispatchToProps)(Search)

后来排查了一下原因

<SearchContainer>
    <Search>
        <SearchBox />
        <PopularSearch />
        <SearchHistory />
    </Search>
</SearchContainer>

因为在输入框中输入了内容,导致更新了redux中inputValue的值

同时也引起我们生成的容器组件中state发生了变化,引起了整个<SearchContainer>重绘,自然而然也引起了PopularSearchSearchHistory的重绘

解决办法是在PopularSearch和SearchHistory中重写shouldComponentUpdate

然后其实还存在了一个问题,selector的重复计算。

稍后再来说。