昨日回顾

  1. 生命周期- 四个阶段-八个钩子函数
  • 初始化阶段-beforeCreate - created- 实例化前后触发

    created可以获取数据-因为data和methods已经初始化

  • 挂载阶段- beforeMount - mounted - dom挂载前后触发

    mounted可以获取dom对象

  • 数据更新阶段 beforeUpdate - updated - 数据更新前后触发

    一般很少用,因为它是监听所有的数据变化-一般用watch代替

  • watch可以监听某个数据

    组件销毁阶段 beforeDestroy -destroyed

  • beforeDestroy- 确定要销毁了, 但是没有销毁

http的请求工具-axios

  • 市场占有率很高-用法很好
  • 支持Promise,还支持在nodejs使用
  1. 通用写法

    1. axios({
    2. method:'get/post/delete/put/patch', // 请求类型
    3. url: '', // 请求地址
    4. data: {}, // 请求体参数 body参数。get只有地址参数 get没有请求体参数
    5. params: {}, // 地址参数 最终参数会拼接到url地址上
    6. }).then(result => {}).catch()
  2. 别名写法

    1. axios.get(url)
    2. axios.post(url, data) // data是请求体参数
    3. axios.put(url, data)
    4. axios.delete(url)
    5. axios.patch(url, data)
  • 组件的ref属性和$nextTick
  1. ref 可以获取真实的dom元素-可以获取组件的实例对象 ```vue
  2. 组件标签一个ref属性

methods: { getRef() { this.$refs.abc 就是h1的dom this.$refs.efg 获取的是 counter-com的组件的实例对象,也是它的this }

}

  1. 2. $nextTick- Vue中的渲染是异步的
  2. > setTimeout/setInterval/ajax/Promise.then.catch
  3. - 先执行同步代码-等到主线程执行完毕-再去(按照到时顺序)执行任务队列中异步代码
  4. > 数据更新之后,立刻获取dom结构,dom结构是上一次的
  5. - this.$nextTick(() => {}) // 异步更新完成-会执行这个函数确保异步更新已经完成。
  6. > 购物车的案例-json-server- 将一个json文件变成一个服务器-开启一个服务-提供接口
  7. - restful接口规范的接口- 业界通用的一个规范
  8. > 同样的地址不同的类型处理不同的业务
  9. - 用户业务- 新增用户-删除用户-更新用户-查询用户
  10. 查询用户-查询用户的列表-查询单个用户的详情<br />get- /user 查询用户的列表 /user/:id 查询单个用户的详情<br />post- /user 请求体参数 新增用户<br />put- /user/:id 请求体参数 更新用户<br />delete- /user/:id 删除用户
  11. - 获取购物车数据
  12. > axios.defaults.baseURL = "" // 配置基础地址
  13. - created中获取数据
  14. ```javascript
  15. export default {
  16. data() {
  17. return {
  18. list: []
  19. }
  20. }
  21. created() {
  22. this.getGoodsList()
  23. },
  24. methods: {
  25. getGoodsList () {
  26. axios.get().then(result => {
  27. this.list = result.data
  28. })
  29. }
  30. }
  31. }

1. 购物车选中问题

  1. checkbox想要点击图片也选中,label的属性for=”id”, id不能写死,因为商品列表是循环出来的,应该生成动态的id和动态的for
  • 购物车的数据里面有id属性-应用到id属性上就可以了
    <div class="custom-control custom-checkbox">
          <input
            type="checkbox"
            class="custom-control-input"
            :id="`input_${item.id}`"
          />
          <label class="custom-control-label" :for="`input_${item.id}`">
            <img :src="item.goods_img" alt="" />
          </label>
     </div>
    

2. 绑定单选框影响的购物车数据

  • props数据单向数据流-修改的数据-回到父组件

    在子组件中v-model绑定props数据不允许,单向数据流

  • 将数据显示到组件上-绑定了input的 checked数据-

  • 监听input的change事件
  • 子传父-改变的值和id传回给父组件
  • 父组件监听这个事件
  • 重新生成一个新数组- 找到对应的id 改变它的状态

GoodsItem.vue

<template>
  <div class="cart-goods-item">
    <div class="left">
      <div class="custom-control custom-checkbox">
        <!--:checked="item.goods_state" 只是负责展示状态  -->
        <input
          type="checkbox"
          class="custom-control-input"
          :id="`input_${item.id}`"
          :checked="item.goods_state"
          @change="changeChecked"
        />
        <label class="custom-control-label" :for="`input_${item.id}`">
          <img :src="item.goods_img" alt="" />
        </label>
      </div>
    </div>
    <div class="right">
      <div class="top">{{ item.goods_name }}</div>
      <div class="bottom">
        <span class="price">¥ {{ item.goods_price }}</span>
        <span>{{ item.goods_count }}</span>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: ["item"],
  methods: {
    changeChecked(event) {
      this.$emit("updateChecked", this.item.id, event.target.checked);
    },
  },
};
</script>

App.vue

<template>
  <div id="app">
    <cart-header></cart-header>
    <goods-item
      v-for="item in list"
      :key="item.id"
      :item="item"
      @updateChecked="updateChecked"
    ></goods-item>
    <cart-footer></cart-footer>
  </div>
</template>

<script>
import CartHeader from "./components/CartHeader.vue";
import CartFooter from "./components/CartFooter.vue";
import GoodsItem from "./components/GoodItem.vue";
import axios from "axios";
export default {
  name: "App",
  components: {
    CartHeader,
    CartFooter,
    GoodsItem,
  },
  data() {
    return {
      list: [],
    };
  },
  created() {
    this.getGoodsList();
  },
  methods: {
    getGoodsList() {
      axios.get("/goodsList").then(({ data }) => {
        this.list = data;
      });
    },
    updateChecked(id, goods_state) {
      // 修改数组
      // push pop shift unshift splice sort reverse
      // 重新赋值一个新数组-原数组长度一样
      this.list = this.list.map((item) => {
        if (item.id === id) {
          // 只会找到一次
          item.goods_state = goods_state;
        }
        return item;
      });
    },
  },
};
</script>

3. 全选状态的设定

  1. 全选组件在Footer中,状态的展示依赖list中是否全部都勾选, 状态肯定是父组件传给Footer的
  2. 父组件定义计算属性- 算出来有没有全部选中
  3. 传给Footer组件- 赋值给checked属性
  4. 监听input的值改变事件
  5. 触发自定义事件-将true/false => 父组件
  6. 父组件监听事件
  7. 根据true/false => 影响所有数据

Footer.vue

<template>
  <div class="cart-footer">
    <div class="custom-control custom-checkbox">
      <input
        :checked="isAll"
        @change="updateChecked"
        type="checkbox"
        class="custom-control-input"
        id="footerCheck"
      />
      <label class="custom-control-label" htmlFor="footerCheck"> 全选 </label>
    </div>
    <div>
      <span>合计:</span>
      <span class="price">¥ 100</span>
    </div>
    <button type="button" class="footer-btn btn btn-primary">结算 (0)</button>
  </div>
</template>

<script>
export default {
  props: ["isAll"],
  methods: {
    updateChecked(event) {
      // 这里没有id  这是全选按钮 true - true  false- false
      this.$emit("updateAll", event.target.checked);
    },
  },
};
</script>

App.vue

<template>
  <div id="app">
    <cart-header></cart-header>
    <goods-item
      v-for="item in list"
      :key="item.id"
      :item="item"
      @updateChecked="updateChecked"
    ></goods-item>
    <cart-footer :isAll="isAll" @updateAll="updateAll"></cart-footer>
  </div>
</template>

<script>
import CartHeader from "./components/CartHeader.vue";
import CartFooter from "./components/CartFooter.vue";
import GoodsItem from "./components/GoodItem.vue";
import axios from "axios";
export default {
  name: "App",
  components: {
    CartHeader,
    CartFooter,
    GoodsItem,
  },
  data() {
    return {
      list: [],
    };
  },
  created() {
    this.getGoodsList();
  },
  computed: {
    isAll() {
      // 计算出 是不是所有的都勾选了
      return this.list.every((item) => item.goods_state);
    },
  },
  methods: {
    getGoodsList() {
      axios.get("/goodsList").then(({ data }) => {
        this.list = data;
      });
    },
    updateChecked(id, goods_state) {
      // 修改数组
      // push pop shift unshift splice sort reverse
      // 重新赋值一个新数组-原数组长度一样
      this.list = this.list.map((item) => {
        if (item.id === id) {
          // 只会找到一次
          item.goods_state = goods_state;
        }
        return item;
      });
    },
    // 要将所有的状态都跟新成传过来的状态
    updateAll(goods_state) {
      this.list = this.list.map((item) => {
        item.goods_state = goods_state; // true => true  false => false
        return item;
      });
    },
  },
};
</script>

4. 算出总数量和总价格显示在Footer上

  1. 总价格 = 选中的商品 单价 * 数量的总和
  2. 总数量 = 选中的商品的数量总和
    totalPrice() {
       return this.list.reduce((prevValue, item) => {
         // item.goods_state true 加 false item.goods_state
         return (
           prevValue +
           (item.goods_state ? item.goods_count : 0) * item.goods_price
         );
       }, 0);
     },
     totalCount() {
       return this.list.reduce((prevValue, item) => {
         // item.goods_state true 加 false item.goods_state
         return prevValue + (item.goods_state ? item.goods_count : 0);
       }, 0);
     },
    
    传给子组件- 子组件显示

5.动态组件

川剧变脸- 一个人可以不断的切换多张脸

  • 一个位置可以切换不同类型的组件

    用到了Vue的内置组件- component

<component :is="变量" />
// 变量是组件的名字-注册组件的名字
  1. 展示多少个组件,就要创建多少个.vue文件 ```vue