demand

In most emporium scenes, the page will have a category switch plus a list of items, and the page will look something like this

Every time I write a similar scenario, I have to write a lot of logic for the category item list, so I decided to separate this part into components to improve the development efficiency.

implementation

Style 1.

All TAB bar styles and item list styles provide slots for business customization

2. The variable

isTabFixed: falseTAB: 1,// Current TAB page: 1,// Current page: 1,//false// Is this the last pagefalse, / /} in the load, the items: [], / / commodity array tabMap: [], / / the TAB list array cache: {}, / / the cache listName:' '// tabName:' '// TAB list name apiName:' '// API method name queryName:' '// API request parameter nameCopy the code

3. Cache design

To reduce overhead, I cache the list of items that have been requested

_addCache(proList) {
    cache[this.tab] = {
        finished: this.listStatus.finished,
        page: this.page,
        list: proList,
    };
},
Copy the code

4. Request data

_getList(type) {
    let data = {};
    data[this.queryName] = this.tab;
    data.page = this.page;
    this.$http[this.apiName](data) .then((res) => { this.listStatus.finished = ! res.has_more; // Update whether the state of the last page is this._handleData(res.items); / / handle to get the list of goods}). The catch ((err) = > {note (err. Message | |'Wrong');
        });
},
_handleData(proList) {
    if(this. items = proList) {this.items = proList; (this. items = proList; }else{// We need to concatenate the data this.items = this.items. Concat (proList); } this.$store.setData(this.listName, this.items); // Update the data to the parent component this._addCache(this.items); // add data to cache},Copy the code

5. The operating

The logical part is mainly divided into two parts: one is the list page turning, the other is TAB related

A list of pages

The logic of this part is relatively simple, mainly divided into two points

  1. Add page one
  2. The request data
_loadmore() {
    this.page = this.page + 1;
    this._getList();
},
Copy the code

There are a lot of things that need to be noted about the pull-up of the mobile list, such as how to avoid continuous requests, but since I’ve handed them over to another component that focuses on the list rendering, I don’t need to worry about them here.

TAB associated

The TAB to switch

There are two main points in tab-switching

  1. Toggle TAB pointing
  2. Toggles the direction of items
    1. The loaded list is fetched from the cache
    2. Request data that has not been loaded and then there are two points of experience
  3. The view should go back to the top
    1. The first page of the toggle list should be retrieved when switching
changeTab(id) {
    this.tab = id;
    this.$store.setData(this.tabName, this.tab); // synchronize the TAB pointer to the parent component this._scrollTotab (); // View back to topif (cache[this.tab]) {
        const target = cache[this.tab];
        this.listStatus.finished = target.finished;
        this.page = target.page;
        this.items = target.list;
        this.$store.setData(this.listName, this.items); // Synchronize the item list to the parent component}else{ this.page = 1; this._getList(); }}, // View back to the top_scrollToTab() {
    let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
    let productsTop = this.$refs.products.$el.offsetTop; // The distance from the top of the listlet topHeight = this.$refs.tabNav.offsetHeight; // The height of the TAB columnif(scrollTop > productsTop) { window.scrollTo(0, productsTop - topHeight); }},Copy the code

Suck the top

You need to make some small changes to the TAB bar style when you hit the top, so you need a variable to know if you hit the top

handleNavFixed() {
    this.stickyTop = this.$store.getData('stickyTop') | | 0; Onscroll = (e) => {let top = document.documentElement.scrollTop || document.body.scrollTop;
        let tabHeight = this.$refs.tabNav.offsetTop - top - 1;
        if(tabHeight <= this.stickyTop && ! This.istabfixed) {// to avoid double counting this.istabfixed =true;
        }
        if (tabHeight >= this.stickyTop && this.isTabFixed) {
            this.isTabFixed = false; }}; },Copy the code

6. Component correlation

As a component, the difference is that it needs to be usable and universal. How the component can get the value of the parent component and send the updated value back to the parent component is a bottleneck in my development process. Finally, I used a set of component pass-through mechanism based on VUE. The implementation of this kind of traffic mechanism on the net is many, here is not detailed. The traffic mechanism has two main functions

  1. Components can call methods to each other
  2. Each component can get and update the parent component’s data

Since the TAB list is passed in as a slot, you don’t need to care about the initial data, just the update data.

For different pages, the name of the TAB list, the name of the TAB location, the name of the item list, the name of the interface, and the parameters of the request interface can all be different, so I’m going to pass these items in here as parameters to initialize the component

Init (data = {}) {this.listStatus.finished =! data.hasMore; this.tabName = data.tabName; this.listName = data.listName; this.apiName = data.apiName; this.queryName = data.queryName; this.handleNavFixed(); }, // call component this.$bus.emit('tab-list.init', {
    tabName: 'tab',
    listName: 'items',
    apiName: 'homeList',
    queryName: 'tab_id',
    hasMore: this.hasMore,
});
Copy the code

The effect

This page toggles the list of items using this component