vue生命周期和购物车案例

1. 生命周期介绍

首先来看下面两张图,一张是完整流程图一张是介绍vue四大阶段的八大方法图
vue生命周期和购物车案例_第1张图片
vue生命周期和购物车案例_第2张图片

下面会详细介绍其使用情况。

所谓生命周期,就是vue从创建到销毁的整个过程。上面八大方法我们简称为钩子函数,作用是在特定的时间点执行特定的操作,就比如在组件创建完毕后,我们可以在created生命周期函数中发起Ajax请求,从而初始化data数据。

vue从创建->编译->显示->更新->销毁的过程。在每个过程中就会执行不同的方法。并且在上图我把整个生命周期分成了四个阶段。

  1. 创建->编译 执行beforecreate和created称为初始化阶段。在created阶段能获取真实data和method因此在这个时候发起Ajax请求

new Vue() -Vue实例化
Init Events & Lifecycle 初始化事件和生命周期函数
vue生命周期和购物车案例_第3张图片
beforeCreate生命周期钩子函数被执行
Init injections & reactivity -vue内部添加data和methods
created生命周期钩子函数被执行,实例创建
接下来是模板编译阶段,Has el option?目的是检查要挂载到index.html中的哪个地方。如果没有,调用$mount()方法,如果有继续检查template选项。第一个阶段到此结束。

  1. 编译->显示,执行了beforeMounte和mounted属于DOM挂载阶段。

首先template选线检查,有的话编译template返回render渲染函数。无的话编译el选项对应标签作为template。
虚拟DOM挂载成真实DOM之前。
beforeMount被执行。
Cretae vm and replace就是把虚拟DOM和渲染的数据一并挂载到真实DOM上
真实DOM挂载完毕
mounted被执行,也就是在mounted才可以调用到真实DOM

  1. 显示->更新,在updated函数里可以获取更新后的DOM

当data里数据改变时,更新DOM之前
首先进行个循环,执行beforeUpdate
Virtual DOM re-render 然后虚拟DOM重新渲染,打补丁。
执行updated函数
当有data数据改变,重复这个循环

  1. 更新->销毁,一般在destoryed函数里手动消除计时器,定时器或者全局事件

当destory被调用,比如组件DOM被移除
执行beforeDestory
拆卸数据监视器,子组件和事件监听器
实例销毁后,最后触发一个钩子函数
destoryed生命周期钩子函数被执行

2. axios使用

axios是一个专门用于发送Ajax请求的库。特点是

  • 支持客户端发送Ajax请求
  • 支持服务店Node.js发送请求
  • 支持Promise相关语法
  • 支持请求和相应的拦截器功能
  • 自动转换JSON数据
  • 底层是原生JS实现,内部通过Promise封装
    vue生命周期和购物车案例_第4张图片
    对于GET请求来说,Ajax在URL拼接查询字符串,axios会在params这个配置项中自动写到url后面。
    对于POST请求来说,在请求体中传递数据,在axios中,data选项可以把参数自动装入到请求体中。默认发给后台请求体数据格式是JSON字符串格式。
    axios的好处是可以配置基础地址,统一管理axios.default.baseURL

3. refs和nextTick使用

在mounted生命周期中,获取原生DOM有两种方法,通过id或者ref。

      <p>1. 获取原生DOM元素</p>
      <h1 id="h" ref="myH">我是一个孤独可怜又能吃的h1</h1>
      mounted(){
        console.log(document.getElementById("h")); // h1
        console.log(this.$refs.myH); // h1
     }

如果我们定义组件的话,那么我们可以调用组件内的所有方法,通过this.$refs.组件名

      <p>2. 获取组件对象 - 可调用组件内一切</p>
      <Demo ref="de"></Demo>
       components: {
        Demo
      },
      mounted(){
        let demoObj = this.$refs.de;
        demoObj.fn()
      }

之后我们可以调用组件里的方法和属性。

      <p>3. vue更新DOM是异步的</p>
      <p ref="myP">{{ count }}</p>
      <button @click="btn">点击count+1, 马上提取p标签内容</button>
      methods: {
        btn(){
            this.count++; // vue监测数据更新, 开启一个DOM更新队列(异步任务)
            console.log(this.$refs.myP.innerHTML); // 0
       }
      }

因为vue更新DOM是异步的,更新语句会被放入到任务队列,最后再去执行。

            this.$nextTick(() => {
                console.log(this.$refs.myP.innerHTML); // 1
            })

因此调用nextTick方法可以访问到更新后的DOM,当然我们也可以在updated生命周期钩子函数中。需要注意的是$nextTick返回的是一个promise对象。以下为一个简单的输入框聚焦案例

<template>
  <div>
      <input ref="myInp" type="text" placeholder="这是一个输入框" v-if="isShow">
      <button v-else @click="btn">点击我进行搜索</button>
  </div>
</template>

<script>
// 目标: 点按钮(消失) - 输入框出现并聚焦
// 1. 获取到输入框
// 2. 输入框调用事件方法focus()达到聚焦行为
export default {
    data(){
        return {
            isShow: false
        }
    },
    methods: {
        async btn(){
            this.isShow = true;
            // this.$refs.myInp.focus()
            // 原因: data变化更新DOM是异步的
            // 输入框还没有挂载到真实DOM上
            // 解决:
            // this.$nextTick(() => {
            //     this.$refs.myInp.focus()
            // })
            // 扩展: await取代回调函数
            // $nextTick()原地返回Promise对象
            await this.$nextTick()
            this.$refs.myInp.focus()
        }
    }
}
</script>

<style>

</style>

4. 购物车案例


把上述案例分为四个模块,头部模块MyHeader底部模块MyFooter中间模块MyGoods数量模块MyCount,其中最复杂的部分在于全选框的设计与实现。

  1. 我们需要从后台获取数据,那么数据放在哪个页面呢?当然放在父页面(App.vue)这个组件中,然后把这个数据分别传递给各个其他组件,因此涉及到父传子。在我们引入组件后,我们需要在父组件中通过定义:arr=list传递或者在for循环中,通过:gObj=obj的形式传递数据
<template>
  <div>
    <MyHeader title="购物车案例"></MyHeader>
    <div class="main">
      <MyGoods v-for="obj in list" :key="obj.id"
      :gObj="obj"
      ></MyGoods>
    </div>
    <MyFooter @changeAll="allFn" :arr="list"></MyFooter>
  </div>
</template>

其中我们需要注意的是需要在created方法中调用axios函数,引入后台数据,并且把数据复制给list空数组。

  1. 头部组件MyHeader中,为了实现组件的复用,因此我们不能把文字和颜色写死,其中在子组件中接受父组件传过来的值,可以进行一定的约束规范
<template>
  <div class="my-header" :style="{backgroundColor: background, color}">{{ title }}</div>
</template>
    props: {
        background: String, // 外部插入此变量的值, 必须是字符串类型, 否则报错
        color: {
            type: String, // 约束color值的类型
            default: "#fff" // color变量默认值(外部不给 我color传值, 使用默认值)
        },
        title: {
            type: String,
            required: true // 必须传入此变量的值
        }
    }
  1. 一个MyGoods组件只能渲染一条商品,因此我们是在App的页面进行for循环,而且把MyGoods组件的东西分为左半部分和右半部分,左边是复选框和label。其中复选框是checkbox并且给复选框关联id属性,这样的话每个商品的id都不一样。给复选框用v-model双向关联商品信息。label和input只要给的值相同,label的for属性和input的id属性,那么就会实现双向绑定。即点击Label和点击input效果一样。右半部分主要设计Mycount组件的使用。
<template>
  <div class="my-goods-item">
    <div class="left">
      <div class="custom-control custom-checkbox">
        <!-- *重要:
          每个对象和组件都是独立的
          对象里的goods_state关联自己对应商品的复选框
         -->
         <!-- bug:
          循环的所有label的for都是input, id也都是input - 默认只有第一个生效
          解决: 每次对象里的id值(1, 2), 分别给id和for使用即可区分
          -->
        <input type="checkbox" class="custom-control-input" :id="gObj.id"
        v-model="gObj.goods_state"
        >
        <label class="custom-control-label" :for="gObj.id">
          <img :src="gObj.goods_img" alt="">
        </label>
      </div>
    </div>
    <div class="right">
      <div class="top">{{ gObj.goods_name }}</div>
      <div class="bottom">
        <span class="price">¥ {{ gObj.goods_price }}</span>
        <span>
            <MyCount :obj="gObj"></MyCount>
        </span>
      </div>
    </div>
  </div>
</template>
  1. 在Mycount组件中,用于统计商品的数量,我们需要注意的是通过props属性,把父组件拿到的后台值,传递给其他组件,并且我们可以通过修改其中的变量值,实现双向绑定,对于组件中的两个button元素和一个input输入框来说,分别为他们绑定动态属性,在数量相减的按钮中,绑定禁止事件,规定商品数量为1,就不可以再减少了。并且规定只有商品数量大于1,才能进行减按钮的操作。在数量相加的按钮中,如果不设置上限规定,那么就直接绑定动态事件。对于输入框来说,需要用v-model实现双向数据绑定,并且将v-model设置为number属性。那么对于这个组件来说,还存在一个bug,也就是我可以手动输入商品数量,如果输入的值小于1.那么该如何处理呢?此时我们需要用到watch来监测值的变化,并且是深度监听,如果商品数量小于1,会被强制修改为1.
> <template>
  <div class="my-counter">
    <button type="button" class="btn btn-light" :disabled="obj.goods_count === 1" @click="obj.goods_count > 1 && obj.goods_count--">-</button>
    <input type="number" class="form-control inp" v-model.number="obj.goods_count">
    <button type="button" class="btn btn-light" @click="obj.goods_count++">+</button>
  </div>
</template>
  1. 在MyFooter中实现全选框的功能,首先我们要明白全选框在MyFooter组件中,每个商品的小选框在MyGoods组件中,但是这两个组件之间没有引入关系,因此我们可以采取跨组件通信的方式,或者直接向App.vue里面传递。
    <div class="custom-control custom-checkbox">
      <input type="checkbox" class="custom-control-input" id="footerCheck" v-model="isAll">
      <label class="custom-control-label" for="footerCheck">全选</label>
    </div>

上面为全选框的逻辑代码,跟label进行关联,并且设置v-model为isAll这个变量
并且isAll是属于计算属性

  computed: {
    isAll: {
      set(val){ // val就是关联表单的值(true/false)
        this.$emit('changeAll', val)
      },
      get(){
        // 查找小选框关联的属性有没有不符合勾选的条件
        // 直接原地false
        return this.arr.every(obj => obj.goods_state === true)
      }
    },
   }

通过复选框本身的属性,通过set函数,传递给主组件

    <MyFooter @changeAll="allFn" :arr="list"></MyFooter>
    methods: {
    allFn(bool){
      this.list.forEach(obj => obj.goods_state = bool)
      // 把MyFooter内的全选状态true/false同步给所有小选框的关联属性上
    }
  }

并且在methods函数中声明这个方法,逻辑代码为复选框的选中状态与其余所有小选框的选中状态一致。并且我们注意的是小选框的选中状态是用obj.goods_state来进行双向绑定的。以上便是整个小项目的业务逻辑流程。

你可能感兴趣的:(Vue,vue)