当前位置:   article > 正文

Vue笔记(4)-组件_const { data: res }

const { data: res }

1.生命周期

  

  1. export default{
  2. data(){
  3. return {
  4. username:"admin"
  5. }
  6. },
  7. beforeCreate(){
  8. console.log(this.username); // 报错,data还不可用
  9. },
  10. // 通常在这个阶段发送请求拿数据
  11. created(){
  12. console.log(this.username); // 输出 admin
  13. }
  14. }

 

  1. export default{
  2. data(){
  3. return {
  4. username:"admin"
  5. }
  6. },
  7. beforeMount(){
  8. console.log(this.$el); // undefined
  9. },
  10. mounted(){
  11. console.log(this.$el); // DOM节点信息
  12. }
  13. }

 

2.组件之间的数据共享

2.1 父组件与子组件共享

使用自定义属性,父组件传递值给子组件。

注意:子组件中不要修改props的值。 如果传递的普通类型,JS会复制一份给子组件,子组件就算修改了,父组件的值也不会修改。  如果是引用类型虽然可以修改但也不建议子组件直接去修改。

2.2 自定义事件(子组件向父组件)

  1. // 子组件
  2. <div @click="add">count +1</div>
  3. export default{
  4. data(){
  5. return {
  6. count:0
  7. }
  8. },
  9. methods:{
  10. add(){
  11. this.count += 1;
  12. // 产生一个自定义事件,并将count传递过去
  13. this.$emit('countChanged',this.count);
  14. }
  15. }
  16. }
  1. // 父组件
  2. <son @countChanged="countChangedHandler"></son>
  3. import son from "*****";
  4. export default{
  5. data(){
  6. return {
  7. bookCount:0
  8. }
  9. },
  10. // 注册组件
  11. components:{
  12. son
  13. },
  14. methods:{
  15. countChangedHandler(count){
  16. // count是子组件传递来的
  17. this.bookCount = count;
  18. }
  19. }
  20. }

2.3 兄弟组件共享解决方案 EventBus

eventBus.js 虽然创建了vue实例但并不去渲染任何东西,只负责帮助两个组件之间的数据传递。 eventBus.js这个文件仍然放在 components 文件夹下。

3.ref

每个Vue组件实例上都有一个 $refs 对象。里面存储了对应的DOM元素或组件的引用。 默认$refs指向一个空对象。 当我们在vue中给任意一个Dom添加 ref属性后,这个Dom属性就会出现在$refs中。

  1. <template>
  2. <div>
  3. <h1 ref="title">Hello</h1>
  4. <button @click="btnClick">this</button>
  5. </div>
  6. </template>
  7. <script>
  8. expoprt default{
  9. methods:{
  10. btnClick(){
  11. console.log(this); // this是一个VueComponent里面的$refs中又一个title它就是h1这个DOM对象
  12. console.log(this.$refs.title); // 输出DOM对象
  13. }
  14. }
  15. }
  16. </script>

如果我们给子组件设置了ref 那么这个组件的实例对象将被存储在 $refs 中。所以可以在父组件中使用 $refs.refname 获取到子组件的实例对象。

  1. // son
  2. export default{
  3. data(){
  4. return {
  5. msg:"Hello!"
  6. }
  7. },
  8. methods:{
  9. sonMethod(){
  10. console.log(this.msg);
  11. }
  12. }
  13. }
  14. <template>
  15. <div>
  16. <son ref="sonComponent"></son>
  17. </div>
  18. </template>
  19. <script>
  20. import son from "....";
  21. export default {
  22. components:{
  23. son
  24. },
  25. methods:{
  26. test(){
  27. const son = this.$refs.sonComponent;
  28. son.sonMethod(); // 调用子组件里的sonMethod方法
  29. son.msg; // 获取子组件的数据
  30. }
  31. }
  32. }
  33. </script>

3.1 this.$nextTick

当使用v-if切换组件或dom元素时,如果是从false切换到true。这个组件的代码会被重新写入到HTML中,而这一个过程是需要时间的,如果在写入之前就去操作这个组件或DOM元素,就会出现该组件是一个undefine的错误。

this.$nextTick(cb());  在Dom渲染完毕后回调cb方法。 操作DOM元素的代码应该写在回调方法中。

4. 数组的一些方法

4.1 some

循环遍历数组,在方法中返回boolean。当有一个返回true时结果为true且立即结束循环。 如果都返回false,结果为false.

问题:为什么不使用forEach?     因为foreach一旦开始就无法在中途停止循环,如果使用return强制停止会终止整个方法的运行

  1. function haveA(arr){
  2. let result = arr.some((item,index)=>{
  3. if(item == 'a'){
  4. return true;
  5. }
  6. });
  7. if(result){
  8. console.log("have a");
  9. }
  10. }
  11. // forEach虽然可以实现但他会循环所有元素,消耗资源
  12. function haveA(arr){
  13. let result = false;
  14. arr.forEach((item,index)=>{
  15. if(item == 'a'){
  16. result = true;
  17. }
  18. });
  19. if(result){
  20. console.log("have a");
  21. }
  22. }

4.2 every

every和some的区别就是只有当所有列表项都返回true时,结果才为true.

4.3 reduce

reduce 就是累加器,最终结果是每次循环返回的数值的总和。

  1. const arr = [
  2. // status代表是否选中
  3. {name:'西瓜',status:true,price:10,count:2},
  4. {name:'榴莲',status:false,price:90,count:0},
  5. {name:'葡萄',status:true,price:12,count:3},
  6. {name:'草莓',status:false,price:20,count:0}
  7. ];
  8. // 求选中的水果的总价格
  9. let result = arr.filter(item => item.status).reduce((amt,item)=>{
  10. return amt += item.count * item.price;
  11. },0);
  12. // 解释
  13. arr.filter(item => item.status) //先过滤出被选中的
  14. .reduce((累加的结果,当前遍历项)=>{},初始值); //累加总价格

4.3.1 reduce的简单写法

arr.filter(item=>item.status).reduce((amt,item)=>amt += item.count*item.price,0);

5.购物车案例

创建项目: vue create cart-demo   该项目只需要使用到 scss,babel。

打开App.vue在该文件中对标签清除一下样式

  1. <style lang="scss">
  2. *{
  3. margin:0px;
  4. padding: 0px;
  5. list-style: none;
  6. font-size: 14px;
  7. color: black;
  8. }
  9. #app{
  10. padding: 0px 20px;
  11. }
  12. </style>

5.1 header组件

在components文件夹下创建 header.vue文件

  1. <template>
  2. <div class="header">购物车</div>
  3. </template>
  4. <script>
  5. export default {
  6. }
  7. </script>
  8. <style lang="scss" scoped>
  9. .header{
  10. color: #7e74b1;
  11. font-size: 24px;
  12. padding: 10px 0px;
  13. }
  14. </style>

在App.vue中引入并使用该组件

  1. <template>
  2. <div id="app">
  3. <headerCom></headerCom>
  4. </div>
  5. </template>
  6. <script>
  7. // 不能叫header因为H5中有header标签
  8. import headerCom from '@/components/header.vue';
  9. export default {
  10. name: 'App',
  11. components: {
  12. headerCom
  13. }
  14. }
  15. </script>

5.2 请求列表数据

安装 axios:

cnpm install axios -S

在App.vue中定义请求方法

  1. methods:{
  2. async initCartList(){
  3. const {data:res} = await axios.get('https://escook.cn/api/cart');
  4. console.log(res);
  5. }
  6. },

我们应该在渲染界面前,实例化对象后请求,所以应该在created生命周期中调用该方法

  1. created(){
  2. this.initCartList();
  3. },

将数据保存到data中

  1. data(){
  2. return {
  3. cartList:[]
  4. }
  5. },
  6. methods:{
  7. async initCartList(){
  8. const {data:res} = await axios.get('https://escook.cn/api/cart');
  9. if(res.status == 200){
  10. this.cartList = res.list;
  11. }
  12. }
  13. },

5.3 创建商品条目组件

该组件就是用来显示每一条商品的信息的。

  1. <template>
  2. <div class="goodsitem-container flex align-item-center">
  3. <!-- 多选框 -->
  4. <div class="checkBox checkBox-checked flex flex-center flex-noshrink"></div>
  5. <!-- 商品图片 -->
  6. <img src="@/assets/logo.png" alt="商品图" class="goodsImg flex-noshrink">
  7. <!-- 商品信息 -->
  8. <div class="goodsInfo flex flex-column justify-content-between">
  9. <div class="goodsInfo-title">班俏BANQIAO超火ins潮卫衣女士2020秋季新款韩版宽松慵懒风薄款外套带帽上衣</div>
  10. <div class="goodsInfo-bottom flex justify-content-between">
  11. <div class="goodsInfo-price">$199</div>
  12. <div class="goodsInfo-count flex">
  13. <div class="goodsInfo-count-sub flex flex-center">-</div>
  14. <div class="goodsInfo-count-num flex flex-center">1</div>
  15. <div class="goodsInfo-count-plus flex flex-center">+</div>
  16. </div>
  17. </div>
  18. </div>
  19. <!-- 商品信息end -->
  20. </div>
  21. </template>
  22. <script>
  23. export default {
  24. }
  25. </script>
  26. <style lang="scss" scoped>
  27. // 总容器
  28. .goodsitem-container{
  29. width:100%;
  30. margin-bottom: 38px;
  31. // 商品信息
  32. .goodsInfo{
  33. margin-left: 12px;
  34. flex-grow: 1;
  35. height: 70px;
  36. overflow: hidden;
  37. max-width: 65%;
  38. div{
  39. font-weight: bold;
  40. font-size: 16px;
  41. color: #7e74b1;
  42. }
  43. .goodsInfo-title{
  44. white-space: nowrap;
  45. }
  46. // 计数器
  47. .goodsInfo-count{
  48. .goodsInfo-count-sub,.goodsInfo-count-plus{
  49. background-color: #f7f6fe;
  50. width:26px;
  51. height: 24px;
  52. border: 1px solid rgba($color: #000000, $alpha: 0.3);
  53. }
  54. .goodsInfo-count-sub{
  55. border-radius: 10px 0px 0px 10px;
  56. }
  57. .goodsInfo-count-plus{
  58. border-radius: 0px 10px 10px 0px;
  59. }
  60. .goodsInfo-count-num{
  61. width:44px;
  62. height: 24px;
  63. border-top: 1px solid rgba($color: #000000, $alpha: 0.3);
  64. border-bottom: 1px solid rgba($color: #000000, $alpha: 0.3);
  65. }
  66. }
  67. }
  68. // 商品图片
  69. .goodsImg{
  70. width:70px;
  71. height: 70px;
  72. border-radius: 10px;
  73. overflow: hidden;
  74. margin-left: 16px;
  75. background-color: #7e74b1;
  76. }
  77. // 多选框
  78. .checkBox{
  79. width:26px;
  80. height:26px;
  81. border-radius: 100px;
  82. background-color: #7e74b1;
  83. }
  84. // 多选框被选中的样式
  85. .checkBox-checked::after{
  86. content:'';
  87. display: table;
  88. width: 16px;
  89. height:16px;
  90. border-radius: 100px;
  91. background-color: rgb(212, 240, 89);
  92. }
  93. }
  94. .flex{
  95. display: flex;
  96. }
  97. .flex-noshrink{
  98. flex-shrink: 0;
  99. }
  100. .flex-center{
  101. justify-content: center;
  102. align-items: center;
  103. }
  104. .flex-column{
  105. flex-direction: column;
  106. }
  107. .justify-content-between{
  108. justify-content: space-between;
  109. }
  110. .align-item-center{
  111. align-items: center;
  112. }
  113. </style>

写好之后也是在App.vue中引用,注册并使用。

  1. <!-- 商品条目 -->
  2. <goodsItemVue v-for="item in cartList" :key="item.id"></goodsItemVue>

5.4 将商品条目的数据改为动态的

  1. <template>
  2. <div class="goodsitem-container flex align-item-center">
  3. <!-- 多选框 -->
  4. <div class="checkBox flex flex-center flex-noshrink" :class="{'checkBox-checked':state}"></div>
  5. <!-- 商品图片 -->
  6. <img :src="pic" alt="商品图" class="goodsImg flex-noshrink">
  7. <!-- 商品信息 -->
  8. <div class="goodsInfo flex flex-column justify-content-between">
  9. <div class="goodsInfo-title">{{ title }}</div>
  10. <div class="goodsInfo-bottom flex justify-content-between">
  11. <div class="goodsInfo-price">¥{{price}}</div>
  12. <div class="goodsInfo-count flex">
  13. <div class="goodsInfo-count-sub flex flex-center">-</div>
  14. <div class="goodsInfo-count-num flex flex-center">1</div>
  15. <div class="goodsInfo-count-plus flex flex-center">+</div>
  16. </div>
  17. </div>
  18. </div>
  19. <!-- 商品信息end -->
  20. </div>
  21. </template>
  22. <script>
  23. export default {
  24. props:{
  25. // 标题
  26. title:{
  27. defualt:'',
  28. type:String
  29. },
  30. // 图片
  31. pic:{
  32. defualt:'',
  33. type:String
  34. },
  35. // 价格
  36. price:{
  37. default:0,
  38. type:Number
  39. },
  40. // 选中状态
  41. state:{
  42. default:true,
  43. type:Boolean
  44. }
  45. }
  46. }
  47. </script>
  1. <!-- 商品条目 -->
  2. <goodsItemVue
  3. v-for="item in cartList" :key="item.id"
  4. :title="item.goods_name"
  5. :pic="item.goods_img"
  6. :price="item.goods_price"
  7. :state="item.goods_state"></goodsItemVue>

如果使用的不是自定义的多选框,而是checkbox不要使用 v-model 绑定选中状态,要使用v-bind因为我们不推荐组件中直接修改props的值。

5.5 封装props两种方法的区别

刚刚我们将所有的数据都一 一传递过去,当然也可以直接将对象传递过去。 

第一种方式的好处就是可以更好的复用组件,直接传递对象的话,如果另一个地方获取到的对象的属性名不一样就无法服用该组件。缺点就是麻烦。

5.6 修改选中状态

在子组件中不可以直接修改props的值,但是我们可以通过发送事件通知父组件,让父组件去更新。

  1. <template>
  2. <div class="goodsitem-container flex align-item-center">
  3. <!-- 多选框 -->
  4. <!-- 添加一个点击事件 -->
  5. <div
  6. class="checkBox flex flex-center flex-noshrink"
  7. :class="{'checkBox-checked':state}"
  8. @click="stateChange"></div>
  9. ....
  10. </div>
  11. </template>
  12. <script>
  13. export default {
  14. props:{
  15. // 新增id 用于传递给父组件让父组件知道谁的选中状态被改变
  16. id:{
  17. type:Number,
  18. require:true
  19. },
  20. .....
  21. },
  22. methods:{
  23. /**
  24. * 多选框被点击
  25. */
  26. stateChange(){
  27. let event = {id:this.id,state:!this.state};
  28. // 发送消息
  29. this.$emit('state-change',event);
  30. }
  31. }
  32. }
  33. </script>
  1. <!-- 商品条目 -->
  2. <goodsItemVue
  3. v-for="item in cartList" :key="item.id"
  4. :id="item.id"
  5. :title="item.goods_name"
  6. :pic="item.goods_img"
  7. :price="item.goods_price"
  8. :state="item.goods_state"
  9. @state-change="goodsStateChange"></goodsItemVue>
  10. <script>
  11. ......
  12. /**
  13. * 商品选中状态改变
  14. */
  15. goodsStateChange(event){
  16. this.cartList.some(item=>{
  17. if(item.id == event.id){
  18. item.goods_state = event.state;
  19. // some可以结束循环
  20. return true;
  21. }
  22. });
  23. },
  24. ......
  25. </script>

5.7 底部组件

  1. <template>
  2. <div class="footer-container flex justify-content-between align-item-center">
  3. <!-- 左边 -->
  4. <div class="left-box flex align-item-center">
  5. <!-- 多选框 -->
  6. <div
  7. class="checkBox flex flex-center flex-noshrink"
  8. :class="{'checkBox-checked':state}">
  9. </div>
  10. <span>全选</span>
  11. </div>
  12. <!-- 右边 -->
  13. <div class="right-box flex align-item-center">
  14. <span class="total">合计:¥{{ totalPrice }}</span>
  15. <!-- 结算按钮 -->
  16. <div class="settlement flex flex-center">下单</div>
  17. </div>
  18. </div>
  19. </template>
  20. <script>
  21. export default {
  22. props:{
  23. state:{
  24. type:Boolean,
  25. default:false
  26. },
  27. totalPrice:{
  28. type:Number,
  29. default:0
  30. }
  31. }
  32. }
  33. </script>
  34. <style lang="scss" scoped>
  35. // 右边
  36. .right-box{
  37. height: 100%;
  38. .total{
  39. color: #7e74b1;
  40. font-weight: bold;
  41. font-size: 18px;
  42. margin-right: 6px;
  43. }
  44. .settlement{
  45. background-color: #7e74b1;
  46. color: #fff;
  47. font-size: 18px;
  48. font-weight: bold;
  49. width: 110px;
  50. height: 100%;
  51. }
  52. }
  53. // 左边
  54. .left-box{
  55. padding-left:20px;
  56. span{
  57. color: #7e74b1;
  58. font-size: 16px;
  59. font-weight: bold;
  60. }
  61. // 多选框
  62. .checkBox{
  63. width:26px;
  64. height:26px;
  65. border-radius: 100px;
  66. background-color: #7e74b1;
  67. margin-right: 4px;
  68. }
  69. // 多选框被选中的样式
  70. .checkBox-checked::after{
  71. content:'';
  72. display: table;
  73. width: 16px;
  74. height:16px;
  75. border-radius: 100px;
  76. background-color: rgb(212, 240, 89);
  77. }
  78. }
  79. .footer-container{
  80. position:fixed;
  81. bottom: 0px;
  82. left: 0px;
  83. z-index: 100;
  84. width: 100vw;
  85. height: 60px;
  86. border-top: 1px solid rgba($color: #000000, $alpha: 0.3);
  87. border-bottom: 1px solid rgba($color: #000000, $alpha: 0.3);
  88. background-color: #fff;
  89. }
  90. .flex{
  91. display: flex;
  92. }
  93. .flex-noshrink{
  94. flex-shrink: 0;
  95. }
  96. .flex-center{
  97. justify-content: center;
  98. align-items: center;
  99. }
  100. .flex-column{
  101. flex-direction: column;
  102. }
  103. .justify-content-between{
  104. justify-content: space-between;
  105. }
  106. .align-item-center{
  107. align-items: center;
  108. }
  109. </style>

在App.vue中使用。

5.8 计算全选的状态

全选的状态取决于每个商品是否都被选中,所以我们要有一个计算属性去判断是否都被选中。

  1. <!-- 底部 -->
  2. <footerCom :state="fullState"></footerCom>
  1. computed:{
  2. fullState(){
  3. return this.cartList.every(item=>item.goods_state);
  4. // 上面的写法就相当于下面的写法
  5. return this.cartList.every(item=>{
  6. return item.goods_state === true;
  7. });
  8. }
  9. },

5.9 实现全选

  1. <template>
  2. ...
  3. <div class="left-box flex align-item-center" @click="checkAll">
  4. <!-- 多选框 -->
  5. <div
  6. class="checkBox flex flex-center flex-noshrink"
  7. :class="{'checkBox-checked':state}">
  8. </div>
  9. <span>全选</span>
  10. </div>
  11. ...
  12. </template>
  13. <script>
  14. export default {
  15. ...
  16. methods:{
  17. checkAll(){
  18. this.$emit('check-all',{state:!this.state});
  19. }
  20. }
  21. }
  22. </script>
  1. // 给组件添加事件的HTML代码就不放出来了
  2. /**
  3. * 全选按钮被点击
  4. * @param {Object} event state选中状态
  5. */
  6. checkAllClick(event){
  7. this.cartList.forEach(item=>{
  8. item.goods_state = event.state;
  9. });
  10. },

5.10 选中的数量和总价

  1. <!-- 左边 -->
  2. <div class="left-box flex align-item-center" @click="checkAll">
  3. <!-- 多选框 -->
  4. <div
  5. class="checkBox flex flex-center flex-noshrink"
  6. :class="{'checkBox-checked':state}">
  7. </div>
  8. <span>已选({{ checkCount }})</span>
  9. </div>
  10. // 将全选修改为 已选(选中个数)
  11. // 在props中传入
  12. checkCount:{
  13. type:Number,
  14. default:0
  15. }

App.vue中计算并传递

  1. <!-- 底部 -->
  2. <footerCom
  3. :state="fullState"
  4. :checkCount="checkCount"
  5. :totalPrice="totalPrice"
  6. @check-all="checkAllClick"></footerCom>
  1. // checkCount和totalPrice都是计算属性
  2. checkCount(){
  3. return this.cartList.reduce((count,item)=>{
  4. if(item.goods_state){
  5. return count+1;
  6. }else{
  7. return count;
  8. }
  9. },0);
  10. },
  11. totalPrice(){
  12. return this.cartList.reduce((price,item)=>{
  13. if(item.goods_state){
  14. return price + item.goods_price;
  15. }else{
  16. return price;
  17. }
  18. },0);
  19. }

5.11 实现数量变化

  1. <div class="goodsInfo-count-sub flex flex-center" @click="subtractClick">-</div>
  2. <div class="goodsInfo-count-num flex flex-center">{{ count }}</div>
  3. <div class="goodsInfo-count-plus flex flex-center" @click="plusClick">+</div>
  4. <script>
  5. export default {
  6. props:{
  7. ......
  8. count:{
  9. type:Number,
  10. default:0
  11. }
  12. },
  13. methods:{
  14. /**
  15. * 减号被点击
  16. */
  17. subtractClick(){
  18. if(this.count <= 1) return;
  19. const data = {id:this.id,count:this.count};
  20. this.$emit('subtract-click',data);
  21. },
  22. /**
  23. * 加号点击
  24. */
  25. plusClick(){
  26. if(this.count >= 10) return;
  27. const data = {id:this.id,count:this.count};
  28. this.$emit('plus-click',data);
  29. },
  30. ........
  31. }
  32. }
  33. </script>
  1. // App.vue
  2. /**
  3. * 加号
  4. */
  5. plusClick(event){
  6. this.cartList.some(item=>{
  7. if(item.id == event.id){
  8. item.goods_count += 1;
  9. return true;
  10. }
  11. });
  12. },
  13. /**
  14. * 减号
  15. */
  16. subtractClick(event){
  17. this.cartList.some(item=>{
  18. if(item.id == event.id){
  19. item.goods_count -= 1;
  20. return true;
  21. }
  22. });
  23. },

一般数量的选择这种我们也会抽取成一个组件,此时就出现了两层嵌套,App嵌套goodsItem,goodsItem嵌套计数器。如果是父组件传递数据可以一层一层往里穿。如果计数器想通知App这个组件可以使用EventBus直接通知,而不需要一层层往上传递。        

 5.12 改变数量时同步更新价格和选中数量

  1. // 修改计算总量和总价格的计算属性即可
  2. checkCount(){
  3. return this.cartList.reduce((count,item)=>{
  4. if(item.goods_state){
  5. return count+item.goods_count;
  6. }else{
  7. return count;
  8. }
  9. },0);
  10. },
  11. totalPrice(){
  12. return this.cartList.reduce((price,item)=>{
  13. if(item.goods_state){
  14. return price + (item.goods_price * item.goods_count);
  15. }else{
  16. return price;
  17. }
  18. },0);
  19. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/294711
推荐阅读
相关标签
  

闽ICP备14008679号