• VUE之旅—day1


    Vue概念

    Vue 是一个用于构建用户界面渐进式 框架

    1. 构建用户界面:基于数据动态渲染页面
    2. 渐进式:循序渐进的学习
    3. 框架:一谈完整的项目解决方案,提升开发效率

    Vue实例

    创建Vue实例,初始化渲染

    1. 准备容器(Vue所管理的范围)
    2. 引包(官网)------开发版本/生产版本
    3. 创建Vue实例 new Vue()
    4. 指定配置项 el data=>渲染数据
      • el 指定挂载点,选择器指定控制的是哪个盒子
      • data提供数据
    <body>
      <div id="app">
        
        {{ msg }}
      div>
      
      <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js">script>
      <script>
        // 一旦引入VueJS核心包,在全局环境,就有了Vue构造函数
        const app = new Vue({
          // 通过el配置选择器,指定Vue管理的是哪个盒子
          el: '#app',
          // 通过data提供数据
          data: {
            msg: '我是vue的第一个实例'
          }
        })
      script>
    body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    插值表达式

    利用表达式进行插值,将数据渲染页面中

    插值表达式的注意点:

    1. 使用的数据要存在
    2. 支持的是表达式,而非if…for语句
    3. 不能再标签属性里面使用

    响应式

    什么是响应式?

    数据改变,视图自动更新

    使用Vue开发---->更专注于业务核心逻辑

    如何访问或修改数据?

    data中的数据,最终会被添加到实例上

    1. 访问数据:“实例.属性名”
    2. 修改数据:“实例.属性名”=“值“

    安装vue工具

    Vue指令

    指令就是带有v-前缀的特殊属性,不同属性对应不同的功能。

    学习不同的指令——>解决不同业务场景需求

    v-html

    v-html="表达式"——>动态设置元素innerHTML

    <body>
      <div id="app">
        <div v-html="msg">div>
      div>
      <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js">script>
      <script>
        const app = new Vue({
          el: '#app',
          data: {
            msg: `CSDN官网 `
          }
        })
      script>
    body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    v-show
    1. 控制元素显示隐藏
    2. v-show="表达式",表达式值true显示,false隐藏
    3. 原理:切换display:none来控制显示隐藏
    4. 场景:频繁切换显示隐藏的场景
    v-if
    1. 控制元素的显示隐藏(条件渲染)
    2. 语法:v-if="表达式",表达式值true显示,false隐藏
    3. 原理:基于条件判断,是否创建或移除元素节点
    4. 场景:要么显示,要么隐藏,不频繁切换场景

    v-showv-if的区别

    • v-show底层原理:切换css的display:none来控制显示隐藏(通常称为简单的显示和隐藏)
    • v-if底层原理:根据判断条件控制元素的创建和移除(通常称为条件渲染)
    <body>
      <div id="app">
        <div v-show="flag" class="box">
          我是v-show控制的盒子
        div>
        <div v-if="flag" class="box">
          我是v-if控制的盒子
        div>
      div>
      <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js">script>
      <script>
        const app = new Vue({
          el: '#app',
          data: {
            flag: false
          }
        })
      script>
    body>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    v-else和v-else-if
    1. 辅助v-if进行判断 渲染
    2. 语法:v-else v-else-if="表达式"
    3. 注意:需要紧挨着v-if 一起使用
    <body>
      <div id="app">
        <h1 v-if="gender===1">性别:男h1>
        <h1 v-else>性别:女h1>
        <p v-if="score>=90">成绩评定Ap>
        <p v-else-if="score>=70&&score<90">成绩评定Bp>
        <p v-else-if="score>=60&&score<70">成绩评定Cp>
        <p v-else>成绩评定Dp>
      div>
      <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js">script>
      <script>
        const app = new Vue({
          el: "#app",
          data: {
            gender: 1,
            score: 89
          }
        })
      script>
    body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    v-on
    1. 注册事件=添加监听+提供处理逻辑
    2. 语法:
      • v-on:事件名=“内联语句"
      • v-on:事件名=“Methods”中的函数名
    3. 简写:@事件名
    4. methods函数内的this指向Vue实例
    <body>
      <div id="app">
        <button @click="count--">-button>
        <span>{{count}}span>
        <button @click="count++">+button>
        <hr>
        
        <button @click="fn">显示与隐藏button>
        <h1 v-show="flag">VUE实例h1>
      div>
      <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js">script>
      <script>
        const app = new Vue({
          el: "#app",
          data: {
            count: 100,
            flag: true
          },
          methods: {
            fn() {
              console.log(this === app);
              this.flag = !this.flag;
            }
          }
        })
      script>
    body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    v-bind
    1. 动态设置html的标签属性——>src url title…
    2. v-bind:属性名=“表达式”
    3. 简写形式 :属性名="表达式"
    <body>
      <div id="app">
        <img :src='imgUrl' :title='msg' alt="">
      div>
      <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js">script>
      <script>
        const app = new Vue({
          el: '#app',
          data: {
            imgUrl: './avator.jpg',
            msg: `我是一个女生`
          }
        })
      script>
    body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    v-for
    <body>
      <div id="app">
        <h1>小黑的书架h1>
        <ul>
          <li v-for="(item,index) in booksList" :key="item.id">
            <span>{{item.name}}span>
            <span>{{item.author}}span>
            <button @click="del(item.id)">删除button>
          li>
        ul>
      div>
      <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js">script>
      <script>
        const app = new Vue({
          el: '#app',
          data: {
            booksList: [
              { id: 1, name: '《红楼梦》', author: '曹雪芹' },
              { id: 2, name: '《西游记》', author: '吴承恩' },
              { id: 3, name: '《水浒传》', author: '施耐庵' },
              { id: 4, name: '《三国演义》', author: '罗贯中' }
            ]
          },
          methods: {
            del(id) {
              this.booksList = this.booksList.filter(item => item.id !== id)
            }
          }
        })
      script>
    body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    **:key="item.id"**的作用分析:

    给元素添加唯一的表示,便于Vu进行列表项的正确排序复用

    1. key 的值只能是字符串或数字类型
    2. key值必须有唯一性
    3. 推荐使用id作为key(唯一),不推荐使用index作为key(会变化,不对应)
    v-model
    1. 作用:给表单元素使用,双向数据绑定——>可以快速获取和设置表单元素内容
      • 数据变化——>视图自动更新
      • 视图变化——>数据自动更新
    2. 语法:v-model=‘变量’

    小黑记事本案例

    DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>小黑记事本title>
      <link rel="stylesheet" href="./diray.css">
    head>
    <body>
      <section id="app">
        <header class="header">
          <h1>小黑记事本h1>
          <div class="header-input">
            <input v-model="todoName" type="text" placeholder="请添加任务">
            <button @click="add()" id="addThing">添加任务button>
          div>
        header>
        <section class="main">
          <ul class="todo-list">
            <li class="todo" v-for="(item,index) in list" :key="item.id">
              <div class="view">
                <span>{{index+1}}.span>
                <span>{{item.thing}}span>
                <span class="del" @click="del(item.id)">Xspan>
              div>
            li>
    
          ul>
        section>
        <footer class="footer" v-show="list.length>0">
          <span class="todo-count">合计:<strong>{{list.length}}strong>span>
          <button class="clearAll" @click="clear()">
            清空任务
          button>
        footer>
      section>
      <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js">script>
      <script>
        document.addEventListener('keyup', function (e) {
          if (e.keyCode === 13) {
            app.add()
          }
        })
        const app = new Vue({
          el: '#app',
          data: {
            todoName: '',
            list: [
              { id: 1, thing: '跑步3km' },
              { id: 2, thing: '游泳3km' },
              { id: 3, thing: '跳绳200次' },
            ]
          },
          methods: {
            del(id) {
              this.list = this.list.filter(item => item.id !== id)
            },
            add() {
              if (this.todoName.trim() === '') {
                alert('请输入任务名称')
                return
              }
              this.list.unshift({
                id: +new Date(),
                thing: this.todoName
              })
              this.todoName = ''
            },
            clear() {
              this.list = []
            }
          }
        })
      script>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    #app {
      width: 500px;
      margin: 0 auto;
      color: #858080;
      background-color: #ffffff;
      padding: 10px 0;
      box-shadow: 0px 0px 5px #a3a1a1;
    }
    
    #app header h1 {
      text-align: center;
      font-size: 38px;
      font-weight: 100;
      color: #b85959;
      margin: 10px 0;
    }
    
    #app header .header-input {
      width: 410px;
      height: 40px;
      margin: 0 auto;
      padding: 20px 10px;
      position: relative;
    }
    
    #app header .header-input input {
      width: 400px;
      height: 40px;
      outline: none;
      font-size: 16px;
      border: none;
      border: 1px solid #be2727;
      border-radius: 8px;
      text-indent: 10px;
    }
    
    #app header .header-input button {
      width: 80px;
      height: 44px;
      position: absolute;
      right: 10px;
      top: 20px;
      border: none;
      color: #eee6e6;
      background-color: #d15959;
      border-top-right-radius: 8px;
      border-bottom-right-radius: 8px;
      cursor: pointer;
    }
    
    #app .main .todo-list {
      margin: 0;
      padding: 0;
      list-style: none;
      width: 430px;
      margin: 0 auto;
    }
    
    #app .main .todo {
      margin: 0 auto;
      height: 50px;
      width: 95%;
      line-height: 50px;
      border-bottom: 1px solid #c3bebe;
      position: relative;
    }
    
    #app .main .todo span {
      display: inline-block;
      margin: 0 12px;
      font-size: 18px;
    }
    
    #app .main .todo .del {
      position: absolute;
      right: 10px;
      cursor: pointer;
      font-weight: bolder;
      display: none;
    }
    
    #app .main .todo .del:hover {
      color: #c35c5c;
    }
    
    #app .main .todo:hover .del {
      display: inline-block;
    }
    
    #app footer {
      width: 410px;
      padding: 10px 10px;
      height: 30px;
      margin: 0 auto;
      font-size: 12px;
      display: flex;
      justify-content: space-between;
    }
    
    #app footer span {
      display: inline-block;
      height: 30px;
      line-height: 30px;
    }
    
    #app footer button {
      color: #858080;
      border: none;
      background-color: #ffffff;
      cursor: pointer;
    }
    
    #app footer button:hover {
      color: #a73535;
      text-decoration: underline;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117

    指令修饰符

    通过.指明一些指令后缀,不同后缀封装了不同的处理操作—>简化代码

    1. 按键修饰符

      @keyup.enter —>键盘回车监听

    2. v-model修饰符

      v-model.trim —>去除首尾空格

      v-model-number —>转数字

    3. 事件修饰符

      @事件名.stop —>阻止冒泡

      @事件名.prevent —>组织默认行为

    v-bind对于样式控制的增强

    操作class

    语法::class="对象/数组"

    1. 对象—>键就是类名,值是布尔值。如果值为true,有这个类,否则没有这个类

      <div class="box" :class="{类名1:布尔值,类名2:布尔值}">div>
      
      • 1
    2. 数组—>数组中所有的类,都会添加到盒子上,本质上就是一个class列表

      <div class="box" :class="[类名1,类名2,类名3]">div>
      
      • 1
    操作style

    语法::style="样式对象"

     <div class="box" :style="{css属性名1:css属性值1,css属性名2:css属性值2}">div>
    
    • 1

    v-model应用于其他表单元素

    常见的表单元素都可以用v-model绑定关联—>快速获取或设置表单的值,它会根据控件类型自动选取正确的方法来更新元素

    输入框 input:text —>value

    文本域 textarea —>value

    复选框 input:checkbox —>checked

    单选框 input:text —>checked

    下拉菜单 select —>value

    <body>
      <div id="app">
        <h1>小黑学习网h1>
    
        姓名:
        <input type="text" v-model="username">
        <br><br>
    
        是否单身:
        <input type="checked" v-model="IsSingle">
        <br><br>
    
        
        性别:
        <input type="radio" name="sex" v-model="gender" value="1"><input type="radio" name="sex" v-model="gender" value="2"><br><br>
    
        
        所在城市:
        <select v-model="city">
          <option value="101">北京option>
          <option value="102">上海option>
          <option value="103">成都option>
          <option value="104">南京option>
        select>
        <br><br>
    
        自我描述:
        <textarea v-model="desc">textarea>
        <br><br>
    
        <button>立即注册button>
      div>
      <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js">script>
      <script>
        const app = new Vue({
          el: '#app',
          data: {
            username: '小黑',
            IsSingle: true,
            gender: 2,
            city: 103,
            desc: '爱好文学'
          }
        })
      script>
    body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    计算属性

    **概念:**基于现有数据,计算出来的新属性。依赖的数据变化,自动重新计算。

    语法:

    1. 声明在computed配置项中,一个计算属性对应一个函数
    2. 使用起来和普通属性一样使用{{计算属性名}}

    计算属性—>可以将一段求值代码进行封装

    computed计算属性 vs methods方法

    computed计算属性

    作用:封装了一段对于数据的处理,求得一个结果。

    语法:

    1. 写在computed配置项中
    2. 作为属性,直接使用—>this.计算属性 {{计算属性}}

    methods方法

    作用:给实例提供一个方法,调用以处理业务逻辑

    语法:

    1. 写在methods配置项中
    2. 作为方法,需要调用—>this.方法名() {{方法名()}} @事件名=“方法名”

    computed计算属性有缓存特性(提升性能)

    计算属性会对计算出来的结果缓存,再次使用直接读取缓存,依赖变化了,会自动重新计算—>并再次缓存

    计算属性的完整写法

    计算属性默认的简写,只能读取访问,不能“修改”。

    如果要“修改”—>需要写计算属性的完整写法

    computed:{
    	计算属性名:{
    	get(){
    		一段代码逻辑(计算逻辑)
    		return 结果
    	},
    	set(修改的值){
    		一段代码逻辑(修改逻辑)
    	}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    watch监听器

    作用:监视数据变化

    语法:

    • 简单写法:简单类型数据,直接监视
     const app = new Vue({
          el: '#app',
          data: {
            words: '苹果',
            obj: {
              words: '苹果'
            }
          },
          watch: {
            words(newValue, oldValue) {
              // 一些业务逻辑 或者 异步操作
            },
            'obj.words'(newValue, oldValue) {
              // 一些业务逻辑 或者 异步操作
            }
          }
        })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 完整写法:添加额外配置项(深度监视复杂类型,立刻执行)
    watch: {
      数据属性名:{
           deep: true,//深度监视
              immediate: true,//立刻执行,已进入页面就立刻执行
              handler(newValue) {
                  console.log(newValue)
              }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    翻译小案例

    1. 接口传参
    2. 防抖优化
    DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>小小翻译器title>
      <style>
        #app {
          width: auto;
        }
    
        .translate {
          display: flex;
        }
    
        .text {
          width: 180px;
          height: 150px;
          font-size: 20px;
          border: 1px solid #838383;
          padding: 20px;
          color: #2f2e2e;
        }
    
        .text textarea {
          width: 95%;
          height: 95%;
          resize: none;
          outline: none;
          border: none;
          font-size: 20px;
    
        }
    
        #outText {
          border: none;
          background-color: #e6e6e6;
        }
      style>
    head>
    
    <body>
      <div id="app">
        <span>翻译成的语言:span>
        <select v-model="obj.lang">
          <option value="italy">意大利option>
          <option value="english">英语option>
          <option value="german">德语option>
        select>
        <div class="translate">
          <div class="text">
            <textarea type="text" placeholder="输入文本" v-model="obj.words" id="inText">textarea>
          div>
          <div class="text" id="outText">
            <div type="text" placeholder="翻译">{{ result }}div>
          div>
        div>
      div>
      <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js">script>
      <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js">script>
      <script>
        const app = new Vue({
          el: '#app',
          data: {
            result: '',
            // timer: null,
            //这个属性无需渲染,用来做防抖优化,可以直接需要使用时挂载到app对象身上
            obj: {
              lang: 'italy',
              words: ''
            }
          },
          watch: {
            obj: {
              deep: true,//深度监视
              immediate: false,//立刻执行,已进入页面就立刻执行
              handler(newValue) {
                //防抖优化
                clearTimeout(this.timer)
                // this.timer 直接将timer属性挂载到app对象身上
                this.timer = setTimeout(async () => {
                  const res = await axios({
                    //接口说明:
                    // 接口地址:https://applet-base-api-t.itheima.net/api/translate
                    // 接口是随机返回 一个字符串
                    // 请求方式:get
                    // 请求参数
                    // (1)words:需要被翻译的文本(必传)
                    // (1)lang:需要被翻译成的语言(可选)默认值 意大利
                    // 
                    url: 'https://applet-base-api-t.itheima.net/api/translate',
                    //请求方式get可以省略不写
                    params: newValue
                  })
                  this.result = res.data.data
                }, 900)
              }
            }
          }
        })
      script>
    body>
    
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105

    水果购物车案例

    需求说明:

    1. 渲染功能
    2. 删除功能
    3. 修改个数
    4. 全选反选
    5. 统计选中的总价和总数量
    6. 持久化到本地

    业务实现

    1. 渲染功能:v-if/v-show v-for :class
    2. 删除功能:点击传参 filter过滤覆盖原数组
    3. 修改个数:点击传参 find找对象
    4. 全选反选:计算属性computed 完整写法get/set
    5. 统计选中的总价和总数量:计算属性computed reduce条件求和
    6. 持久化到本地:watch监视,localStorage JSON.stringify JSON.parse
    DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>水果购物车title>
      <style>
        .app-container {
          width: 720px;
          height: auto;
          margin: 0 auto;
        }
    
        .banner {
          width: 100%;
          height: 100%;
        }
    
        .banner img {
          width: 100%;
          height: 100%;
        }
    
        .main .table {
          width: 100%;
          height: auto;
          border-collapse: collapse;
        }
    
        .empty {
          width: 100%;
          height: 100px;
          font-size: 24px;
          color: #575757;
          line-height: 100px;
          text-align: center;
          background-color: #f7f7f7;
    
        }
    
        .main .table .tr {
          height: 100%;
          display: flex;
          border: 1px solid #e8e7e7;
          justify-content: space-around;
          text-align: center;
        }
    
        .main .table .tbody .active {
          background-color: #f3f3f3;
        }
    
        .main .table .tr .th,
        .td {
          width: 120px;
          height: 80px;
          box-sizing: border-box;
          text-align: center;
          color: #575757;
        }
    
        .main .table .thead {
          background-color: #f7f5f5;
          height: 50px;
          line-height: 50px;
        }
    
        .main .table .tbody .td {
          line-height: 80px;
          font-size: 16px;
    
        }
    
        .main .table .tbody .td img {
          width: 80px;
          height: 80px;
          margin: 0 auto;
        }
    
        .main .table .tbody .td input {
          cursor: pointer;
        }
    
        .main .table .tbody .number-op {
          width: 120px;
          height: 40px;
          display: flex;
          justify-content: center;
          text-align: center;
          margin: 20px auto;
          box-shadow: 0 0 5px #818181;
        }
    
        .main .table .tbody .number-op .sub,
        .add {
          height: 100%;
          line-height: 40px;
          width: 40px;
          background-color: #f1f1f1;
          border: none;
          outline: none;
          cursor: pointer;
        }
    
        .main .table .tbody .number-op .number {
          width: 55px;
          height: 100%;
          line-height: 40px;
          background-color: #fefefe;
        }
    
        .main .table .tbody .tr .delelte button {
          width: 60px;
          height: 30px;
          line-height: 30px;
          background-color: #fc4f4f;
          color: #FFF;
          margin: 20px auto;
          border-radius: 4px;
          font-size: 12px;
          outline: none;
          border: none;
          cursor: pointer;
        }
    
        .main .bottom {
          width: 100%;
          height: 60px;
          line-height: 60px;
          border: 1px solid #e8e7e7;
        }
    
        .main .bottom .checkAll,
        .total {
          width: 60%;
          display: inline-block;
          text-indent: 50px;
        }
    
        .main .bottom .total {
          width: 25%;
          display: inline-block;
        }
    
        .main .bottom .total span {
          color: rgb(251, 135, 152);
          font-size: 30px;
          font-weight: bold;
        }
    
        .main .bottom button {
          width: 80px;
          height: 35px;
          border: none;
          background-color: #5689bc;
          color: #fff;
          border-radius: 4px;
          cursor: pointer;
        }
      style>
    head>
    
    <body>
      <div class="app-container" id="app">
        
        <div class="banner"><img src="https://img.tukuppt.com/bg_grid/00/03/63/pd1Ab79ETa.jpg!/fh/350" alt="">div>
        
        <div class="breadcrumb">主页/购物车div>
        
        <div class="main" v-if="fruitList.length>0">
          <div class="table">
            
            <div class="thead">
              <div class="tr">
                <span class="th">选中span>
                <span class="th">图片span>
                <span class="th">单价span>
                <span class="th">个数span>
                <span class="th">小计span>
                <span class="th">操作span>
              div>
            div>
            
            <div class="tbody">
              <div class="tr" v-for="(item,index) in fruitList" :key="item.id" :class="{active:item.isChecked}">
                <div class="td"><input type="checkbox" v-model="item.isChecked">div>
                <div class="td"><img :src="item.icon" alt="加载失败">div>
                <div class="td">{{item.price}}div>
                <div class="td number-op">
                  <button class="sub" :disabled="item.num<=1" @click="sub(item.id)">-button>
                  <div class="number">{{item.num}}div>
                  <button class="add" @click="add(item.id)">+button>
                div>
                <div class="td">{{item.num*item.price }}div>
                <div class="td delelte"><button @click="del(item.id)">删除button>div>
              div>
            div>
            
            <div class="bottom">
              <div class="checkAll"><input type="checkbox" v-model="isAll">全选div>
              <div class="total">总价:¥<span>{{totalPrice}}span>div>
              <button>结算(<span>{{totalCount}}span>)button>
            div>
          div>
        div>
        
        <div class="empty" v-else>
          <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-cart"
            viewBox="0 0 16 16">
            <path
              d="M0 1.5A.5.5 0 0 1 .5 1H2a.5.5 0 0 1 .485.379L2.89 3H14.5a.5.5 0 0 1 .491.592l-1.5 8A.5.5 0 0 1 13 12H4a.5.5 0 0 1-.491-.408L2.01 3.607 1.61 2H.5a.5.5 0 0 1-.5-.5zM3.102 4l1.313 7h8.17l1.313-7H3.102zM5 12a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm7 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm-7 1a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm7 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2z" />
          svg> 空空如也
        div>
      div>
      <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js">script>
      <script>
        const app = new Vue({
          el: '#app',
          data: {
            // fruitList: [
            //   {
            //     id: 1,
            //     // 草莓
            //     icon: 'https://bpic.588ku.com/element_origin_min_pic/19/03/07/e2a9f2049f8a27a802adc898a9a77263.jpg',
            //     isChecked: true,
            //     num: 2,
            //     price: 6,
            //   },
            //   {
            //     id: 2,
            //     //橘子
            //     icon: 'https://ts1.cn.mm.bing.net/th/id/R-C.cc043de924afe4c8e339ac9153fc6d0a?rik=idAViLjhl02Pkw&riu=http%3a%2f%2fimg95.699pic.com%2felement%2f40114%2f0063.png_860.png&ehk=%2bswafoP2gLwU6isZhlfWUgCDGnXxHt00KH1VlPG%2f3FI%3d&risl=&pid=ImgRaw&r=0',
            //     isChecked: true,
            //     num: 3,
            //     price: 6,
            //   },
            //   {
            //     id: 3,
            //     // 葡萄
            //     icon: 'https://tse2-mm.cn.bing.net/th/id/OIP-C.IE5_7TEOtuDLzOWoWmIevQHaFP?w=280&h=198&c=7&r=0&o=5&dpr=1.8&pid=1.7',
            //     isChecked: true,
            //     num: 2,
            //     price: 5,
            //   },
            //   {
            //     id: 4,
            //     // 西瓜
            //     icon: 'https://tse1-mm.cn.bing.net/th/id/OIP-C.j5_OeK8DBQLiq6PSHsh_bwHaHa?w=197&h=198&c=7&r=0&o=5&dpr=1.8&pid=1.7',
            //     isChecked: true,
            //     num: 2,
            //     price: 5,
            //   },
            //   {
            //     id: 5,
            //     //水蜜桃
            //     icon: 'https://tse1-mm.cn.bing.net/th/id/OIP-C.cJAO-NexLdDrjFm_TtOWvgHaHa?w=193&h=194&c=7&r=0&o=5&dpr=1.8&pid=1.7',
            //     isChecked: true,
            //     num: 8,
            //     price: 6,
            //   },
            //   {
            //     id: 6,
            //     // 苹果
            //     icon: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.3sFMitLegnrn_6OOGf6drQHaHa?rs=1&pid=ImgDetMain',
            //     isChecked: true,
            //     num: 2,
            //     price: 6,
            //   },
            //   {
            //     id: 7,
            //     // 荔枝
            //     icon: 'https://tse1-mm.cn.bing.net/th/id/OIP-C.zy_tCKWr8TxhJbG_ZZpFbQHaHa?w=210&h=210&c=7&r=0&o=5&dpr=1.8&pid=1.7',
            //     isChecked: true,
            //     num: 2,
            //     price: 6,
            //   },
            // ]
            // 取数据的时候去的是本地浏览器保存的数据
            fruitList: JSON.parse(localStorage.getItem('list')) || defaultArr,
          },
          methods: {
            del(id) {
              this.fruitList = this.fruitList.filter(item => item.id !== id)
            },
            add(id) {
              const fruit = this.fruitList.find(item => item.id === id)
              fruit.num++
            },
            sub(id) {
              const fruit = this.fruitList.find(item => item.id === id)
              fruit.num--
            }
          },
          computed: {
            // 完整写法:set+get 
            isAll: {
              get() {
                return this.fruitList.every(item => item.isChecked)
              },
              set(value) {
                // 同步小选框和全选框的状态
                this.fruitList.forEach(item => item.isChecked = value)
              }
            },
            totalCount() {
              return this.fruitList.reduce((sum, item) =>
                item.isChecked ? sum + item.num : sum
                , 0)
            },
            totalPrice() {
              return this.fruitList.reduce((sum, item) =>
                item.isChecked ? sum + item.num * item.price : sum
                , 0)
            }
          },
          watch: {
            fruitList: {
              deep: true,
              handler(newValue) {
                // 一旦修改,就将新数据存入浏览器本地中
                localStorage.setItem('list', JSON.stringify(newValue))
              }
            }
          }
        })
      script>
    body>
    
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
  • 相关阅读:
    alibaba dragonwell jdk
    ORB-SLAM3的Tracking线程详解
    CC2540和CC2541的区别简单解析
    儿童牙刷,U型牙刷,磨牙器亚马逊CPC认证检测标准
    Spring中的设计模式
    Windows系统下安装Ubuntu系统(双系统)
    不懂 Kubernetes 实现云原生是什么体验?
    【Mybatis小白从0到90%精讲】09:Mybatis动态SQL:if、where、set标签
    【Ubuntu虚拟机】
    Day 54 前端 jQuery
  • 原文地址:https://blog.csdn.net/L19541216/article/details/138160612