VUE项目笔记(idolmall)

71 阅读6分钟

计算机里面没有黑魔法

common是公共的,可以在多个项目使用 content是和本项目业务有关的公共文件

我来一一介绍

项目优化

1,添加home.js作为中间件,专门管理home模块的所有网络请求

  • home.vue面向home.js开发
  • home.js面向request.js

这样做的好处是,可以使网络请求的代码和.vue文件里的函数分离开,防止耦合(良好的代码设计)

home页

函数里面的变量是局部变量,函数执行完,数据就不存在了(被内存回收掉),函数的调用带。

函数调用:是把变量压入函数栈,函数执行结束,变量会pop出来,被内存回收掉

垃圾回收

  • 当函数执行完之后,变量被回收,变量指向对象的箭头(指针)消失了,然后对象因为没有指针指着他也会被回收

所以我们才需要另外创建一个变量,把res赋给它,然后由它来指向原来res指向的对象,这样对象(数据)就不会被回收

我们这样取,直接取到list(只是这个项目这样,而且是看了数据才这样决定的)

然后我们就取出来优美的一个个对象的格式

github上面有很多vue的UI库(ui小组件),可以拿下来直接用,就像轮播图这样的其实也是小组件

我们把**可以单独用的功能(代码)**取出,创建一个home子组件,把它放进home的子组件里

 <swiper>
    <swiper-item v-for="item in banners">
      <a :href="item.link">
        <img :src="item.image" alt="">
      </a>
    </swiper-item>
  </swiper>

然后再在母组件里,导入子组件,并且把数据传给调用过来的子组件。。。如图

flex到底是啥意思

关于滚动问题,我们后面会用到一个叫better-scroll

  position: fixed;

这是什么意思???

导入组件的时候,把公共组件,子组件,方法分隔开,养成良好的代码规范

import NavBar from "../../components/common/navbar/NavBar";
  import TabControl from "../../components/content/tabControl/TabControl";

  import HomeSwiper from "./childComps/HomeSwiper";
  import RecommendView from "./childComps/RecommendView";
  import FeatureView from "./childComps/FeatureView";
  
  import {gethomemultidata} from "../../network/home";

吸顶效果:

  • 监听滚动

简单法(不过如果要兼容ie就不要这样做)

  .tab-control{
    position: sticky;
    top: 44px;
  }

如何解决vue自带的复用问题,如果你不想让input复用的话,你可以绑定一个key

保存商品的数据结构设计

  • 这里我们直接这样做

数据结构

  • map类数据结构

怎么把一个数组的内容同步(塞)到另一个数组

push本身语法,括号里面表示可变参数,意思就是可以push很多

一旦你看到一个函数里面有很多...意味着可以传很多东西,这是一个语法,对数组进行解析,把元素一个一个push出来

        goods:{
          'pop':{page:0,list:[]},
          'news':{page:0,list:[]},
          'sell':{page:0,list:[]},
      }

我们来看看一开始加一,为什么后面还要加呢?(没错,我们把方法封装了,在created里面只是引用了一下)

    created() {
      this.gethomemultidata()
      this.getHomeGoods('pop')
    },
     methods:{
      getHomeGoods(type){
        const page =this.goods[type].page + 1
        getHomeGoods(type,page).then(res => {
          this.goods[type].list.push(...res.data.list)
          this.goods[type].page+=1
        })
    }

因为我们这里定义的page变量是为了获得最新页码,然后传给后台服务器看,它的名字甚至可以不用叫page 如图 还有这种塞list的做法是动态的,相当于网址不一样时,又塞一次,所以我们刷新网页的时候会变化。

心累,看看下面两个属性作用

.goods{
  display: flex;
  flex-wrap: wrap;//包裹,防止挤一块
  justify-content: space-around;//均等分(中间缝隙等于侧边的两倍)
}

如果不满意,可以微调,比如设置padding

父子件通信(我服了我又忘了)

props:{//从外面传进来
      titles:{
        type:Array,
        default(){
          return[]
        }
      }
      
itemClick(index){
        this.currentIndex =index;
        this.$emit('tabClick',index)//从里面传出去
      }

case穿透???

为什么下面这里要加两个this,this到底是是什么??

computed:{
      showGoods(){
        return this.goods[this.currentType].list
      }
    },

移动端滚动卡顿怎么办

  • github,iscroll(太老了不用它)
  • better scroll (用它) npm install better scroll
  • 然后,
<template>
  <div class="wrapper" ref="aaa">
  <ul>
    <li>顺滑演示1</li>
    <li>顺滑演示2</li>
    <li>顺滑演示3</li>
    <li>顺滑演示4</li>
    <li>顺滑演示5</li>
    <li>顺滑演示6</li>
    <li>顺滑演示7</li>
    <li>顺滑演示8</li>
    <li>顺滑演示9</li>
    <li>顺滑演示10</li>
    <li>顺滑演示11</li>
    <li>顺滑演示12</li>
    <li>顺滑演示13</li>
    <li>顺滑演示14</li>
    <li>顺滑演示15</li>
    <li>顺滑演示16</li>
    <li>顺滑演示17</li>
    <li>顺滑演示18</li>
    <li>顺滑演示19</li>
    <li>顺滑演示20</li>
    <li>顺滑演示21</li>
    <li>顺滑演示22</li>
    <li>顺滑演示23</li>
    <li>顺滑演示24</li>
    <li>顺滑演示25</li>
    <li>顺滑演示26</li>
    <li>顺滑演示27</li>
    <li>顺滑演示28</li>
    <li>顺滑演示29</li>
    <li>顺滑演示30</li>
    <li>顺滑演示31</li>
    <li>顺滑演示32</li>
    <li>顺滑演示33</li>
    <li>顺滑演示34</li>
    <li>顺滑演示35</li>
    <li>顺滑演示36</li>
    <li>顺滑演示37</li>
    <li>顺滑演示38</li>
    <li>顺滑演示39</li>
    <li>顺滑演示40</li>
    <li>顺滑演示41</li>
    <li>顺滑演示42</li>
    <li>顺滑演示43</li>
    <li>顺滑演示44</li>
    <li>顺滑演示45</li>
    <li>顺滑演示46</li>
    <li>顺滑演示47</li>
    <li>顺滑演示48</li>
    <li>顺滑演示49</li>
    <li>顺滑演示50</li>
  </ul>
  </div>
</template>

<script>
  import BScroll from 'better-scroll'
  export default {
    name: "Category",
    data(){
      return{
        scroll:null
      }
    },
    mounted() {
      this.scroll = new BScroll(this.$refs.aaa)
    }
  }
</script>

<style scoped>
.wrapper{
  height: 100px;
  background-color: #5ea732;
  overflow: hidden;
}
</style>

记住 滚的时候要按着鼠标拖,而不是用鼠标滚轮。。

然后,尽量用ref,而且,ref不但可以绑定在组件上还可以在普通元素上(比如div)

ref如果是绑定在组件中的, 那么通过this.$refs.refname获取到的是一个组件对象.

ref如果是绑定在普通的元素中, 那么通过this.$refs.refname获取到的是一个元素对象.

滚动监测

  // 默认情况下BScroll是不可以实时的监听滚动位置
  // probe 侦测
  // 0,1都是不侦测实时的位置
  // 2: 在手指滚动的过程中侦测, 手指离开后的惯性滚动过程中不侦测.
  // 3: 只要是滚动, 都侦测.
  const bscroll = new BScroll(document.querySelector('.content'), {
    probeType: 3,
    click: true,
    pullUpLoad: true
  })

监听位置,position位置

bscroll.on('scroll', (position) => {
    // console.log(position);//
  })

上拉加载更多

  • pullingUp
  bscroll.on('pullingUp', () => {
    console.log('上拉加载更多');
    // 发送网络请求, 请求更多页的数据

    // 等数据请求完成, 并且将新的数据展示出来后
    setTimeout(() => {
      bscroll.finishPullUp()
    }, 2000)
  })

解决框架更改的终极方案:封装

注意,插条要这样放哦,wrapper里面只可以一整个大的东西,所以要再加一个div包住(这个插槽到时候可能会塞很多小东西)

此代码在Scroll.vue组件里,没错封装了。。
<template>
<div class="wrapper">
  <div class="content">
  <slot></slot>
  </div>
</div>
</template>

不要在created里面获取外部信息,要在mounted里面 OK?

100vh代表百分之百的视口的高度

 #home{
    /*padding-top: 44px;*/
    height: 100vh;
  }
  .content{
   height: calc(100% - 93px);
    overflow: hidden;
    margin-top: 44px;
  }

这个样式设计有点烧脑

监听组件,.native

   <back-top @click.native="backclick"/>

回到顶部

   this.scroll.scrollTo(0,0)

监听位置(优化前)

    mounted() {
      this.scroll=new BScroll(this.$refs.hhh,{
        click:true,
        probeType:3
      })
      this.scroll.on("scroll",(position)=>{
        console.log(position)
      })

优化后

为什么要这样优化呢,朋友,因为不一定每个界面都需要监听位置,如果像上面一样这样直接赋值3,那么每次调用scroll组件都会回调一次position,影响性能

  export default {
   props:{
      probeType: {
        type:Number,
        default:0
      }
    },
      mounted() {
      this.scroll=new BScroll(this.$refs.hhh,{
        click:true,
        probeType:this.probeType
      })
      this.scroll.on("scroll",(position)=>{
        console.log(position)
      })
 }

未解之谜,为什么在scroll组件里面打印不行????????两次都不行!!! why

实现回到顶部小按键在固定位置消失

//第一步传事件和参数position给父组件
 this.scroll.on('scroll', (position) => {
        console.log(position);
        this.$emit('scroll',position)
      })
//为事件绑定方法      
 <Scroll class="content" @scroll="contentScroll">
 //布尔值可能会判断为变量,所以搞个变量接收布尔值
 isShowBackTop:false
 //绑定变量给小组件
  <back-top @click.native="backclick" v-show="isShowBackTop"/>
//写方法            
contentScroll(position){
         this.isShowBackTop=-(position.y)>1000
      },

上拉加载更多核心代码

      pullUpLoad: {
        type:Boolean,
        default: false
      }
    mounted() {
      this.scroll=new BScroll(this.$refs.w,{
        click:true,
        pullUpLoad:this.pullUpLoad
     })
    this.scroll.on('pullingUp',()=>{
          this.$emit('pullingUp')
    })
     :pull-up-load="true"
     @pullingUp="loadmore"
     loadmore(){
        this.getHomeGoods(this.currentType)
      },

因为图片是异步加载的原因,加载需要时间, better-scroll反应不过来那个所有图片的总长度变化,不能及时地改变可滑动长度,其实就是和图片加载速度有关

解决

        this.$refs.scroll.scroll.refresh()

解决最大的bug,关于滚动的bug

如何监听图片加载完成了?

  • 原生的js监听图片: img.onload = function() {}
  • Vue中监听: @load='方法'

事件总线出场

如何将GoodsListItem.vue中的事件传入到Home.vue中

  • 因为涉及到非父子组件的通信, 所以这里我们选择了

    事件总线

    • bus ->总线
    • Vue.prototype.$bus = new Vue()
    • this.bus.emit('事件名称', 参数)
    • this.bus.on('事件名称', 回调函数(参数))

    优化

  • 问题一: refresh找不到的问题

  • 该代码报错

      this.$bus.$on('itemImageLoad',() =>{
         this.$refs.scroll.refresh()
      })
  • 第一: 在Scroll.vue中, 调用this.scroll的方法之前, 判断this.scroll对象是否有值
  • 第二: 在mounted生命周期函数中使用 this.$refs.scroll而不是created中
  • 问题二: 对于refresh非常频繁的问题, 进行防抖操作

该代码执行频繁

      this.$bus.$on('itemImageLoad',() =>{
         this.$refs.scroll.refresh()
      })
  • 防抖debounce/节流throttle(课下研究一下)
  • 防抖函数起作用的过程:
    • 如果我们直接执行refresh, 那么refresh函数会被执行30次.
    • 可以将refresh函数传入到debounce函数中, 生成一个新的函数.
    • 之后在调用非常频繁的时候, 就使用新生成的函数.
    • 而新生成的函数, 并不会非常频繁的调用, 如果下一次执行来的非常快, 那么会将上一次取消掉
     debounce(func, delay) {
        let timer = null
        return function (...args) {
          if (timer) clearTimeout(timer)
          timer = setTimeout(() => {
            func.apply(this, args)
          }, delay)
        }
      },

有的时候,加上判断更保险(&&)因为它们不一定同时请求到

  scrollTo(x, y, time) {
          this.scroll && this.scroll.scrollTo(x, y, time)
        }
      },
      finishPullUp() {
        this.scroll.finishPullUp()
      },
      refresh() {
        this.scroll && this.scroll.refresh()
      }

在better-scroll前提下设置吸顶功能(难点)

1,监听轮播图加载完了没有 2,本来就是父子组件的话,不用设置全局的事件总线??所以事件总线是?

加载一次就够啦!

  data(){
       return{
          isLoad:false
       }
   },
  methods:{
    imageLoad(){
      if (!isLoad){
        this.$emit('swiperImageLoad')
        this.isLoad=true
      }
    }
  }

防抖专题

目的:减轻服务器压力 核心:学会等待下一(n)个图片/字符来临,然后一起请求服务器

下面我们看到

  • 设置了延时,delay=50
  • 而且为什么timer虽然在函数里面,为局部变量还是无法销毁呢??因为函数里面有个闭包操作对它进行了引用。
  • 但是不不太能理解里面的arg是什么??
  • 还有apply()是什么函数?
mounted() {
         const refresh=debounce(this.$refs.scroll.refresh,50)
         this.$bus.$on('itemImageLoad',()=>{
        refresh()
      })
      
debounce(func,delay){
         let timer=null
        return function (...args) {
          if (timer) clearTimeout(timer)
          timer=setTimeout(()=>{
            func.apply(this.args)
          },delay)
        }
      }

面试考点

setTimeout里面的函数是异步函数,在下一次事件循环的时候执行,所以就算后面没有写延迟时间,它也会放在后面执行(也就是本身带有延迟时间

timer=setTimeout(()=>{
            func.apply(this.args)
          },delay)
//里面的它 函数        
()=>{
            func.apply(this.args)
          }

打印顺序:这个考点涉及到浏览器事件循环

受不了,这个也打印不出来!和之前一模一样的情况!!

 refresh() {
        console.log('.......')
        this.scroll && this.scroll.refresh()
      }

吸顶成功了 但是不圆滑,而且两个bar不同步·