Git Product home page Git Product logo

anotherleon.github.io's Introduction

anotherleon.github.io

Leon‘s Blog

anotherleon.github.io's People

Watchers

 avatar

anotherleon.github.io's Issues

TAB便签异步请求数据引发的BUG

TAB便签异步请求数据引发的BUG

最近做了一个小程序的项目,用mpvue写的,项目很小,主要有登录页,查询页,查询结果页,维保页等,我负责开发登录和结果页。之前觉得项目很简单,应该很容易就能搞定,没想到十个工作日的排期都在跌跌撞撞中进行。

问题描述

  • 说说遇到的问题吧,结果页有一部分列表是使用tab按钮的方式展现的,按照采购和零售行情,把不同的来源的车放在不同的tab下进行切换展示。当我非常快速的点击切换tab页时,最后切换的那个tab页会先显示别的tab下的数据然后再变成自己的数据。

原因分析

  • 事后分析原因的话,其实很简单。就是异步请求导致的数据返回的顺序不确定,导致给list赋值的时候,无法保证给list的就是当前便签请求到到的数据。比如我点击tab a之后马上点击了tab b,然后又点tab a,这时候有可能b的数据请求结束,并把数据给了carList,而渲染a下的列表用的数据实际上是请求b返回的。所以a下面会先显示b的,然后再显示a自己的数据。

解决方法一(同步)

  • 其实一开始并没有这个问题,之前后端把所有的数据一次性给前端,前端负责分类显示,不涉及到异步请求的过程,因此没有这个现象。因此我认为解决方法之一就是和后端预定好请求的方式,如果遇到展示数据很少的情况可以一次请求,如果是使用vue的话,直接设置list为computed属性,根据name或者id,filter数组即可。

解决方法二(异步)

  • 这种情况比较复杂,我采用的解决办法是设置一个cache对象,代码如下
    getCarList(searchForm, options) {
        // ... 省略部分非关键代码
        const sellerName = this.sellerName 
        if (!sellerName) return
        const cache = this.cacheList[sellerName]
        if (cache && !!cache.length) {
            this.carList = cache
            return
        }
        // 异步请求过程
        wx.request({
            // ... 省略部分代码
            success: (res) => {
                // 先把获得的数据放进缓存
                this.cacheList[sellerName] = res.data.data.carInfoList
                // 出现bug的地方,this.carList就好比一个筐子,谁先请求到数据,它的里面装的就是谁。
                // this.carList = res.data.data.carInfoList // 同时把数据给v-for循环中的carList,carList得值变化或会重新渲染列表
                this.carList = this.cacheList[this.sellerName] || [] // 临时处理的办法:从缓存中找,没找到就是空的
            },
        })
    },

缺陷

  • this.carList = this.cacheList[this.sellerName],这样的写法隐藏隐患,因为carList,cacheList,sellerName随时可能在请求的过程中被修改。比如:由tab a快速切换到 tab b 的时候,tab a 正好请求回数据,这时候实际上执行的是 this.carList = this.cacheList['b'] || [],而这时候缓存中其实肯定是没有b的,所以必然为空数组。
  • 快速切换tab(还没有缓存)的时候,每次点击都会请求一次数据,造成多次无用请求。

改进

    getCarList(sellerName, searchForm, options) {
        /* ... 省略部分非关键代码  */
        // 处理promise的方法
        const process = async (promise) => {
            const result = await promise
            if (result.sellerName === this.sellerName) { // 根据当前tab的sellerName和结果中的sellerName是否一致决定items的数据
                this.items = result.data
            }
        }
        this.items = [] // 清空一下,防止装着别的数据
        // 第一次请求缓存里没有promise
        const cache = this.cacheList[sellerName]
        if (cache) {
            process(cache[sellerName])
            return false
        }
        // promise化小程序异步请求过程,这一步可以进一步封装...
       const p = new Promise((resolve, reject) => {
            wx.request({
                /* 省略部分代码 */
                resolve({sellerName, data: data.data.carInfoList}) // 返回sellerName作为一个凭证
            })
        })
        this.cacheList[sellerName] = p // 把promise保持在缓存里,之后如果promise已经resolve就不用等待了
        process(p)
    },

补充: 也可以把所有的数据放在list{ [sellerName]: [] }这样的数据结构里,每次请求的数据这样添加this.list = {...this.list, data}就能保持数据响应。这样可以省去判断数据是否是当前的。

进一步解耦

  • 上面的代码虽然已经可以正常用了,但其实代码与业务逻辑耦合性很高,能否进一步解耦?请看代码。
    // 业务页面
    async onClick(sellerName) { /// 这里是相应点击事件的业务逻辑
        this.sellerName = sellerName
        this.items = []
        this.loading = true
        const result = await memoizedReqest(sellerName)
        if(result.name === this.sellerName){ 
            this.items = result.data
            this.loading = false
        }
    },
    // utils.js
    request(options) {
       return new Promise((resolve, reject) => {
            wx.request({
                /* 省略部分代码 */
                resolve({name, data: res}) 
            })
        })
    }
    memoize(func) {
        var cache = {}
          return function(name) {
            context = this;
            args = arguments;
            if (cache[name]) return cache[name]
            return cache[name] = func.apply(context, args);
          };
    }
    export default memoizedReqest = memoize(request)

总结

异步请求的应该场景还是挺多的,比如类似百度的搜索框的自动联想、树形目录、分页、新闻列表等等都可能用到异步请求,因此我认为这篇总结对类似情境有一定的参考意义,也希望对看到这篇文章的人有所帮助。

当然我知道的我的方法必定是不完美的,一定存在更优雅、更具有普遍适用性的方案,不过限于本人的见识目前只能做到这样了,我在今后的工作中也将继续探索更好的解决办法。

通过这个简单的小项目,我发现要做到完美并不容易,需要对框架、业务、JS语言的特性有很深的理解才行。当然办法总部困难多,只要动脑筋多思考总会有更好的办法的。

1. i++ 和 ++i 的区别

The value i++ is the value of i before the increment. The value of ++i is the value of i after the increment.

  • i++:返回 i 增加之前的值
  • ++i:返回 i 增加之后的值

Example:

  var i = 42;
  alert(i++);   // shows 42
  alert(i);        // shows 43
  i = 42;
  alert(++i);  // shows 43
  alert(i);       // shows 43

一名【合格】前端工程师的自检清单

https://mp.weixin.qq.com/s/SJVKl-cTpqIz1vcUgNy_Cw

  1. 三种判断 JavaScript数据类型的方式,以及他们的优缺点,如何准确的判断数组类型

CSS

  1. 盒子模型、相邻的Box都设置margin可能发生什么情况?
  2. 有哪些常用表示大小的单位, 使用的场合
    px最常用,绝对 em 相对,当前字体 rem 相对根元素字体 % 相对包裹层 vw 视口宽 vh 视口高
  3. viewport
  4. 文字省略
    width: 100px; // 要有宽
    overflow: hidden;
    text-overflow: ellipsis;
    -webkit-line-clamp: @clamp;
    white-space:nowrap;
  1. 居中
    文本、图片、定宽块、不定块块
    考察点 line-height, flex布局、block、inline-blcok, vertical-align, 负margin

  2. relative,absolute 文档流

JS/ES6

let 块级作用域、先声明后使用(没有变量提示)、不能重复声明 const 常量

解构赋值
变量名和属性名不一致怎么办:let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
默认值写法? fetch({page=1, pageSize=10}), 如果传入的是 {page:null, pageSize: undefined} 会怎样? undefined 才会触发默认值

async函数,写法 返回值,await 结果

箭头函数 this 指向外层函数运行时的this,外层没有指向全局
箭头函数 如果想返回一个对象,但是又不想使用return,怎么办 =>({})
优点 简洁优雅

装饰器

三种判断类型的方式

数组过滤falsy值
const compact = arr => arr.filter(Boolean)
compact([0, 1, false, 2, '', 3, 'a', 'e' * 23, NaN, 's', 34])

React

react生命周期

ref的干什么用的

组件通信方式
父->子 props
子->父 回调
兄弟、多层级 redux、mobx、react context

setState要注意什么
异步

mobx redux 相关概念

  • 设计组件的原则
    低耦合
    无状态组件
    小组件
    一个组件完成一件事情
    尽量无副作用、不要再组件内修改传入的引用类型参数
    抽象业务逻辑
    props要尽量扁平化,传入什么参数一目了然,便于维护,例如 // 不够扁平化

项目
代码目录分层

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.