Vue基础篇之封装和网络请求的应用

本文介绍了在Vue项目中如何进行组件封装,详细探讨了less和sass的安装、使用,包括嵌套、运算、变量、混合和导入等高级特性。接着,通过实现一个简单的购物车功能,讲解了如何处理样式、数据操作、事件传递,包括商品的增删、数量调整及选中状态。最后,提到了过滤器和自定义指令的使用场景和定义方式。


1、封装

  • 1)在项目中导入 axiosnpm i axios
  • 2)对于封装我们一般在项目的 src 文件下 去创建一个 plugins
// axios 封装
import axios from 'axios'
import Vue from 'vue'

const instance = axios.create({
  // 请求基地址
  baseURL: "http://localhost:3008/",
  timeout: 2000 // 控制时间 2000 = 2s,
  // 请求头的配置
  // headers
})

// 请求拦截器
// 例:提示开会员
instance.interceptors.request.use((config) => {
  // 请求头配置 config
  // config.auth = 'token'
  return config
})

// 响应拦截器
instance.interceptors.response.use((res) => {
  return res.data
})

// vue 全局加上 $http
// 在组件中使用 this.$http 就相当于 instance 了
Vue.prototype.$http = instance
  • 3)封装好了后,我们在父组件中去使用
<script>
export default {
  async created() {
   const res = await this.$http.get('/cart');
   console.log(res);
  }
}
</script>

并且在 main.js 中引入

import Vue from 'vue'
import App from './App.vue'
import './plugins/http' //引入 plugins 文件下的 http.js

Vue.config.productionTip = false

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

2、 Vue 中的样式处理

  • 1)可以直接在 style 内添加 scoped 将组件样式变成私有化
<template>
  <div class="cart-item">
  ...
    <button class="del">删除</button>
  </div>
</template>

<script>
export default {

}
</script>

<style scoped>
/* 样式的处理 */
/* 1.直接在 style 内添加 scoped 将组件样式变成私有化 */
/* scoprd 作用是给所在组件内的的标签 添加一个相同,但又与其他组件不同的属性  data-v-xxxxx */
/* 所有样式都会使用这个属性选择器来书写 */

.del {
  color: red;
}
</style>

scoprd 作用是给所在组件内的的标签 添加一个相同,但又与其他组件不同的属性 data-v-xxxxx
所有样式都会使用这个属性选择器来书写

在这里插入图片描述

============================================================================

2.1高级 CSS 语法

css 预处理 css 控制语言 高级的 css 语法
因为:浏览器不支持高级的 css 语法解决方法安装相应的工具包

less 介绍以及安装

  • Less (Leaner Style Sheets 的缩写) 是一门向后兼容的 CSS 扩展语言。
  • 步骤一:首先需要安装对应的包 npm i less less-loader -D
  • 安装好后在:项目的 package.json查看对应的版本号
    在这里插入图片描述
  • 导入 less 语法
  • <style scoped lang='less'>...</style>

less 的使用方法

- 1)嵌套(Nesting)
  • Less 提供了使用嵌套(nesting)代替层叠或与层叠结合使用的能力
  • 可以根据结构将 .del.cart-item .count input 嵌套 到 .cart-item
  • 滑过效果的写法:&:hover{ colc:..} & 表示自己

老写法

<style scoped lang='less'>
.cart-item{
  display: flex;
  justify-content: space-between;
  padding: 20px 0;
  border-top: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
  align-items: flex-start;
}

.del {
  color: red;
}

.del:hover {
  color: aqua;
}

.cart-item .count input{
  width: 40px;
}


</style>

新写法

<style scoped lang='less'>.cart-item{
  display: flex;
  justify-content: space-between;
  padding: 20px 0;
  border-top: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
  align-items: flex-start;
  .count {
  /* 在 input 前面可以写 + 号,> 号*/
    input {
      width: 40px;
    }
  }

  .del {
    color: red;
    &:hover{
     color: aqua;
  }
}
</style>

============================================================================

- 2)运算(Operations)
  • 在css中可以使用算术运算符 +、-、*、/ 可以对任何数字、颜色或变量进行运算。
<style scoped lang='less'>
.cart-item{
  display: flex;
  justify-content: space-between;
  padding: 20px 0;
  border-top: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
  align-items: flex-start;
  .count {
    input {
      width: 30px +10 px;  /* 宽度结果为40px */
    }
  }

  .del {
    color: red;
    &:hover{
       color: aqua;
    }
  }
}
</style>在这里插入代码片

============================================================================

- 3)变量(Variables)
@width: 10px;
@height: @width + 10px;

.header {
  width: @width;
  height: @height;
}

编译结果是;

.header {
  width: 10px;
  height: 20px;
}

============================================================================

- 4)混合(Mixins)
  • 混合(Mixin)是一种将一组属性从一个规则集包含(或混入)到另一个规则集的方法
// 先定义
.border-top-bottom {
  border-top: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
}


.cart-item{
        ...
   /* 使用  */   
  .border-top-bottom ();
        ...
}

============================================================================

- 5)导入(Importing)
  • 在assets创建一个 style.less 文件
  • 导入 style.less 文件 @import '../assets/style.less';
  • 使用 语法:@定义名
@width:30px;
导入:@import '../assets/style.less';
使用:width: @width + 10px;

sass 介绍以及安装

  • sass基于Ruby语言开发而成,因此安装sass前需要安装Ruby。
  • 步骤一:正常安装 sass 大概率会失败:
  • 首先先执行:npm config set sass_binary_site https://npm.taobao.org/mirrors/node-sass/
  • 然后安装对应的包 npm i node-sass sass-loader -D
  • 安装好后在:项目的 package.json查看对应的版本号
    在这里插入图片描述
  • 导入 sass 语法
  • <<style lang="scss">...</style>

sass 的使用方法

- 1)嵌套(Nesting)
  • css中重复写选择器是非常恼人的。如果要写一大串指向页面中同一块的样式时,往往需要 一遍又一遍地写同一个ID

老写法

<style lang="scss">
.cart{
  width: 600px;
  margin: 0 auto;
}
.cart-header .form-head{
  display: flex;
  justify-content: space-between;
}

.cart .cart-footer {
  display: flex;
  justify-content: space-between;
}
</style>

新写法

<style lang="scss">
.cart {
  width: 600px;
  margin: 0 auto;
  .cart-header {
    .form-head {
      display: flex;
      justify-content: space-between;
    }
  }
  .cart-footer {
    display: flex;
    justify-content: space-between;
  }
}
</style>

============================================================================

- 2)运算(Operations),导入(Importing) 与 less 基本相同

============================================================================

- 3)变量(Variables)
<style lang="scss">
$width:600px;

.cart {
  width: $width;
  margin: 0 auto;
  .cart-header {
    .form-head {
      display: flex;
      justify-content: space-between;
    }
  }
  .cart-footer {
    display: flex;
    justify-content: space-between;
  }
}
</style>

编译结果是;

.cart {
  width: 600px;
  margin: 0 auto;
  .cart-header {
    .form-head {
      display: flex;
      justify-content: space-between;
    }
  }
  .cart-footer {
    display: flex;
    justify-content: space-between;
  }
}

============================================================================

- 4)混合(Mixins)
  • 混合器使用@mixin标识符定义
  • 通过@include来使用这个混合器
// 定义
@mixin df {
  display: flex;
}
.cart {
  width: $width;
  margin: 0 auto;
  .cart-header {
    .form-head {
      @include df;  // 定义 使用
      justify-content: space-between;
    }
  }
  .cart-footer {
    @include df;
    justify-content: space-between;
  }
}

3、实现简单购物车的制作

  • 基础结构
    在这里插入图片描述

父组件

<template>
  <div class="cart">
    <div class="cart-header">
      <h3>全部商品</h3>
      <div class="form-head">
        <div>
          <input type="checkbox" name="" id="all" />
          <label for="all">全选</label>
        </div>
        <span>商品</span>
        <span>单价</span>
        <span>数量</span>
        <span>小计</span>
        <span>操作</span>
      </div>
    </div>
    <CartItem />
    <div class="cart-footer">
      <div>
        <input type="checkbox" name="" id="all1" />
        <label for="all1">全选</label>
      </div>
      <span>删除选中的商品</span>
      <span>已选商品 200 件</span>
      <span>总价</span>
    </div>
  </div>
</template>

<script>
import CartItem from "./CartItem.vue";
export default {
  components: { CartItem },
};
</script>

<style lang="scss">
$width:600px;
@mixin df {
  display: flex;
}
.cart {
  width: $width;
  margin: 0 auto;
  .cart-header {
    .form-head {
      @include df;
      justify-content: space-between;
    }
  }
  .cart-footer {
    @include df;
    justify-content: space-between;
  }
}
</style>

子组件

<template>
  <div class="cart-item">
    <input type="checkbox" name="" id="">
    <img src="https://img30.360buyimg.com/n0/s80x80_jfs/t1/132022/23/12216/60913/5f86195bEacd08599/c5dc348d3f943324.jpg.dpg" alt="">
    <span>标题</span>
    <span>$100</span>
    <div class="count">
      <button>-</button>
      <input type="text">
      <button>+</button>
    </div>
    <span>$100</span>
    <button class="del">删除</button>
  </div>
</template>

<script>
export default {

}
</script>

<style scoped lang='less'>
@import '../assets/style.less';
@color:red;
/* 样式的处理 */
/* 1.直接在 style 内添加 scoped 将组件样式变成私有化 */
/* scoprd 作用是给所在组件内的的标签 添加一个相同,但又与其他组件不同的属性  data-v-xxxxx */
/* 所有样式都会使用这个属性选择器来书写 */
/* css 预处理  css 控制语言  高级的 css 语法
  因为:浏览器不支持高级的 css 语法解决方法安装相应的工具包
 - 1)less 
   - 步骤一:首先需要安装对应的包 `npm i less less-loader -D` */
// 语法
// a. 嵌套
// b. 运算
// c. 变量

.border-top-bottom {
  border-top: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
}

.cart-item{
  display: flex;
  justify-content: space-between;
  padding: 20px 0;
  align-items: flex-start;
  .border-top-bottom();
  .count {
    input {
      width: @width + 10px;
      // width: 30px 10 px;
    }
  }

  .del {
    color: @color;
    &:hover{
       color: aqua;
    }
  }
}
</style>

3.1 功能的实现与应用

- 1)创建空数组并且发送请求拿数据
<script>
import CartItem from "./CartItem.vue";
export default {
  data(){
    return {
      carts: [] //创建空数组
    }
  },
  components: { CartItem },
  async mounted(){
    this.carts = await this.$http.get('/cart'), //发送请求拿数据
  }
};
</script>

在这里插入图片描述


- 2)使用 v-for循环展示获取到的内容
<CartItem v-for="item in carts" :key="item.id"/>

注意:当子组件需要父组件对象内的所有数据来展示的时候一个一个的传递太麻烦了 我们可以使用

  • 1.直接将对象传递过去,需要什么内容直接用 info.xxx提取数据

父组件:

 <CartItem v-for="item in carts" :key="item.id"  :info="item" />

子组件:

<template>
  <div class="cart-item">
    <input type="checkbox" name="" id="" :checked="info.checked">
    <img :src="info.imgSrc" alt="">
    <span>{{info.title}}</span>
    <span>${{info.price}}</span>
    <div class="count">
      <button>-</button>
      <input type="text" :value="info.count">
      <button>+</button>
    </div>
    <span>${{info.price * info.count}}</span>
    <button class="del">删除</button>
  </div>
</template>

<script>
export default {
  props: ['info'] // 接收传递过来的 info
}
</script>
  • 2.将对象的属性全部传递过去 可以利用 v-bind 简化程度

父组件

<CartItem v-for="item in carts" :key="item.id"  v-bind="item" />

子组件

<template>
  <div class="cart-item">
    <input type="checkbox" name="" id="" :checked="checked">
    <img :src="imgSrc" alt="">
    <span>{{title}}</span>
    <span>${{price}}</span>
    <div class="count">
      <button>-</button>
      <input type="text" :value="count">
      <button>+</button>
    </div>
    <span>${{price * count}}</span>
    <button class="del">删除</button>
  </div>
</template>

<script>
export default {
  props: ['id','imgSrc','title','price','checked','count'] 
}
</script>

在上代码中如果文字过多,会显得太难看,这时候我们可以使用多行文本溢出省略号

<template>
  <div class="cart-item">
  ......
  // 给文字地方添加css 元素
    <span class="title">{{title}}</span>
  ......
</template>
......
<style scoped lang='less'>
......
.cart-item{
......
  .title {
    width: 80px; 
    // 老版弹性盒子
    display: -webkit-box;
    // 元素 垂直排列
    -webkit-box-orient: vertical;
    // 控制显示的行数
    -webkit-line-clamp: 3;
    // 溢出隐藏
    overflow: hidden;
    text-overflow: clip;
  }
......
</style>

完成什么代码后得到的样式展示为
在这里插入图片描述

3.2 利用请求方式实现购物车功能

- 1)实现删除功能

父组件中定义 删除方法
使用:delete进行操作

  • 先删除数据库内的,数据库内的删除成功了 ,然后在修改本地的
<script>
。。。。。
  methods:{
    // 删除操作
    async del(id){
      // 先删除数据库内的,数据库内的删除成功了 ,然后在修改本地的
      await this.$http.delete('/cart/' + id)
    }
  }
};
</script>

将删除方法传递到 子组件中

<template>
   <div>
   <CartItem v-for="item in carts" :key="item.id"  v-bind="item" @del="del" />
   </div>
</template>

子组件接收传递过来的功能

<template>
  <div class="cart-item">
.......
    <button @click="del" class="del">删除</button>
  </div>
</template>

<script>
export default {
  props: ['id','imgSrc','title','price','checked','count'],
  methods:{
    del(){
      this.$emit('del',this.id)
    }
  }
}
</script>

完成以上步骤 后端的数据以及删除成功,但前端页面并没有删除
如何删除前端页面的:在父组件中定义方法

<script>
。。。。。
  methods:{
    // 删除操作
    async del(id){
      // 先删除数据库内的,数据库内的删除成功了 ,然后在修改本地的
      await this.$http.delete('/cart/' + id)
      // 删除前端页面显示的
      this.carts = this.carts.filter(item => item.id !== id)
    }
  }
};
</script>

在这里插入图片描述

- 2)对数量实现加减操作

父组件中定义 加减 功能
使用 patch 方法

<script>
。。。。
  methods:{
。。。。
    // 数量的添加
    async add(id){
      // 获取原来的数据是对象
      const currentCartItem = this.carts.find(item => item.id === id )
      //定义方法
      await this.$http.patch(`/cart/${id}`,{count: currentCartItem.count + 1 } )
      currentCartItem.count++
    }
  }
};
</script>

将添加方法传递到 子组件中

<template>
   <div>
   <CartItem v-for="item in carts" :key="item.id"  v-bind="item" @del="del" @add="add" />   </div>
</template>

子组件接收传递过来的功能

<template>
  <div class="cart-item">
.......
      <button @click="add">+</button>
    </div>
  </div>
</template>

<script>
export default {
  props: ['id','imgSrc','title','price','checked','count'],
  methods:{
.......
    add(){
      this.$emit('add',this.id)
    }
  }
}
</script>

在这里插入图片描述

错误产生原因:当出现小数点的时候,数据运算会产生错误。
解决方法一:定义一个同一的函数,使用toFixed去解决,保留两位小数

<template>
....
    <span>${{formatNum(price * count)}}</span>
    .....
  </div>
</template>

<script>
export default {
  props: ['id','imgSrc','title','price','checked','count'],
  methods:{
  .....
    formatNum(num){
      return num.toFixed(2)//保留两位小数
    }
  }
}
</script>

在这里插入图片描述

解决方法二:使用过滤器来解决

<template>
。。。
    <!-- 过滤器的用法 -->
    <!-- {{ 参数1 | 过滤器(参数2,3,4)}} -->
    <span>${{ (price * count) | formatNum}}</span>
  </div>
</template>
<script>
export default {
。。。。
  filters: {
    formatNum(num) {
      return num.toFixed(2);
    },
  }
}
</script>

如果过滤器想要如何地方都可以使用我们可以设置成全局过滤器
在这里插入图片描述

数量的减

<script>
。。。。
  methods:{
。。。。
    // 数量的减
    async sub(id){
      // 获取原来的数据是对象
      const currentCartItem = this.carts.find(item => item.id === id )
      //定义方法
      await this.$http.patch(`/cart/${id}`,{count: currentCartItem.count - 1 } )
      currentCartItem.count--
    }
  }
};
</script>

将添加方法传递到 子组件中

<template>
   <div>
   <CartItem 。。。 。@sub="sub"/>  </div>
</template>

子组件接收传递过来的功能
当 数量为 1 我们应该禁止 减号使用::disabled="count === 1

<template>
  <div class="cart-item">
.......
      <button @click="sub" :disabled="count === 1>-</button>
    </div>
  </div>
</template>

<script>
export default {
  props: ['id','imgSrc','title','price','checked','count'],
  methods:{
.......
    sub(){
      this.$emit('sub',this.id)
    }
  }
}
</script>

在这里插入图片描述

- 3)复选框的选中与不选中

父组件中定义 选择也不选择的修改 功能
使用 patch 方法

<script>
。。。。
  methods:{
。。。。
    / // 复选框的选中与不选中
    async changeCheckedStatus(id){
      // 获取原来的数据是对象
      const currentCartItem = this.carts.find(item => item.id === id )
      //定义方法
      await this.$http.patch(`/cart/${id}`,{checked: !currentCartItem.checked } )
      currentCartItem.checked = !currentCartItem.checked
    },
  }
};
</script>

将添加方法传递到 子组件中

<template>
   <div
    <CartItem
    。。。。。。
      @changeCheckedStatus="changeCheckedStatus"
    />  </div>
</template>

子组件接收传递过来的功能

<template>
 <div class="cart-item">
    <input type="checkbox" ....  @change="$emit('changeCheckedStatus',id)">
  </div>
</template>
</script>

当进行加与减的操作的时候都是要点击这个商品,从侧面反映了我们选中了他
这时需要在 加减的接收处进行一个判断

<script>
。。。。
    add(){
      this.$emit('add',this.id);
      // 当进行加与减的操作的时候都是要点击这个商品,从侧面反映了我们选中了他
      if(!this.checked){
        this.$emit('changeCheckedStatus', this.id)
      }
    },
    sub(){
      this.$emit('sub',this.id);
       if(!this.checked){
        this.$emit('c hangeCheckedStatus', this.id)
      }
</script>

- 4)以下功能

在这里插入图片描述

  • 使用计算属性 computed
    • res.count 老的
    • item.count 新的
。。。。。。
 <span>已选商品 {{totalInfo.count}}件</span>
<span>总价$ {{totalInfo.total | formatNum}}</span>
。。。。。。
<script>
export default {
。。。。
    computed: {
    // 商品个数
    productNum(){
       return this.cart.length
    },
    // 商品件数和总价 {count:30 , total: 500}
    totalInfo(){
      return this.carts.reduce(
        (res, item) => ({
          count: res.count + item.count,
          total: res.total + item.price * item.count,
        }),
        { count:0, total: 0 }
      )
</script>

在这里插入图片描述

  • 删除已选中的商品

绑定 @click 方法

<template>
。。。。。
          <input type="checkbox" name="" id="all" v-model="isAllChecked" />
          <label for="all">全选</label>
。。。。。
        <input type="checkbox" name="" id="all1" v-model="isAllChecked"  />
        <label for="all1">全选</label>
      </div>
      <span @click="delAllChecked">删除选中的商品</span>
。。。。。。。
</template>

<script>
。。。。。。。
    async delAllChecked (){
      const res = this.filter((item) => item.checked) // 先拿到已选择的数据
      await Promise.all(res.map(item => this.$http.delete('/cart/'+ item.id)))
      // res.map(item => this.$http.delete('/cart/'+ item.id))
      // 会解析成 [res.map(item => this.$http.delete('/cart/'+ item.id),
      // res.map(item => this.$http.delete('/cart/'+ item.id))]数组的形式
      this.carts = this.carts.filter((item) => !item.checked)// 得到新的
    }
  },
};
</script>

4、代码

  • CartDemo.vue
<template>
  <div class="cart">
    <div class="cart-header">
      <h3>全部商品{{productNum}}</h3>
      <div class="form-head">
        <div>
          <input type="checkbox" name="" id="all" v-model="isAllChecked" />
          <label for="all">全选</label>
        </div>
        <span>商品</span>
        <span>单价</span>
        <span>数量</span>
        <span>小计</span>
        <span>操作</span>
      </div>
    </div>
    <CartItem
      v-for="item in carts"
      :key="item.id"
      v-bind="item"
      @del="del"
      @add="add"
      @sub="sub"
      @changeCheckedStatus="changeCheckedStatus"
    />
    <div class="cart-footer">
      <div>
        <input type="checkbox" name="" id="all1" v-model="isAllChecked"  />
        <label for="all1">全选</label>
      </div>
      <button @click="delAllChecked">删除选中的商品</button >
      <span>已选商品 {{totalInfo.count}}件</span>
      <span>总价$ {{totalInfo.total | formatNum}}</span>
    </div>
  </div>
</template>

<script>
import CartItem from "./CartItem.vue";
export default {
  data() {
    return {
      carts: [], //创建空数组
    };
  },
  components: { CartItem },
    computed: {
    // 商品个数
    productNum(){
       return this.carts.length
    },
    // 商品件数和总价 {count:30 , total: 500}
    totalInfo(){
      return this.carts.reduce(
        (res, item) => ({
          count: res.count + item.count,
          total: res.total + item.price * item.count,
        }),
        { count:0, total: 0 }
      )
    },
    isAllChecked:{
      get(){
        return this.carts.every(item => item.checked)
      },
      set(val){
        this.carts.forEach(item => {
          item.checked = val
        });
      }
    }

  },
  async mounted() {
    this.carts = await this.$http.get("/cart"); //发送请求拿数据
  },
  methods: {
    // 删除操作
    async del(id) {
      // 先删除数据库内的,数据库内的删除成功了 ,然后在修改本地的
      await this.$http.delete("/cart/" + id);
      // 删除前端页面显示的
      this.carts = this.carts.filter((item) => item.id !== id);
    },
    // 数量的添加
    async add(id) {
      // 获取原来的数据是对象
      const currentCartItem = this.carts.find((item) => item.id === id);
      //定义方法
      await this.$http.patch(`/cart/${id}`, {
        count: currentCartItem.count + 1,
      });
      currentCartItem.count++;
      console.log(0.1 + 0.2 === 0.3);
    },
    // 数量的添减
    async sub(id) {
      // 获取原来的数据是对象
      const currentCartItem = this.carts.find((item) => item.id === id);
      //定义方法
      await this.$http.patch(`/cart/${id}`, {
        count: currentCartItem.count - 1,
      });
      currentCartItem.count--;
    },
    // 复选框的选中与不选中
    async changeCheckedStatus(id) {
      // 获取原来的数据是对象
      const currentCartItem = this.carts.find((item) => item.id === id);
      //定义方法
      await this.$http.patch(`/cart/${id}`, {
        checked: !currentCartItem.checked,
      });
      currentCartItem.checked = !currentCartItem.checked;
    },
    async delAllChecked (){
      const res = this.filter((item) => item.checked) // 先拿到已选择的数据
      await Promise.all(res.map(item => this.$http.delete('/cart/'+ item.id)))
      // res.map(item => this.$http.delete('/cart/'+ item.id))
      // 会解析成 [res.map(item => this.$http.delete('/cart/'+ item.id),res.map(item => this.$http.delete('/cart/'+ item.id))]数组的形式
      this.carts = this.carts.filter((item) => !item.checked)// 得到新的
    }
  },
};
</script>

<style lang="scss">
$width: 600px;
@mixin df {
  display: flex;
}
.cart {
  width: $width;
  margin: 0 auto;
  .cart-header {
    .form-head {
      @include df;
      justify-content: space-between;
    }
  }
  .cart-footer {
    @include df;
    justify-content: space-between;
  }
}
</style>
  • CartItem.vue
<template>
  <div class="cart-item">
    <input type="checkbox" name="" id="" :checked="checked"  @change="$emit('changeCheckedStatus',id)">
    <img :src="imgSrc" alt="">
    <span class="title">{{title}}</span>
    <span>${{price}}</span>
    <div class="count">
      <button @click="sub" :disabled="count === 1 ">-</button>
      <input type="text" :value="count">
      <button @click="add">+</button>
    </div>
    <!-- 过滤器的用法 -->
    <!-- {{ 参数1 | 过滤器(参数2,3,4)}} -->
    <span>${{ (price * count) | formatNum}}</span>
    <button @click="del" class="del">删除</button>
  </div>
</template>

<script>
export default {
  props: ['id','imgSrc','title','price','checked','count'],
  methods:{
    del(){
      this.$emit('del',this.id)
    },
    add(){
      this.$emit('add',this.id);
      // 当进行加与减的操作的时候都是要点击这个商品,从侧面反映了我们选中了他
      if(!this.checked){
        this.$emit('changeCheckedStatus', this.id)
      }
    },
    sub(){
      this.$emit('sub',this.id);
       if(!this.checked){
        this.$emit('c hangeCheckedStatus', this.id)
      }
    }
  }
  // 过滤器
  // filters: {
  //   formatNum(num) {
  //     return num.toFixed(2);
  //   },
  // }
}
</script>

<style scoped lang='less'>
@import '../assets/style.less';
@color:red;
/* 样式的处理 */
/* 1.直接在 style 内添加 scoped 将组件样式变成私有化 */
/* scoprd 作用是给所在组件内的的标签 添加一个相同,但又与其他组件不同的属性  data-v-xxxxx */
/* 所有样式都会使用这个属性选择器来书写 */
/* css 预处理  css 控制语言  高级的 css 语法
  因为:浏览器不支持高级的 css 语法解决方法安装相应的工具包
 - 1)less 
   - 步骤一:首先需要安装对应的包 `npm i less less-loader -D` */
// 语法
// a. 嵌套
// b. 运算
// c. 变量

.border-top-bottom {
  border-top: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
}

.cart-item{
  display: flex;
  justify-content: space-between;
  padding: 20px 0;
  align-items: flex-start;
  .border-top-bottom();
  .title {
    width: 80px; 
    // 老版弹性盒子
    display: -webkit-box;
    // 元素 垂直排列
    -webkit-box-orient: vertical;
    // 控制显示的行数
    -webkit-line-clamp: 3;
    // 溢出隐藏
    overflow: hidden;
    text-overflow: clip;
  }
  .count {
    input {
      width: @width + 10px;
      // width: 30px 10 px;
    }
  }

  .del {
    color: @color;
    &:hover{
       color: aqua;
    }
  }
}
</style>
  • App.vue
<template>
  <div>
    <CartDemo />
  </div>
</template>

<script>
import CartDemo from './components/CartDemo.vue';
export default {
  components: { CartDemo },
  async created() {
   const res = await this.$http.get('/cart');
   console.log(res);
  }
}
</script>

<style>

</style>
  • http.js
// axios 封装
import axios from 'axios'
import Vue from 'vue'

const instance = axios.create({
  // 请求基地址
  baseURL: "http://localhost:3008/",
  timeout: 2000 // 控制时间 2000 = 2s,
  // 请求头的配置
  // headers
})

// 请求拦截器
// 例:提示开会员
instance.interceptors.request.use((config) => {
  // 请求头配置 config
  // config.auth = 'token'
  return config
})

// 响应拦截器
instance.interceptors.response.use((res) => {
  return res.data
})

// vue 全局加上 $http
// 在组件中使用 this.$http 就相当于 instance 了
Vue.prototype.$http = instance
  • main.js
import Vue from 'vue'
import App from './App.vue'
import './plugins/http'
import './plugins/utils'
Vue.config.productionTip = false

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

  • data.json
{
  "cart": [
    {
      "id": "ygy6y7",
      "imgSrc": "https://img30.360buyimg.com/n0/s80x80_jfs/t1/132022/23/12216/60913/5f86195bEacd08599/c5dc348d3f943324.jpg.dpg",
      "title": "Apple iPhone 12 (A2404) 128GB 白色 支持移动联通",
      "price": 4899,
      "checked": false,
      "count": 1
    },
    {
      "id": "ygyte7",
      "imgSrc": "https://img20.360buyimg.com/n0/s80x80_jfs/t1/222320/23/22353/38203/6384d1a1Eebf9b463/9deecbd336d2a17a.jpg.dpg",
      "title": "Apple iPhone 13 (A2634) 256GB 粉色 支持移动联通电信5G 双卡双待手机",
      "price": 6090,
      "checked": true,
      "count": 1
    }
    ,
    {
      "id": "yqdrg7",
      "imgSrc": "https://img30.360buyimg.com/n0/s80x80_jfs/t1/179091/36/28087/35197/632873cbE8079c6b0/d6f86f1b1d5f112d.jpg.dpg",
      "title": "荣耀70 IMX800三主摄 双曲屏设计 高通骁龙778G Plus 66W快充 5G手机 8GB+256GB 墨玉青",
      "price": 2399,
      "checked": true,
      "count": 1
    }
  ]
}

5、总结

过滤器

什么时间可以考虑到过滤器

  • 数字的格式化 (保留两位小数)
  • 日期的格式化
  • 字符串的格式化

自定义指令

自定义指令我们可以写成局部与全局

  • 局部
<template>
  <div>
    <CartDemo />
    <!-- 让输入框出现的时候就获得交点 -->
    <!-- 有些对真实 dom 的操作 可以通过自定义指令来实现 -->
    <input v-focus type="text">
  </div>
</template>

<script>
import CartDemo from './components/CartDemo.vue';
export default {
  components: { CartDemo },
  async created() {
   const res = await this.$http.get('/cart');
   console.log(res);
  },
  directives: {
    focus: {
      // 当被绑定的元素插入到 DOM 中时 使用
      inserted(el){
        el.focus()
      }
    }
  }
}
</script>

<style>

</style>
  • 全局 需要写在:main.Js 但需要写在 new Vue之前
  • 定义好了后,在需要使用的定义直接 v-focus就可以使用
import Vue from 'vue'
import App from './App.vue'
import './plugins/http'
import './plugins/utils'
Vue.config.productionTip = false

Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时 使用
  inserted: function (el){
    // 聚交元素
    el.focus()
  }
})

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

-还可以写成 v-focus:a.b = 'c'
- a 指令参数
- b 指令修饰符可以写多个
- c 指令的值

  • 对于 v-focus:a.b = ‘c’`传递过来的我们接收方式可以用
    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值,例如:v-my-directive=“1 + 1” 中,绑定值为 2。
    • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
    • expression:字符串形式的指令表达式。例如 v-my-directive=“1 + 1” 中,表达式为 “1 + 1”。
    • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 “foo”。
    • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。

注意对于 inserted使用地方是上来就需要进行操作的,比如判断用户是否注册,弹窗的广告等等

 <input v-focus:x.a.b="false" type="text">

Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时 使用
  inserted: function (el,{value,arg,modifiers}){
    // 聚交元素
    console.log(value)
     console.log(arg)
      console.log(modifiers)
    el.focus()
  }
})

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fvcvv

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值