This is the 95th unwatered original, want to get more original good articles, please search the public number to pay attention to us ~ this article was first published in the political cloud front-end blog: H5 page list cache solution

preface

In H5 daily development, will often encounter the list click to enter the details page and then return to the list of the situation, especially for e-commerce platforms, such as we usually use Taobao, JD.com and other e-commerce platforms are done cache, and not only the list, many places are used to cache. But we’re talking about apps. In a native App, the page is a layer of views on top of LastPage, which naturally preserves the state of the LastPage. Unlike H5, when you go back from the details to the list, the state is cleared, you go through the life cycle again, you make a new request, you write a new state, For paging interface, the list is very long, when the user after turned over several pages, click details to see details and then returns a list of goods, the page back to the first page, so that the user experience is very poor, if at the time of entering the details will list data cached, use the cached data when you return to list, rather than to request data, stay in the left list page browsing location; Or, like the App, you can stack pages on Top of each other on LastPage and display the corresponding page when you return, so the user experience will be much better. This article briefly describes some of the points to consider when you do list caching, followed by a simple implementation.

thinking

Cause of state loss

Usually in page development, we manage different pages through routing, and there are many commonly used routing libraries, such as React-router, dvA-router…… When we switch routes, the unmatched Component will be replaced and the original state will be lost. Therefore, when the user returns from the details page to the list page, the list page Component will be reloaded and the life cycle will be reloaded. The data on the first page will be retrieved, thus returning to the top of the list. The following are common route matching code segments.

function RouterConfig({ history, app }) {
  const routerData = getRouterData(app);
  return (
    <ConnectedRouter history={history}>
      <Route
        path="/"
        render={(props)= > <Layouts routerData={routerData} {. props} / >}
        redirectPath="/exception/403"
      />
    </ConnectedRouter>
  );
}
Copy the code
// Route configuration description (you don't need to load the entire configuration,
// Just load the root route you want,
// You can also load this configuration lazily.
React.render((
  <Router>
    <Route path="/" component={App}>
      <Route path="about" component={About}/>
      <Route path="users" component={Users}>
        <Route path="/user/:userId" component={User}/>
      </Route>
      <Route path="*" component={NoMatch}/>
    </Route>
  </Router>
), document.body)
Copy the code

How to solve

The reason is found, so how do we cache pages or data? The solution can be solved in either of the following ways: 1. Automatically saves the status during route switchover. 2. Manually save the status. In Vue, keep-Alive can be used directly to cache components. As long as the components wrapped with the keep-alive tag are automatically cached when the page is switched, it is very convenient to use. The following is a simple example.

<! Deactivated components will be cached! --><keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>
Copy the code

However,ReactThere is nothing in thekeep-aliveThis kind of similar tag or function,Officially, this feature is not supported because it is prone to memory leaks.

Therefore, the only way to do this is to tamper with the routing layer and do the corresponding cache operation when switching routes. Some developers proposed a solution before: Use styles to control the display/hide of components, but this can cause problems such as not being able to animate when switching components, or using data flow management tools such as Redux or Mobx. Also, the developer implemented the React Version of React Keep Alive through the React. CreatePortal API, and it is easier to use. The second solution is to manually save the state, that is, manually collect and store the state of the page when the page is unloaded, and restore the data when the page is mounted. Personally, the latter is simple and rough, and the implementation is relatively simple. Cache cache is nothing more than two things, save and fetch, so in the process of save and fetch what problems need to pay attention to?

In my opinion, the following points should be paid attention to:

Deposit? When to deposit? Where there is? When to take? Where to take?

Save what

The first thing we need to care about is: what to save? So if we’re going to cache, what are we going to store? Whether to cache the entire Component, the list data, or the scrollTop of the scroll container. WeChat, for example, in the public, this article made a cache, arbitrary click on an article to browse, browse to the half closed after exit, once again open before the article would stay in place, and we can test by oneself, again when open the article is to obtain data, under this scenario is to cache the article details the container’s height, scroll the Save it when you leave the page, and jump to the previous height when you get the data when you enter again. In addition, there are many other ways to cache, you can cache the whole page, cache state data, etc., which can achieve the desired effect, depending on the specific business scenario.

When to deposit

Second, we need to consider when to save. When the page jumps, there will be a variety of action navigation operations, such as: POP, PUSH, REPLACE, etc., when we combine some more general routing library, action will be more detailed, for different actions in different business scenarios are not the same way to deal with, or take wechat public account for example, the article details page is no brain, No matter PUSH or POP, the height data will be stored, so no matter how many times we jump to the page, we can always jump to the position we left before when we open it again. For the scene of the List of goods, we can’t have no brain memory, because it is ok to cache from the List -> Detail -> List. However, when the user returns from the List to another page and enters the List again, he is entering a new page, and logically should not use the cached data, but retrieve the data. The right way to do it is to save it when you PUSH it and get it when you POP it.

Where there is

  1. Persistent cache. If data persistence can be saved toURLlocalStorage, on theURLOne of the nice things about it is that it’s deterministic, it’s easy to spread. butURLYou can firstpassDrop, because in the case of complex lists, there’s a lot of data to store, so put it all inURLIs not realistic. Even if it could, it wouldURLIt was extremely lengthy and obviously inappropriate.localStorageIt’s a way of providinggetItem,setItemSuch APIS are also sufficient to support access operations, with a maximum support of 5M, and the capacity is also sufficient, through serializationSerializeIntegration can also meet requirements, plusIndexDBAnd in a good way,WebSQLHas been abandoned, will not be considered, details can be found in this article by Zhang XinxuHTML5 indexedDB Front-end Local Storage Database ExampleView the comparison.
  2. Memory. For lists or data that do not need to be persisted, memory may be a better approach, as I/O operations in memory are faster and more convenient for frequent read and write operations. So we can put it inreduxrematchState management tools such as, encapsulate some common access methods, very convenient, for common single-page applications, can also be placed in the globalwindowIn the.

When to take

When you get to the cached page, there are several situations when you get to the cached page

  1. When the navigation operation isPOPWhen, because wheneverPUSHIn this case, the data should not be cached.
  2. Data is fetched regardless of the navigation operation, which needs to be viewed along with when it is stored.
  3. Look at specific business scenarios to determine the timing.

Where to take

It’s a simple question, take it from where it exists.

CacheHocThe scheme

  • What to store: list data + scroll height of the scroll container
  • When to Save: The page is away and the navigation action isPUSH
  • Where:window
  • When to take: page initialization phase and navigation operation asPOPwhen
  • Where to take:window

A CacheHoc is a higher-order component that stores cached data in a window and converges with a CACHE_STORAGE. All you need to do is pass in a CACHE_NAME, scrollElRefs, and the CACHE_NAME is the key of the cached data. ScrollElRefs is an array of scrollcontainers. If there are multiple scrollcontainers on a page, the scrollTop and state of the scrollrefs are recorded in componentWillUnmount. Initialize state within Constructor and update scrollTop in componentDidMount.

Simple to use

import React from 'react'
import { connect } from 'react-redux'
import cacheHoc from 'utils/cache_hoc'

@connect(mapStateToProps, mapDispatch)
@cacheHoc
export default class extends React.Component {
  constructor (. props) {
    super(... props)this.props.withRef(this)}/ / set CACHE_NAME
  CACHE_NAME = `customerListThe ${this.props.index}`;
  
  scrollDom = null

  state = {
    orderBy: '2'.loading: false.num: 1.dataSource: [].keyWord: undefined
  }

  componentDidMount () {
    // Set the scroll container list
    this.scrollElRefs = [this.scrollDom]
    // Request data, update state
  }

  render () {
    const { history } = this.props
    const { dataSource, orderBy, loading } = this.state

    return (
      <div className={gcmc('wrapper')} >
        <MeScroll
          className={gcmc('wrapper')}
          getMs={ref= >(this.scrollDom = ref)} loadMore={this.fetchData} refresh={this.refresh} up={{ page: { num: Size: 15 // Number of data per page // time: null // time to load the first page of data returned by the server; Prevent the user page, the background added data resulting in the next page data duplication; } }} down={{ auto: false }} > {loading ? (<div className={gcmc('loading-wrapper')} >
              <Loading />
            </div>
          ) : (
            dataSource.map(item => (
              <Card
                key={item.clienteleId}
                data={item}
                {. this.props}
                onClick={()= >
                  history.push('/detail/id')
                }
              />
            ))
          )}
        </MeScroll>
        <div className={styles['sort']} >
          <div className={styles['sort-wrapper']} onClick={this._toSort}>
            <span style={{ marginRight: 3}} >Recent order time</span>
            <img
              src={orderBy= = ='2' ? SORT_UP : SORT_DOWN}
              alt='sort'
              style={{ width: 10.height: 16}} / >
          </div>
        </div>
      </div>)}}Copy the code

The effect is as follows:

Cached data:

code

const storeName = 'CACHE_STORAGE'
window[storeName] = {}

export default Comp => {
  return class CacheWrapper extends Comp {
    constructor (props) {
      super(props)
      / / initialization
      if (!window[storeName][this.CACHE_NAME]) {
        window[storeName][this.CACHE_NAME] = {}
      }
      const { history: { action } = {} } = props
      / / state
      if (action === 'POP') {
        const { state = {} } = window[storeName][this.CACHE_NAME]
        this.state = { ... state, } } }async componentDidMount () {
      if (super.componentDidMount) {
        await super.componentDidMount()
      }
      const { history: { action } = {} } = this.props
      if(action ! = ='POP') return
      const { scrollTops = [] } = window[storeName][this.CACHE_NAME]
      const { scrollElRefs = [] } = this
      / / get scrollTop
      scrollElRefs.forEach((el, index) = > {
        if(el && el.scrollTop ! = =undefined) {
          el.scrollTop = scrollTops[index]
        }
      })
    }

    componentWillUnmount () {
      const { history: { action } = {} } = this.props
      if (super.componentWillUnmount) {
        super.componentWillUnmount()
      }
      if (action === 'PUSH') {
        const scrollTops = []
        const { scrollElRefs = [] } = this
        scrollElRefs.forEach(ref= > {
          if(ref && ref.scrollTop ! = =undefined) {
            scrollTops.push(ref.scrollTop)
          }
        })
        window[storeName][this.CACHE_NAME] = {
          state: {
            ...this.state
          },
          scrollTops
        }
      }
      if (action === 'POP') {
        window[storeName][this.CACHE_NAME] = {}
      }
    }
  }
}

Copy the code

conclusion

The CacheHoc above is only the simplest implementation, and there are many improvements that can be made. For example: 1. Direct existence in the window is a little rough, multi-page applications stored in the window will lose data, you can consider to save in IndexDB or localStorage, Mescroll will not initialize data if it has a value for componentDidMount. Mescroll will not initialize data if it has a value for componentDidMount.

There are many caching schemes, but these are the ones to consider. In about the need to pay attention to five points, when introduced to save what and which, in fact exist which is not very important, also don’t need too care about, find a suitable place in line, more important is what and when to save, requires a combination of the actual application scenarios, to choose the right way, may be a different page using in different way, and there is no fixed, It is important to analyze the timing and location of access.

Recommended reading

How to implement binary heap with JS

Writing high quality maintainable code: Program paradigm

, recruiting

ZooTeam (ZooTeam), a young and creative team, belongs to the product RESEARCH and development department of ZooTeam, based in picturesque Hangzhou. The team now has more than 40 front end partners, the average age of 27 years old, nearly 30% are full stack engineers, no problem youth storm team. The membership consists of “old” soldiers from Alibaba and netease, as well as fresh graduates from Zhejiang University, University of Science and Technology of China, Hangzhou Electric And other universities. In addition to the daily business connection, the team also carried out technical exploration and actual practice in the direction of material system, engineering platform, building platform, performance experience, cloud application, data analysis and visualization, promoted and implemented a series of internal technical products, and continued to explore the new boundary of the front-end technology system.

If you want to change the things you’ve been doing, you want to start doing things. If you want to change, you’ve been told you need to think more, but you can’t change; If you want to change, you have the power to achieve that result, but you are not needed; If you want to change what you want to accomplish, you need a team to support you, but there is no place for you to bring people; If you want a change of pace, it’s “3 years of experience in 5 years”; If you want to change the original savvy is good, but there is always a layer of fuzzy window paper… If you believe in the power of belief, that ordinary people can achieve extraordinary things, that you can meet a better version of yourself. If you want to get involved in the growth of a front end team with a deep understanding of the business, a sound technology system, technology that creates value, and spillover impact as the business takes off, I think we should talk about it. Any time, waiting for you to write something, to [email protected]