Vue(四):生命周期&数据共享

目录

一、组件的生命周期

1.1 生命周期&生命周期函数

1.2 组件生命周期函数的分类

1.3 生命周期图示例

二、组件之间的数据共享

2.1 组件之间的关系

2.1.1 父组件向子组件共享数据

2.1.2 子组件向父组件共享数据

2.1.3 兄弟之间的数据共享

三、ref引用

3.1 什么是ref引用

3.2 使用ref引用获取DOM元素

3.3 使用ref引用组件实例

3.4 this.$nextTick(cb)方法---代码需要等待页面渲染完成后执行

四、数组中的方法

4.1 some循环

4.2 every循环

4.3 reduce方法

五、购物车案例

5.1 案例效果

5.2 实现步骤

5.3 备注

5.4 案例详细代码

5.4.1 Header.vue

5.4.2 Footer.vue

5.4.3 Counter.vue

5.4.4 Goods.vue

5.4.5 App.vue

5.3.6 eventBus.js

5.5 在Vue中使用Bootstrap


一、组件的生命周期

1.1 生命周期&生命周期函数

生命周期是指一个组件从创建->运行->销毁的整个阶段,强调的是一个时间段。

生命周期函数:由vue框架提供的内置函数,会伴随着组件的生命周期,自动按序执行。

1.2 组件生命周期函数的分类

1.3 生命周期图示例

VUE官网生命周期图--------------------Vue 实例 — Vue.js

Vue 实例生命周期

 created生命周期函数,非常常用,经常在它里面,调用methods中的方法,请求服务器的数据。并且把请求到的数据,转存到data中,供template模板渲染的时候用

 Mounted:该阶段已经把内存中的HTML结构,成功地渲染到了浏览器之中。在Mounted函数中最早操作DOM元素。

Updated:页面更新后,操作最新的DOM

二、组件之间的数据共享

2.1 组件之间的关系

项目开发中,组件之间最常用的关系有:(1)父子关系(2)兄弟关系

2.1.1 父组件向子组件共享数据

父组件向子组件共享数据需要使用自定义属性。示例代码如下:

 npm i 安装项目所需的所有的包

2.1.2 子组件向父组件共享数据

子组件向父组件共享数据使用自定义事件

 示例如下

(1)子组件在点击自增时,将自增后的值自定义事件,传递出去

    methods: {
      add() {
        console.log('add')
        this.count += 1
        this.$emit('numchange',this.count)
      }
    },

(2)父组件接收。分为触发事件,事件定义两部分

<template>
<div id="left" style="float:left">
    <MyDemo @numchange="getNumCount"></MyDemo>
</div>
</template>
<script>
export default {
    data() {
        return {
            countFromSon: 0
        }
        
    },
    methods: {
        getNumCount(val){
            this.countFromSon = val
        }
    }
}
</script>
<style lang="less" scoped>
</style>

2.1.3 兄弟之间的数据共享

在vue2.0x中,兄弟之间数据共享的方案是EventBus

 EventBus的使用步骤:

(1)在src/components目录下创建eventBus.js模块,并向外共享一个Vue的实例对象

(2)在数据发送方,d调用bus.$emit('事件名称',要发送的数据)方法触发自定义事件

(3)在数据接收方,调用bus.$on('事件名称',事件处理函数)方法注册一个自定义事件

三、ref引用

3.1 什么是ref引用

ref用来辅助开发者在不依赖于jquery的情况下,获取DOM元素或者组件的引用。

每个vue的组件实例中,都包含一个$ref对象,里面存储着对应的DOM元素或组件的引用。默认情况下,组件的$ref指向一个空对象

3.2 使用ref引用获取DOM元素

总结:ref获取DOM元素分为2步:(1)DOM元素中添加ref属性(2)方法中获取DOM元素,this.$refs.xxx

(1)App.vue中代码如下:

<template>
  <div id="app">
  <h1>App.vue</h1>
  <button @click="print">打印this</button>
  </div>
</template>

<script>
export default {
  methods: {
    print() {
      console.log(this)
    }
  }
}
</script>

<style lang="less" scoped>
</style>

控制台打印this,发现this中存在$refs参数,默认为空值

 (2)在h1中添加ref参数,并在methods中打印this.$refs.myh1的值,获取到了DOM元素。此时便可以操作DOM元素,实例中将字体颜色调为红色

<template>
  <div id="app">
  <h1 ref="myh1">App.vue</h1>
  <button @click="print">打印this</button>
  </div>
</template>

<script>
export default {
  methods: {
    print() {
       console.log(this.$refs.myh1)
       this.$refs.myh1.style.color = "red"
    }
  }
}
</script>

<style lang="less" scoped>
</style>

3.3 使用ref引用组件实例

如果想使用ref引用页面上的组件实例,方式如下:

 应用场景:如下图,app.vue为父组件,left.vue为子组件。left.vue中实现了数据的自增和重置。若此时想实现在app.vue中点击重置,也可以重置left页面的值,就需要先获取left的组件实例。

3.4 this.$nextTick(cb)方法---代码需要等待页面渲染完成后执行

组件的$nextTick(cb)方法,会把cb回调推迟到下一个DOM更新周期之后执行。通俗地来说,等组件的DOM更新完成后,再执行cb回调函数,从而保证cb回调函数可以操作到最新的DOM元素。

如案例按钮和文本框的按需展示:点击按钮后输入框展示,输入框获得焦点后按钮展示。

 解决办法:调用$nextTick(cb)方法

 等待页面渲染完成,那为什么这里不采用Updated方法呢

 分析:inputvisible值默认为false,显示的是按钮。点击按钮,inputvisible值改变为true,文本框显示,触发updated函数,聚焦文本框。文本框聚焦后,触发showButton事件,inputvisible值改变为false,按钮显示。因为inputvisible值改变,触发updated函数,获取文本框DOM失败。

四、数组中的方法

4.1 some循环

在数组中查找值等于某个值的一项。在找到对应的项之后,可以通过return true固定的语法,来终止some循环

forEach用法:无论是否满足条件,都会一直循环下去,无法终止。采用some方法来解决该问题

</script>
const arr = ['zs','ls','ww']
arr.forEach((item,index) => {
  console.log('ok')
  if(item === 'ls') {
    console.log(index)
    return true 
  }
})
</script>

some用法:

</script>
const arr = ['zs','ls','ww']
arr.some((item,index) => {
  console.log('ok')
  if(item === 'ls') {
    console.log(index)
    return true 
  }
})
</script>

4.2 every循环

若item.state值都相等,则result值为true,否则为false

<script>
const arr1 = [
  {id: 1, state: true},
  {id: 2, state: true},
  {id: 3, state: true}
]

const result = arr1.every(item => item.state)
console.log(result)
</script>

4.3 reduce方法

实现用户选中项价格的和。

 <script>
const arr1 = [
  {id: 1, count: 10, state: true},
  {id: 2, count: 20, state: false},
  {id: 3, count: 30, state: true}
]

const result = arr1.filter(item => item.state).reduce((amt, item) => {
  console.log(amt)
    return amt += item.id * item.count
}, 0)


//reduce简写:若只有一行代码,可省去return
const result = arr1.filter(item => item.state).reduce((amt, item) => {amt += item.id * item.count}, 0)
 </script>

五、购物车案例

5.1 案例效果

5.2 实现步骤

(1)初始化项目结构

(2)封装MyHeader组件

(3)基于axios请求商品列表数据(GET请求,地址为https://www.escook.cn/api/cart)

(4)封装MyFoot组件

(5)封装MyGoods组件

(6)封装MyCounter组件

5.3 备注

(1)安装并在App.vue中导入axios。

         npm i axios -S                import axios from 'axios'

(2)在methods方法中,定义initCardList函数请求列表数据

(3)在created生命周期函数中,调用步骤2中封装的initCradList函数

(4)引入Goods.vue组件,v-for将数据循环渲染

(5)每一个商品的标题和图片不同,需要使用props实现

(6)勾选框后后台的state值并未改变,子向父传值,这里用的是自定义事件 

(7)count.toFixed(2)保留2位小数

(8)若存在多层嵌套,如孙子像爷爷传值,可直接借助event Bus实现传值

(9)使用eventBus时创建eventBus文件

注:编码规范,绑定时,先指令,再绑定属性,再事件

注:引入组件的时候最好命名为首字母大写,便于和标签区分

5.4 案例详细代码

主要包括Header.vue、Goods.vue、Footer.vue、Counter.vue、App.vue五个页面。

5.4.1 Header.vue

Header.vue负责页面头部,仅涉及组件调用

<template>
    <div class="headContainer">
        <h5>购物车案例</h5>
    </div>
</template>

<script>
export default {  
};
</script>

<style lang="less" scoped>
.headContainer {
    h5 {
        color: cornsilk;
        font-weight:bold;
    }
    background-color: rgb(30,122,255);
    text-align: center;
    padding: 10px;
}
</style>

5.4.2 Footer.vue

Footer.vue负责页面底部。页面主要包含全选按钮、合计金额、结算的商品数量三部分。涉及父子组件之间的传值。

<template>
  <div class="footer-container">
      <div>
          <div class="check">
            <!-- 调用change事件,当chexkbox的值改变时,执行fullChange事件,
            将全选按钮的选中状态值发送给父组件App.vue,此处是子向父传值-->
            <input type="checkbox" :checked="fullflag" @change="fullChange">
            <span>全选</span>
          </div>
          <!-- 结算总金额保留两位小数,采用toFixed函数,父向子传值 -->
          <!-- 结算总金额和结算的商品数量是父组件App.vue传过来的,父向子传值 -->
          <h5 class="amt">合计:¥{{ amount.toFixed(2) }}</h5>
          <button class="count">结算({{ count }})</button>
      </div>
  </div>
</template>

<script>
export default {
    props: {
        // 全选按钮是否勾选
        fullflag: {
            type: Boolean
        },
        // 商品结算总金额
        amount: {
            default: 0,
            type: Number
        },
        // 结算商品数量
        count: {
            default: 0,
            type: Number
        }
    },
    methods: {
        // 子向父传值,自定义事件
        fullChange(e) {
            this.$emit('changeFull',{flag: e.target.checked})
        }
    }

}
</script>

<style lang="less" scoped>
.check, .amt, .count {
    float: left;
    width: 33%;
}
.count {
    background-color: rgb(4,126,249);
    border-radius: 8%;
}
</style>

5.4.3 Counter.vue

<template>
  <div class="counter-container">
      <!-- 减1的按钮 -->
      <!-- 执行值减1,并将值传递给组件App.vue,此处采用的是eventBus -->
      <button class="btn" @click="minus">-</button>
      <!-- 购买的数量 -->
      <!-- 购买数量是父组件Goods组件传递过来的 -->
      <span class="number-box">{{ count }}</span>
      <!-- 加1的按钮,并将值传递给组件App.vue,此处采用的是eventBus-->
      <button class="btn" @click="add">+</button>
  </div>
</template>

<script>
import bus from '@/components/eventBus.js'
export default {
    props: {
        // 接收商品的id值,将来使用eventBus方案,把数量传递到App.vue的时候,需要通知App组件,
        // 更新的是哪个商品的数量
        id: {
            type: Number,
            required: true
        },
        // 购买的数量,父组件App.vue赋值
        count: {
            default: 1,
            type: Number
        }
    },
    methods: {
        minus() {
            // 要发送App的数据格式为{id,value}
            // 减去的时候等于0的时候,不能再点
            if(this.count - 1 === 0) return 
            const obj  = { id: this.id, value: this.count - 1}
            bus.$emit('send',obj)
        },
        add() {
            const obj  = { id: this.id, value: this.count + 1}
            bus.$emit('send',obj)
        }
    }
}
</script>

<style lang="less" scoped>
.number-box {
    min-width: 30px;
    text-align: center;
    margin: 0 5px;
}
</style>

5.4.4 Goods.vue

<template>
  <div class="goods-container">
      <!-- 左侧图片 -->
      <div class="thumb">
          <div class=" custom-control custom-checkbox">
              <!-- 复选框 -->
              <input type="checkbox" class="custom-control-input" :id="'cb'+id" :checked="state" @change="stateChange">
              <label class="custom-control-lable" :for="'cb'+id">
                  <img :src="pic" alt="">
              </label>
          </div>
      </div>
      <!-- 右侧信息区域 -->
      <div class="goods-info">
          <!-- 商品标题 -->
          <h6 class="goods-title">{{ title }}</h6>
          <div class="goods-info-bottom">
              <!-- 商品价格 -->
              <span class="goods-price">¥{{ price }}</span>
              <!-- 数量标签 -->
              <Counter :count="count" :id="id"></Counter>
          </div>
      </div>
      <hr>
  </div>
</template>

<script>
import Counter from '@/components/Counter/Counter.vue'
export default {
    components: {
        Counter
    },
    props: {
        id: {
            required: true,
            type:Number
        },
        title: {
            default: '',
            type: String
        },
        price: {
            default: 0,
            type: Number
        },
        pic: {
            default: '',
            type: String
        },
        state:{
            default: true,
            type: Boolean
        },
        count: {
            default: true,
            type: Number
        }
    },
    methods: {
        stateChange(e) {
            const newState = e.target.checked
            this.$emit('FstateChange',{id: this.id, value: newState })
        }
    }
}
</script>

<style lang="less" scoped>
.thumb {
    float: left;
    width: 50%
}
</style>

5.4.5 App.vue

<template>
  <div id="app" class="app-container">
    <div>
      <Header></Header>
    </div>  
    <Goods v-for="item in list" :key="item.id" :id="item.id" :title="item.goods_name" :pic="item.goods_img" :price="item.goods_price" :state="item.goods_state" :count="item.goods_count" @FstateChange="getState"></Goods>
    <Footer :fullflag="fullstate" :amount="amt" :count="count" @changeFull="changeAll"></Footer>
  </div>
</template>

<script>
import axios from 'axios'
import Header from './components/Header/Header.vue'
import Goods from './components/Goods/Goods.vue'
import Footer from './components/Footer/Footer.vue'
import bus from '@/components/eventBus.js'
export default {
  components: {
    Header,
    Goods,
    Footer
  },
  data() {
        return {
            list: []
        };
    },
  computed: {
    // 动态计算出全选的状态是true还是false
    fullstate() {
      return this.list.every(item => item.goods_state === true)
    },
    amt() {
      console.log(this.list)
      return this.list.filter(item => item.goods_state).reduce((amt, item) => {
        return amt += item.goods_price * item.goods_count
      },0)
    },
    count() {
      return this.list.filter(item => item.goods_state).reduce((count, item) => {
        return count += item.goods_count
      },0)
    }
  },
  created() {
        this.initCardList()
        this.getCount()

    },
    methods: {
      // 发送axios请求获取数据
        async initCardList() {
             const {data : res} = await axios.get('https://www.escook.cn/api/cart')
             this.list = res.list
             console.log(this.list)
        },
        // 接收子组件传过来的勾选框值,并更新
        getState(val) {
          this.list.some( item => {
              if(item.id === val.id) {
                item.goods_state = val.value
                return true
              }
          })
        },
// 全选或者全不选时将值传给每一个商品的多选框
        changeAll(val){
          this.list.forEach((item,index) => {
            item.goods_state = val.flag
          })          
        },
// 接收Counter.vue点击新增或减去按钮后的counter值
     getCount() {
        bus.$on('send',val => {
          this.list.some(item => {
              if(item.id === val.id) {
                item.goods_count = val.value
                return true
              }
          })
      })
    }
    },

}
</script>

<style lang="less" scoped>
Header {
  padding-top: 0%;
  position: absolute;
}
Footer {
  position: fixed; 
  bottom:0;
}


</style>

5.3.6 eventBus.js

import Vue from 'vue'

export default new Vue()

5.5 在Vue中使用Bootstrap

(1)终端执行npm install bootstrap@3 安装包

(2)然后在Vue项目中的main.js引入

import Vue from 'vue'
import App from './App'
// 引入BootStrap
import 'bootstrap/dist/css/bootstrap.min.css'

new Vue({
  el: '#app',
  components: { App },
  template: '<App/>'
})
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值