vue 生命周期、数据共享

一、组件的生命周期

1、生命周期 & 生命周期函数

  • 生命周期(Life Cycle)是指一个组件从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段
  • 生命周期函数:是由 vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行

注意:生命周期强调的是时间段,生命周期函数强调的是时间点。

2、组件生命周期函数分类

  • 组件创建阶段:new Vue()beforeCreatecreatedbeforeMountmounted
  • 组件运行阶段:beforeUpdateupdated
  • 组件销毁阶段:beforeDestroydestroyed

3、生命周期详解图

从学习视频中获得

二、组件之间的数据共享

1、组件之间的关系

在项目开发中,组件之间的最常见的关系分为如下两种:
父子关系
兄弟关系

2、父子组件之间的数据共享

父 -> 子共享数据
子 -> 父共享数据

(1)父组件向子组件共享数据

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

// 父组件
<Left :username="username"></Left>

data() {
  return {
    username: 'admin'
  }
}

//  子组件
<template>
    <div>
        <p>用户名:{{username}}</p>
    </div>
</template>
<script>
export default {
    props:['username']
}
</script>

在这里插入图片描述

(2)子组件向父组件共享数据

子组件向父组件共享数据使用自定义事件。示例代码如下

// 父组件
<Right @changeName="chagneFromSon"></Right>
<script>
	methods: {
	  chagneFromSon(val) {
	    this.username = val;
	  }
	}
</script>

//  子组件
<template>
    <div>
        <button @click="change">我要换名字</button>
    </div>
</template>
<script>
export default {
    data() {
        return {
            username: '来自子组件'
        }
    },
    methods: {
        change() {
            this.$emit('changeName', this.username)
        }
    },
}
</script>

在这里插入图片描述

3、兄弟组件之间的数据共享

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

在这里插入图片描述

EventBus 的使用步骤
① 创建 eventBus.js 模块,并向外共享一个 Vue 的实例对象

import Vue from 'vue'
// 向外共享 Vue 的实例对象
export default new Vue()

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

import bus from './eventBus.js'
export default {
    data() {
        return {
            msg: 'hello vue.js'
        }
    },
    methods: {
        sendMsg() {
            bus.$emit('share', this.msg)
        }
    }
}

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

import bus from './eventBus.js'
export default {
    data() {
        return {
            msgFromLeft:''
        }
    },
    created() {
        bus.$on('share', val => {
            this.msgFromLeft = val
        })
    }
}

三、ref 引用

1、 什么是 ref 引用

  • ref 用来辅助开发者在不依赖于 jQuery 的情况下,获取 DOM 元素或组件的引用。
  • 每个 vue 的组件实例上,都包含一个 $refs 对象,里面存储着对应的 DOM 元素或组件的引用。默认情况下,组件的 $refs 指向一个空对象。

2、使用 ref 引用 DOM 元素

如果想要使用 ref 引用页面上的 DOM 元素,则可以按照如下的方式进行操作:

<h1 class="box" ref="appH1">根组件</h1>
<button @click="chagneColor">改变"根组件"颜色</button>

chagneColor() {
      this.$refs.appH1.style.color = 'red';
},

3、 使用 ref 引用组件实例

如果想要使用 ref 引用页面上的组件实例,则可以按照如下的方式进行操作:

<button @click="chagneUserColor">改变"用户名"字体颜色</button>
<Left :username="username" ref="leftCom"></Left>

methods: {
  chagneUserColor() {
    this.$refs.leftCom.chagneColor();
  }
},  

在这里插入图片描述

4、 让文本框自动获得焦点

当文本框展示出来之后,如果希望它立即获得焦点,则可以为其添加 ref 引用,并调用原生 DOM 对象的.focus() 方法即可。示例代码如下

<input type="text" v-if="inputVis" ref="ipt">
<button v-else @click="showInput">展示输入框</button>

showInput(){
  this.inputVis=true;
  //数据已存在,但是还未渲染至页面,所以未实现,解决方法见(5、this.$nextTick(cb) 方法)
  this.$refs.ipt.focus();
},

5、 this.$nextTick(cb) 方法

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

showInput(){
 this.inputVis=true;
 this.$nextTick(()=>{
   this.$refs.ipt.focus();
 })
},

六、购物车案例

1、案例效果

在这里插入图片描述

2、目录展示

在这里插入图片描述

3、代码实现

(1)main.js

import Vue from 'vue'
import App from './App.vue'
// 导入 bootstrap 样式表
import 'bootstrap/dist/css/bootstrap.min.css'

// 全局注册axios
import axios from 'axios'
Vue.prototype.axios = axios

Vue.config.productionTip = false

new Vue({
  render: h => h(App)
}).$mount('#app')

(2)app.vue

<template>
  <div class="app-container">
    <Header :title="title"></Header>
    <Goods v-for="item in list" :key="item.id" :id="item.id" :tit="item.goods_name" :price="item.goods_price+''"
      :pic="item.goods_img" :status="item.goods_state" :num="item.goods_count"  @status-change="statusChange"></Goods>
    <Footer :fullState="fullState" :amt="amount" :count="count" @change-full="changeFull"></Footer>
  </div>
</template>

<script>
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'
export default {
  components: {
    Header,
    Goods,
    Footer
  },
  computed: {
    fullState() {
      return this.list.every(item => item.goods_state)
    },
    amount() {
      return this.list.filter(item => item.goods_state).reduce((sum, item) => sum += item.goods_price * item.goods_count, 0)
    },
    count() {
      return this.list.filter(item => item.goods_state).reduce((sum, item) => sum = sum + item.goods_count, 0)
    }
  },
  data() {
    return {
      title: '购物车案例',
      list: [],
    }
  },
  created() {
    // 获取购物车列表
    this.initCartList();
    bus.$on('num', (val) => {
      this.list.some(item => {
        if (item.id == val.id) {
          item.goods_count = val.value;
          return true
        }
      })
    })
  },
  methods: {
    // 获取购物车列表
    async initCartList() {
      const { data: res } = await this.axios.get('https://www.escook.cn/api/cart')
      if (res.status == 200) {
        this.list = res.list
      }
    },
    // 商品选中状态变化
    statusChange(e) {
      console.log(e)
      this.list.some(item => {
        if (item.id === e.id) {
          item.goods_state = e.value
          return true
        }
      })
    },
    // 商品是否全选
    changeFull(e) {
      this.list.forEach(item => item.goods_state = e)
    }
  }
}
</script>

<style lang="less" scoped>
.app-container {
  padding-top: 45px;
  padding-bottom: 50px;
}
</style>

(3)Header.vue

<template>
  <div class="header-container">{{title}}</div>
</template>

<script>
export default {
  props:['title']
}
</script>

<style lang="less" scoped>
.header-container {
  font-size: 12px;
  height: 45px;
  width: 100%;
  background-color: #1d7bff;
  display: flex;
  justify-content: center;
  align-items: center;
  color: #fff;
  position: fixed;
  top: 0;
  z-index: 999;
}
</style>

(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="'cb1'+id" :checked="status" @change="statuChange" />
        <label class="custom-control-label" :for="'cb1'+id">
          <!-- 商品的缩略图 -->
          <img :src="pic" alt="" />
        </label>
      </div>
    </div>
    <!-- 右侧信息区域 -->
    <div class="goods-info">
      <!-- 商品标题 -->
      <h6 class="goods-title">{{tit}}</h6>
      <div class="goods-info-bottom">
        <!-- 商品价格 -->
        <span class="goods-price">¥{{price}}</span>
        <!-- 商品的数量 -->
        <Counter :id="id" :num="num"></Counter>
      </div>
    </div>
  </div>
</template>

<script>
import Counter from '../Counter/Counter.vue'
export default {
  components: { Counter },
  props: {
    id: {
      require: true,
      type: Number,
    },
    tit: {
      default: '',
      type: String
    },
    pic: {
      default: '',
      type: String
    },
    price: {
      default: '',
      type: String
    },
    status: {
      default: true,
      type: Boolean
    },
    num: {
      default: 1,
      type: Number
    }
  },
  methods: {
    statuChange(e) {
      this.$emit('status-change', { id: this.id, value: e.target.checked })
    }
  }

}
</script>

<style lang="less" scoped>
.goods-container {
  +.goods-container {
    border-top: 1px solid #efefef;
  }

  padding: 10px;
  display: flex;

  .thumb {
    display: flex;
    align-items: center;

    img {
      width: 100px;
      height: 100px;
      margin: 0 10px;
    }
  }

  .goods-info {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    flex: 1;

    .goods-title {
      font-weight: bold;
      font-size: 12px;
    }

    .goods-info-bottom {
      display: flex;
      justify-content: space-between;

      .goods-price {
        font-weight: bold;
        color: red;
        font-size: 13px;
      }
    }
  }
}
</style>

(5)Counter.vue

<template>
  <div class="number-container d-flex justify-content-center align-items-center">
    <!-- 减 1 的按钮 -->
    <button type="button" class="btn btn-light btn-sm" @click="sub">-</button>
    <!-- 购买的数量 -->
    <span class="number-box">{{num}}</span>
    <!-- 加 1 的按钮 -->
    <button type="button" class="btn btn-light btn-sm" @click="add">+</button>
  </div>
</template>

<script>
import bus from '../eventBus'
export default {
  props: {
    id: {
      type: Number,
      require: true
    },
    num: {
      default: 1,
      type: Number,
    }
  },
  methods: {
    add() {
      bus.$emit('num', { id: this.id, value: this.num + 1 })
    },
    sub() {
      if (this.num > 1) {
        bus.$emit('num', { id: this.id, value: this.num - 1 })
      }
    }
  }
}
</script>

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

.btn-sm {
  width: 30px;
}
</style>

(6)Footer.vue

<template>
  <div class="footer-container">
    <!-- 左侧的全选 -->
    <div class="custom-control custom-checkbox">
      <input type="checkbox" class="custom-control-input" id="cbFull" :checked="fullState" @change="fullChange" />
      <label class="custom-control-label" for="cbFull">全选</label>
    </div>

    <!-- 中间的合计 -->
    <div>
      <span>合计:</span>
      <span class="total-price">¥{{ amt.toFixed(2) }}</span>
    </div>

    <!-- 结算按钮 -->
    <button type="button" class="btn btn-primary btn-settle">结算({{ count }})</button>
  </div>
</template>

<script>
export default {
  props:{
    fullState:{
      default:false,
      type:Boolean
    },
    amt:{
      default:0,
      type:Number
    },
    count:{
      default:0,
      type:Number
    }
  },
  methods:{
    fullChange(e){
      this.$emit('change-full',e.target.checked)
    }
  }
}
</script>

<style lang="less" scoped>
.footer-container {
  font-size: 12px;
  height: 50px;
  width: 100%;
  border-top: 1px solid #efefef;
  position: fixed;
  bottom: 0;
  background-color: #fff;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 10px;
}

.custom-checkbox {
  display: flex;
  align-items: center;
}

#cbFull {
  margin-right: 5px;
}

.btn-settle {
  height: 80%;
  min-width: 110px;
  border-radius: 25px;
  font-size: 12px;
}

.total-price {
  font-weight: bold;
  font-size: 14px;
  color: red;
}
</style>

(7)eventBus.js

import Vue from 'vue'
export default new Vue();
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值