Front-end introduction (vue picture loading framework finished)

Keywords: Vue github Attribute Mobile

Foreword: 2018 has passed half of the year, and I don't feel anything bad in this life year. Every day is still very happy. I thank my friends for their company and gratitude!!! I haven't been home for more than half a year. Recently, I sent a video with Ma Ma. Compared with the New Year, my sister grew 10 cm, Ma was 10 kg thinner, and my dog grew from a small puppy to 30 kg. Ma Ma Ma said, "You are flax." The son has to scrape once he grows up."Only when he finds himself really small ~ ~After experiencing vicissitudes of life for his dream, going through thousands of sails and returning home, he finds himself already not a teenager!!!! No pushing, he enters our theme today.~

I've already written two articles in the same series. Interested children's shoes can go to see Ha. h5 is just starting. The article belongs to personal learning notes. Daniel don't spray!

Front-end introduction (vue picture loading framework 1)
Front-end introduction (vue picture loading framework II)

Front-end introduction (vue picture loading framework II) Finally, we have implemented the basic function of the framework, that is, placeholder(loading and error) placeholder effect.
[Picture upload failed. (image-6288f8-1533729480712)]

Finally, a question remains:
When we have a lot of pictures, we just need to load what we see, and then load when we slide to other parts (time for space). Now we just load all pictures (space for time) as soon as we come out. If it's on the pc side, we can load all pictures directly. It's so fast, and it seems that memory problem on the pc is not a big problem, but when it comes to On the phone side, memory usage directly affects user experience, so we need to load pictures lazily.

Let's first see what happens if we load a few pictures directly.

<template>
  <div class="opt-container">
    <img v-lazy="{src: image}" v-for="(image,index) in images" v-bind:key="'image-'+index">
  </div>
</template>

<script>
  const IMAGES = [
    'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1533137283186&di=c136f7387cfe2b79161f2f93bff6cb96&imgtype=0&src=http%3A%2F%2Fpic1.cxtuku.com%2F00%2F09%2F65%2Fb3468db29cb1.jpg',
    'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1533137283186&di=de941561df3b6fd53b2df9bfd6c0b187&imgtype=0&src=http%3A%2F%2Fpic43.photophoto.cn%2F20170413%2F0008118236659168_b.jpg',
    'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1533137283185&di=aff7e8aa60813f6e36ebc6f6a961255c&imgtype=0&src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F01d60f57e8a07d0000018c1bfa2564.JPG%403000w_1l_2o_100sh.jpg',
    'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1533726597165&di=198b9836a377021082281fcf0e5f3331&imgtype=0&src=http%3A%2F%2Fchongqing.sinaimg.cn%2Fiframe%2F159%2F2012%2F0531%2FU9278P1197T159D1F3057DT20140627094648.jpg',
    'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1533726597167&di=e06fc5f74fac9bb61d229249219cbe4f&imgtype=0&src=http%3A%2F%2Fimg2.ph.126.net%2FuWKrNCkdBNBPzdjxCcUl-w%3D%3D%2F6630220042234317764.jpg'
  ]
  export default {
    name: 'Lazy',
    data() {
      return {
        images: IMAGES,
        showImage: true
      }
    }
  }
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
  .opt-container {
    font-size: 0px;
  }
</style>

As you can see, our code is very simple, that is to load five pictures directly, and then let's look at the traffic consumption:

As you can see, we consumed 1.3MB of traffic, and all the pictures were loaded at the same time. This is only 5 pictures. In the project, hundreds of pictures on one page are not impossible. So it's a little scary to think about. It's good to put them on the pc. It's estimated that the rhythm of crying on the mobile phone. ~So let's think about it. When we slide, the pictures we see will be loaded. We won't load the image until it slides down. Let's try Ha-ha.~~

The first is to listen for the scrolling of the form, because we are directly scrolling the body, so we listen directly:

 mounted(){
      window.onscroll=()=>{
        console.log(window.scrollY);
      }
    }

Then we slide the form:

So we need to notify all listeners (brokers) in onscroll and ask him, "We're in this position now, do you need to load?" First we need to notify manager (manager), and then manager will notify listener (broker):
First inform LazyDelegate, then how can we get the LazyDelegate reference? We can pass it to the prototype of Vue when the install method is executed, so that every instance of Vue will have the reference of LazyDelegate:

import lazyDelegate from './LazyDelegate';

export default {
  install(Vue, options = {}) {
    let LazyClass = lazyDelegate(Vue);
    let lazy = new LazyClass(options);
    Vue.prototype.$Lazyload = lazy
   ...
  }
}

Then in our demo vue file:

 mounted(){
      window.onscroll=()=>{
        this.$Lazyload.lazyLoadHandler();
      }
    }

Then our manager's lazy LoadHandler method notifies listener one by one to load the picture.

 /**
     * Notify all listener s that it's time to work
     * @private
     */
    _lazyLoadHandler() {
      //Find out what has been done.
      const freeList = []
      this.ListenerQueue.forEach((listener, index) => {
        if (!listener.state.error && listener.state.loaded) {
          return freeList.push(listener)
        }
        listener.load()
      })
      //Eliminate listener s who have completed the work
      freeList.forEach(vm => remove(this.ListenerQueue, vm))
    }

We log in _lazy LoadHandler, and when we slide the form:

We can see that by calling back our _lazyLoadHandler method, we will notify all listener s to load the picture:

_lazyLoadHandler() {
      ....
      this.ListenerQueue.forEach((listener, index) => {
        if (!listener.state.error && listener.state.loaded) {
          return freeList.push(listener)
        }
        listener.load()
      })
    ...
    }

As we said earlier, when sliding, there is another condition for notifying listener to load the picture. Is the current img visible in the form?
So we need to add a judgment:

_lazyLoadHandler() {
      ....
      this.ListenerQueue.forEach((listener, index) => {
        if (!listener.state.error && listener.state.loaded) {
          return freeList.push(listener)
        }
       if(Is it in the form?,Is it visible??){
          listener.load()
        }
      })
    ...
    }

The conversion code is:

    /**
     * Notify all listener s that it's time to work
     * @private
     */
    _lazyLoadHandler() {
      //Find out what has been done.
      console.log('_lazyLoadHandler');
      const freeList = []
      this.ListenerQueue.forEach((listener, index) => {
        if (!listener.state.error && listener.state.loaded) {
          return freeList.push(listener)
        }
        //Determine whether it's in the form or not, and don't load the picture if it's not in the form.
        if(!listener.checkInView())return;
        listener.load()
      })
      //Eliminate listener s who have completed the work
      freeList.forEach(vm => remove(this.ListenerQueue, vm))
    }

listener.js:

 getRect() {
    this.rect = this.el.getBoundingClientRect()
  }

  checkInView() {
    this.getRect()
    return (this.rect.top < window.innerHeight && this.rect.bottom > 0
      && this.rect.left < window.innerWidth && this.rect.right > 0)
  }

Let's modify the Lazy.vue test page to give img a certain height, otherwise it will be loaded by default:

<div v-for="(image,index) in images" v-bind:key="'image-'+index">
      <img v-lazy="{src: image}" width="100%" height="500px">
    </div>

Then add a log prompt to Lazy Delegate:

_lazyLoadHandler() {
     ..
        //Determine whether it's in the form or not, and don't load the picture if it's not in the form.
        if(!listener.checkInView())return;
        console.log(listener.src+'It can be loaded.');
        listener.load()
      })
     ...
    }

Let's run through the code to see the effect:

We can see that log, when we slide, when we slide to an img, we load the current img. Let's compare the traffic consumption:

As you can see, the effect is still very obvious when the first screen is only 739 kb, haha. Actually, there is nothing fantastic, just a change of loading mode. We used to change space for time, but now we change space for time. Obviously, the second is more in line with the mobile strategy.

Some children's shoes will say, since you are a framework, why do you put rolling monitoring in components? Also, how can you make sure that someone else is a body rolling? Or a module is rolling itself, so it's not jj? Yes!! Let's optimize our code, when our instructions are implemented to add, we create a listener.
So besides listening to our scroll incidents, what other incidents do we have to listen to? Let's list them:

const DEFAULT_EVENTS = ['scroll', 'wheel', 'mousewheel', 'resize', 'animationend', 'transitionend', 'touchmove']

Then we instruct add to take the scrolling element and listen:

 /**
     * Called only once, when the instruction is first bound to the element. One-time initialization settings are available here.
     * @param el The elements bound by instructions can be used to manipulate DOM directly.
     * @param binding
     * @param vnode
     */
    add(el, binding, vnode) {
      console.log('add');
      let {src, loading, error} = this._valueFormatter(binding.value)

      Vue.nextTick(() => {
        const newListener = new LazyListener({
          el,
          loading,
          error,
          src,
          options: this.options,
          elRenderer: this._elRenderer.bind(this),
        })
        this.ListenerQueue.push(newListener)
        //Get scrolling elements
        let $parent;
        if (!$parent) {
          $parent = scrollParent(el)
        }
        //towindowAdd monitoring
        this._addListenerTarget(window)
        //Adding listeners to parent scroll elements
        this._addListenerTarget($parent)
        Vue.nextTick(() => {
          this.lazyLoadHandler()
        })
      })
    }

    /**
     * Add monitoring
     * @param el
     * @private
     */
    _addListenerTarget(el) {
      if (!el) return
      DEFAULT_EVENTS.forEach((evt) => {
        el.addEventListener(evt, this.lazyLoadHandler.bind(this), false)
      })

    }

function scrollParent(el) {
  if (!(el instanceof HTMLElement)) {
    return window
  }
  let parent = el

  while (parent) {
    if (parent === document.body || parent === document.documentElement) {
      break
    }

    if (!parent.parentNode) {
      break
    }

    if (/(scroll|auto)/.test(overflow(parent))) {
      return parent
    }

    parent = parent.parentNode
  }

  return window
}

function overflow(el) {
  return style(el, 'overflow') + style(el, 'overflow-y') + style(el, 'overflow-x')
}

const style = (el, prop) => {
  return typeof getComputedStyle !== 'undefined'
    ? getComputedStyle(el, null).getPropertyValue(prop)
    : el.style[prop]
}

Finally, we modify the code of the test page:

<template>
  <div class="opt-container">
    <div v-for="(image,index) in images" v-bind:key="'image-'+index">
      <img v-lazy="{src: image}" width="100%" height="500px">
    </div>
  </div>
</template>

<script>
  const IMAGES = [
    'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1533137283186&di=c136f7387cfe2b79161f2f93bff6cb96&imgtype=0&src=http%3A%2F%2Fpic1.cxtuku.com%2F00%2F09%2F65%2Fb3468db29cb1.jpg',
    'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1533137283186&di=de941561df3b6fd53b2df9bfd6c0b187&imgtype=0&src=http%3A%2F%2Fpic43.photophoto.cn%2F20170413%2F0008118236659168_b.jpg',
    'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1533137283185&di=aff7e8aa60813f6e36ebc6f6a961255c&imgtype=0&src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F01d60f57e8a07d0000018c1bfa2564.JPG%403000w_1l_2o_100sh.jpg',
    'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1533726597165&di=198b9836a377021082281fcf0e5f3331&imgtype=0&src=http%3A%2F%2Fchongqing.sinaimg.cn%2Fiframe%2F159%2F2012%2F0531%2FU9278P1197T159D1F3057DT20140627094648.jpg',
    'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1533726597167&di=e06fc5f74fac9bb61d229249219cbe4f&imgtype=0&src=http%3A%2F%2Fimg2.ph.126.net%2FuWKrNCkdBNBPzdjxCcUl-w%3D%3D%2F6630220042234317764.jpg'
  ]
  export default {
    name: 'Lazy',
    data() {
      return {
        images: IMAGES,
        showImage: true
      }
    },
    mounted(){
    }
  }
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
  .opt-container {
    font-size: 0px;
  }
</style>

Then we run the code:

When we slide up, it's consistent with what we did before.~~~

All right!!! Here we have parsed the frame code of the picture, and knocked it with us. Some children's shoes will feel a little familiar with the code, right, the code of vue-lazyload, haha!!! My little buddies don't drag my code directly into the project, if you want to use it, drag the code of vue-lazyload directly, and finally attach the github link of demo and the github link of vue-lazyload:

DEMO address: https://github.com/913453448/VuexDemo.git

[Vue-Lazyload address: https://github.com/hilongjw/vue-lazyload
](https://github.com/hilongjw/vue-lazyload
)

Finally, like-minded small partners are welcome to join the group, and exchanges are welcome.~~~~
Group qq:

Posted by senojeel on Wed, 08 May 2019 11:42:39 -0700