前端与移动开发----Vue----Vue快速入门

vue基本介绍

是什么

vue是:渐进式javascript框架

能帮我们做什么

  1. 充当模板引擎:把数据渲到页面上

image-20200511092943426

  1. 利用它的响应式特点,处理页面交互
  2. 使用路由组件系统开发复杂应用
  3. 使用脚手架,快速开发前端项目
  4. 跨端应用 – 小程序,原生app(手机中下载安装使用的app)

三大框架

安装使用vue的两种方式

vue的物理表现就是一个名为vue.js的文件

在不同的学习阶段有不同的使用方式:

  • 基础阶段:直接当做一个普通的js库来引入使用(与jquery类似)。有两种方式

    1. 本地方式
    • 先下载:https://cn.vuejs.org/js/vue.js
    • 然后在你的.html文件中引入:<script src="./vue.js"></script>
    1. 直接引入公网中的js
    // 地址1
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.9/vue.js"></script>
    // 地址2
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
    
  • 项目阶段:以npm包的方式来引入

    • 安装 npm install vue
    • 导入import Vue from 'vue'后使用即可

在基础学习阶段,使用本地下载引入的方式或者公网中的js均可。

第一个vue程序-体验模板引擎的效果

内容

  • 回顾模板引擎的概念
  • 回顾art-template.js的使用
  • 对比vue.js的模板引擎功能

回顾模板引擎的概念

在这里插入图片描述

这里的数据一般是从指从后端接口中获取的JSON数据,例如:

在这里插入图片描述

感受一下vue的使用

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <!-- 3. 视图 -->
  <div id="app">
    <h3>{{name}}</h3>
    <img :src="url"/>
    <p>
      {{info}}
    </p>
  </div>
  <!-- 1. 引入VUE核心js文件 -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> 
  <!-- 功能代码 -->
  <script>
    // 需要把数据渲染到页面上
    // 页面上输入的内容,收集到数据中

    // 2. 创建vue实例
    const vm = new Vue({
      el: '#app',
      data: {
        name: '尤雨溪',
        info: '尤雨溪是Vue.js框架的作者,HTML5版Clear的打造人。他认为,未来App的趋势是轻量化和细化,能解决问题的应用就是好应用。而在移动互联网时代大的背景下,个人开发者的机遇在门槛低,成本低,跨设备和多平台四个方面。',
        url: 'https://bkimg.cdn.bcebos.com/pic/4afbfbedab64034f29596c8ba6c379310b551da2'
      }
    })
  </script>
</body>
</html>

要点:

  • new Vue
  • {{ }}
  • :src

认识Vue实例及配置项

  • el

  • data

创建Vue实例

引入vue.js之后,就会多出一个Vue构造器,我们通过它来创建vue实例

const vm = new Vue({
    el: ,
    data: 
})
console.log(vm)

理解:

  • Vue(V是大写的)是一个构造器,用它来创建vue实例。
  • vm就是创建出来的实例对象
  • 这个构造器只有一个参数–配置项,这个参数是一个对象,其中的属性名和属性值都有特定的含义。

配置项

每一个都有自己的含义,并不全部都会用到,绝大多数都是可选的。

new vue({
	el: '#app',
  template,
  render,
  
  mixins,
  provide,
  inject,
  
  directives,
  filters,
  components,
  
  name,
  
  data:{},
  computed:,
  methods:,
  props:,
  
  beforeCreate,
  created:,
  beforeMount,
  mounted:,
  beforeUpdate,
  updated,
  activated,
  deactivated
  beforeDestory,
  destoryed:,
  watch:

})

参见:https://cn.vuejs.org/v2/api/

结论:

​ 学习vue,就是学习它的各个配置项的使用

学习Vue配置项-el

作用

el选项用来指定vue实例管理的容器。

取值

el 的值可以是:

  • 选择器(类选择器,id选择器,标签选择器,…)
  • 某个dom元素

不能是 html,body 容器

示例代码

<body>
  {{msg}}
  <div id="app">
    {{msg}}
    <!-- 被管理的容器才具备使用vue提供的功能 -->
  </div>
  <script src="./vue.js"></script>
  <script>
    // 初始化vue实例,vue实例就是vm的意思
    const vm = new Vue({
      // 初始化的配置对象,里面有很多选项
      // el === element 元素,标签,容器
      // el指定当前vue实例管理的容器是谁。其值是:选择器 | dom元素
      // (1) 选择器  el: '#app',
      // (2) dom元素 el:  document.querySelector('#app'),
      // Do not mount Vue to <html> or <body> - mount to normal elements instead.
      // (3)el不能指定 html 和 body 容器
      el: 'body',
      data: {
        msg: 'hi vue'
      }
    })
  </script>
</body>
</html>

要点:

  • el 可以是选择器,也可以是某个dom元素
  • 只能是普通的dom(div,p),不能是 html,body 容器
  • 不在此容器内的部分,不会受到vue实例控制

学习Vue配置项-data选项

作用

它是一个对象(可以嵌套各类数据),它用来设置数据项,为视图提供数据源。没有在data中定义的数据,在模板中是无法访问的

定义格式

new Vue({
	data: {
		
	}
})

使用格式

在模板中使用

{{数据}}

在代码中使用

Vue实例.数据

示例

<div id="app">
  <h3>理解data</h3>

  <p>姓名:{{name}}</p>
  <p>爱好1:{{aihao[0]}}</p>
  <p>爱好2:{{aihao[1]}}</p>
  <ul>
    <li>QQ:{{lianxi.QQ}}</li>
    <li>email:{{lianxi.email}}</li>
    <li>weixin:{{lianxi.weixin}}</li>
  </ul>
</div>


<script>
  // data: 它是一个对象,用来规定数据。

  // 现在是把vue当作一个模板引擎来使用。
  // 它的作用是把数据显示在页面上。data就是用来
  // 约定数据的。
  // 在后面的学习的,这个部分的数据将会是从ajax接口中获取到的
  // 在初学阶段,我们将直接设置它


  // 在视图中访问数据,通过{{}}插值表达式 直接访问。
  /// 不要写data.XXX - 不要写data - 不要写data

  const vm = new Vue({
    el: "#app", // el: document.getElementById("app"),
    data: {
      name: '東三城',
      aihao:['听音乐','读书'],
      lianxi: {
        QQ:"123456",
        email:'xxxxxx',
        weixin:'yyyyyy'
      }
    }
  })
  console.log(vm)
  console.log(vm.name)
  console.log(vm.aihao[1])
  console.log(vm.lianxi.QQ)
</script>

访问方式:

  1. 通过实例可以直接访问data中的数据 vm.msg
  2. 在视图中使用数据的字段名称即可。{{msg}}

注意:

  • 没有在data中定义的数据项将无法在视图中访问到。

插值表达式

功能

将数据显示到模板中。

把{{ }} 理解为一个占位符(一个坑), {{ msg }} 就是把msg显示在这个占位符中(把msg插到坑里面边去)

在这里插入图片描述

在vue中{{ }}语法,叫做:插值表达式,大白话就是输出{{}}中的表达式的值的语法。

语法

可以

  • 写data数据字段名称
  • 对data数据字段进行表达式运算
    • 拼接
    • 算术运算
    • 三元运算

不能:

  • js语句:声明变量,分支,循环
  • 访问在vue实例中的data之外定义的自定义的变量

数据项

​ data: {
​ name: ‘vue’,
​ num: 80,
​ isOpen: false,
​ info: { city: ‘北京’, specialty: ‘麻辣小龙虾’}
​ goods: [{name: ‘手机’, price: 3000}]
​ }

示例代码

<!DOCTYPE html>
<html lang="zh">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>学习插值表达式</title>
  <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
  <style>
    body {
      background-color: #eee;
    }

    #app {
      background-color: #fff;
      width: 500px;
      margin: 50px auto;
      box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.5);
      padding: 2em;
    }

    .box {
      padding: 1em;
      border: 1px solid #ccc;
      margin: 1em;
    }
  </style>
</head>

<body>
  <div id="app">
    <h2>学习插值表达式</h2>
    <!-- 1. 直接获取data中的数据 -->
    <p>{{name}}</p>
    <!-- 2. 字符串拼接 -->
    <p>{{'你好呀!' + name}}</p>
    <!-- 3. 算术运算 -->
    <p>{{num/2}}</p>
    <!-- 4. 直接输出常量 -->
    <button>{{ '打开' }}</button>
    <button>{{ "打开" }}</button>
    <button>{{ isOpen }}</button>
    <!-- 5. 三元 -->
    <button>{{ isOpen ? '打开' : '关闭' }}</button>
    <!-- 6. 对象的属性 -->
    <div>欢迎大家来 {{info.city}} 做客,吃{{info.specialty}}</div>
    <!-- 7. 数组 -->
    <div>我的手机是{{goods[0].price}}元买的</div>

    <!-- 不能 1. 写js语句 -->
    <!-- <h1>{{var a = 1}}</h1> -->
    <!-- 不能 2. 访问vue实例之外的变量 -->
    <!-- <h1>{{ a }}</h1> -->
    
  </div>
  <script>
    var a = 100
    new Vue({
      el: '#app',
      data: {
        name: 'vue',
        num: 80,
        isOpen: false,
        info: { city: '北京', specialty: '麻辣小龙虾' },
        goods: [
          { name: '手机', price: 3000 }
        ]
      }
    })

  </script>
</body>

</html>

可能会遇到的错误

在{{}}引用了没有在data中定义的数据项

错误

Property or method "abc" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property

解决

检查{{}}中引用的数据是否出现在data中。

vue中的指令

{{}}可以用来把数据显示在视图上,但是,它的功能还是相当有限的,例如,如果是一个列表数据(数组)希望使用循环来显示,它就做不到了。

所以我们需要来学习vue的指令

指令

vue提供了指令(directive)功能,这些指令都是以v-开头(例如:v-for ),它们从形式上来看是标签的自定义属性(只不过是由vue提供的)

示例:

在这里插入图片描述

作用

指令必须用在某个标签(或者是组件)上, 用来扩展标签的原有功能。

学习vue,一个主要的学习内容就是各种指令的用法

明细

每个指令都有不同的作用,下面是一些常见的,必须要掌握的指令:

v-model

v-for

v-text

v-html

v-if

v-show

v-if,v-else-if, v-else

v-bind

小结

  • vue提供指令系统来增强标签的能力

  • 指令的学习基本上没有先后顺序关系的,先学哪一个,后学哪一个都可以。

  • 指令要与具体的标签结合一起使用。

  • 一个标签上也可以使用多个指令。

v-for 列表渲染

在模板中,当我们需要对数据进行循环时,就可以使用v-for指令(不像js中的三种循环结构:while, for, do…while; 在vue中只有唯一一个循环指令v-for)

作用

快速批量生成列表结构

格式

格式1:完整格式

<元素 v-for="(遍历变量,索引变量) in 可遍历的数据项" :key="唯一的id"></元素>

格式2:简写格式

在完整格式的基础上,可以省略 :key 和 索引变量。

<元素 v-for="遍历变量 in 可遍历的数据项"></元素>

遍历目标

对于v-for指令,可遍历的数据项可以是:

  • 数组(重要)
  • 对象(了解)
  • 数值(了解)

代码1 - 对数组进行循环

<!DOCTYPE html>
<html lang="zh">
<head>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>学习v-for</title>
 <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
 <style>
   body{
     background-color: #eee;
   }
   #app{
     background-color:#fff;
     width: 500px;
     margin: 50px auto;
     box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);padding:2em;
    }
   .box{
     padding: 1em;
     border:1px solid #ccc;
     margin:1em;
   }
   </style>
 </head>
 <body>
   <div id="app">
     <h2>学习v-for</h2>
     <p v-for="(good,index) in goods" :key="index">
      {{index}} -- 商品名:<b>{{good.name}}</b>-价格:{{good.price}}
     </p>
     <hr>

     <h2 v-for="abc in goods">{{abc.name}}:{{abc.price}}</h2>

     <p>商品名: <b>{{goods[0].name}}</b>-价格:{{goods[0].price}}</p>
     <p>商品名: <b>{{goods[1].name}}</b>-价格:{{goods[1].price}}</p>
   </div>
   <script>
     /**
      * 
      * <p v-for="(good,index) in goods" :key="index">
          {{index}} -- 商品名:<b>{{good.name}}</b>-价格:{{good.price}}
        </p>
      * 
      * 理解为:
      * goods.forEach(function (good, index){
      *   console.log(good.name, index)
      * })
      * 
      */ 
     new Vue({
       el: '#app',
       data: {
         goods: [
           { name: '手机', price: 3000},
           { name: '电脑', price: 5000}
         ]
       }
     })
   </script>
 </body>
</html>

使用了v-for指令的元素,在其内部可以通过插值表达式来访问遍历变量和循环变量。

代码2-对对象-数值进行循环

v-for一般用于遍历数组(正道),但是,在语法上,它也可以对对象及数值进行循环。下面是示例代码:


   <div id="app">
     <h2>学习v-for - 对象-数值</h2>
     <div>属性值: 是遍历变量,是第一个参数 item</div>
     <div>属性名: 是循环变量,是第二个参数 index</div>
     <p v-for="(item,index) in info">
       item-属性值: {{item}}, index-属性名:{{index}}
     </p> 
     <hr>
     <!-- <h5 v-for="(item,index) in num">
        item-值,从1开始: {{item}}, index-下标从0开始:{{index}}我爱你!
      </h5> -->

      <div v-for="item in 11">
        i miss you!
      </div>

   </div>
   <script>
     
     new Vue({
       el: '#app',
       data: {
         num: 3000,
         info: {
           name: '张三',
           msg: '法外狂徒',
           source: 'B站'
         },
         goods: [
           { name: '手机', price: 3000},
           { name: '电脑', price: 5000}
         ]
       }
     })
   </script>

v-text和v-html

作用

​ 都可以用来指定标签的内容

区别

  • v-text指定标签的内容,格式是文本,和 innerText 相似。
  • v-html指定标签的内容,格式是html,和 innerHTML 相似。

在代码中体验

<div id="app">
  <h3>v-html,v-text</h3>
  <!-- {{}}可以用来设置标签中的部分内容 -->
  <p>学习内容:{{str}}</p>

  <p>{{str}}</p>
  <!-- v-text:设置标签中的 全部内容 -->
  <p v-text="str"></p>
  <!-- v-text:设置标签中的 
如果这里内容是html字符串,它会转义一下,只是显示字符串
而不会显示成对应的dom结构 -->

  <div v-text="strHtml"></div>

  <!-- v-html用来设置标签中的内容。与v-text不同,它直接解析其中的
html结构,并渲染 -->
  <div v-html="strHtml"></div>

</div>


<script>
  // v-text : ===> innerText
  // v-html : ===> innerHTML
  const vm = new Vue({
    el: "#app",
    data: {
      str: 'vue',
      // 有时,从后端取回来的数据是一个html字符串。
      // 例如:获取文章详情,取回来就是一html字符串。

      strHtml: '<h1>vue</h1>'
    }
  })
</script>

总结:

  • v-text 显示纯文本
  • v-html 显示html内容

v-show和v-if

功能

用v-show和v-if去控制标签的显示隐藏

格式

<元素 v-show="布尔值"></元素>
<元素 v-if="布尔值"></元素>

或者可以转换成布尔值的表达式

区别

共同点:都能控制元素的是否可见

不同点:

  • v-show是通过 display:none来控制隐藏
  • v-if 是通过动态创建和移除元素来控制隐藏和显示的

示例

<div id="app">
        <h3>v-if,v-show</h3>
        <div v-if="isVisiable">
            我是通过v-if来控制是否可见!
        </div>

        <div v-show="isVisiable">
            我是通过v-show来控制是否可见!
        </div>
        
    </div>

    <script>
        // 作用:控制标签的可见与不可见

        // 格式:
        // <元素 v-show="布尔值"></元素>
        // <元素 v-if="布尔值"></元素>

        // 如果是false,则不可见,如果是true,则可见

        // 区别:
        // 对于v-if,如果为false,则,在页面根本就不生成这个dom。
        //     所以不可见。
        // 对于v-show,如果为false,则,在页面有这个dom,display:none。
        //     所以不可见。

        const vm = new Vue({
            el: "#app",
            data: {
                isVisiable: false
            }
        })
    </script>

拓展-可以是返回boolean的表达式

<p v-show="1">p3</p>
<p v-show="-1">p4</p>
<p v-show="0">p5</p>
<p v-show="1 > 2">p6</p>
<p v-show="1 < 2">p7</p>
<p v-show="isShow2">p8</p>
<p v-show="!isShow2">p9</p>
<p v-show="arr2">p10</p>
<p v-show="arr2.length">p11</p>
<p v-show="isShow">p12</p>
<p v-show="'isShow'">p13</p>
<p v-show="''">p14</p>

区分下使用场景

  • v-show 性能较好,对应需要频繁切换显示与隐藏的功能,可以使用v-show。注意它还是会创建元素的。
  • v-if 会动态创建和删除元素。它的安全性更好的一些, 效率会低一点。

具体场景:

场景1:如果是登陆用户就显示头像,如果不是登陆用户就显示请登陆(v-if)

场景2:如果用户单击了某个选项卡签,就显示对应的内容(v-show)

v-if,v-else-if,else

功能

模板中的选择结构

格式

与js中的if选择结构是一致的。

<标签 v-if="条件">        </标签> 
<标签 v-else-if="条件">   </标签> 
<标签 v-else-if="条件">   </标签> 
<标签 v-else>             </标签>

示例

<script>
    //  if(){
       
    //  }
    //  else if(){

    //  }
    //  else{

    //  }

   </script>
   <div id="app">
     <h2>学习v-if-elseif-else</h2>
     <p v-if="num >= 80">优秀</p> 
     <p v-else-if="num >= 60">一般</p> 
     <p v-else>不及格</p>
   </div>
   <script>
      new Vue({
        el: '#app',
        data: {
          num: 59 // 分数
        }
      })
   </script>

v-bind

作用

动态绑定标签上的属性的值。(标签上的属性不能使用插值表达式来设置)。

格式

bind:绑定。以前学习过,用它来改变this的指向

<元素 v-bind:属性名1="值1" v-bind:属性名2="值3"></元素>
// 简写用法 `:` 冒号   省略v-bin
<元素 :属性名1="值1" :属性名2="值3"></元素>

v-bind指令有简写用法 : 冒号,例如:

<img :src="data数据">

一般在是在属性名的=里面写一个data中的数据项, :属性名="数据项"

称之为:把x绑定在y属性上

演示代码

<div id="app">
     <h2>v-bind绑定属性值</h2>
     <p>{{name}}</p>
     <!-- 绑定属性 
      v-bind:src 可以简写成  :src 
    -->
     <!-- <img src="{{imgSrc}}"/> -->
     <img v-bind:src="imgSrc" v-bind:id="id"/>
   </div>
   <script>
      var vm = new Vue({
        el: '#app',
        data: {
          name: "尤大大",
          id: 'imgHeader',
          imgSrc: "https://bkimg.cdn.bcebos.com/pic/4afbfbedab64034f29596c8ba6c379310b551da2"
        }
      })
   </script>

思考题

问:如下两种写法的区别是什么?

<div id="myId"></div>

<div :id="myId"></div>

答: 就是一个普通的id属性;这个div的id属性值要从data中的定义的myId这个中去找, 如果找不到,就会报错。

v-bind绑定class

目标

这是v-bind的经典应用场景之一:掌握动态绑定类

三种方式

  • 三元运算符
    • 格式: <元素 :class="表达式 ? '类名1' : '类名2'">
    • 逻辑:三元
    • 场景:适合二选一的场景
  • 绑定对象
    • 格式: <元素 :class="{'类名1': 表达式1, '类名2': '表达式2', ....}">
    • 逻辑:如果属性值为true,则添加对应的类给元素
    • 场景:适合是否有的场景
  • 绑定数组
    • 适合绑定多个类

代码

<!DOCTYPE html>
<html lang="zh">
<head>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>学习v-bind处理class</title>
 <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
 <style>
   body{
     background-color: #eee;
   }
   #app{
     background-color:#fff;
     width: 500px;
     margin: 50px auto;
     box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);padding:2em;
    }
   .box{
     padding: 1em;
     border:1px solid #ccc;
     margin:1em;
   }
   .p { background-color: pink;}
   .pass { font-size: 50px; color: green;}
   .fail { font-size: 80px; color: red;}
   .blod {font-weight: bold;}
   .tc {text-align: center;}
   .fs50 { font-size: 50px;}
   </style>
 </head>
 <body>
   <div id="app">
     <h2>学习v-bind处理class</h2>
     <!-- v-bind:class会与原生的class进行合并 -->

     <!-- 1. 三元表达式。在两个类选一个 -->
     <!-- <p class="p" v-bind:class="num > 60 ? 'pass' : 'fail'">
       p1
     </p> -->

     <!-- 2. 对象。如果属性值是true,则添加属性名到class中 -->
     <!-- <p class="p" v-bind:class="{blod: true, tc: false}">
       p2 - 会有p, bold 这两个类
     </p> -->

     <!-- <p class="p" v-bind:class="{blod: false, tc: num < 60}">
      p3 - 会有p,tc这两个类
     </p> -->
     <!-- <p class="p" v-bind:class="classObj">
      p4 - 会有p, bold, tc这三个类
     </p> -->

     <!-- 3. 数组: 会数组所有的类都加到class中 -->
     <!-- <p class="p" v-bind:class="['bold', 'tc']">
       p5  ---> p,blod, tc
     </p>
     <p class="p" v-bind:class="classArr">
      p6: 它会有p, bold, tc, fs50这四个类
    </p>
   </div>
   <script>
      var vm = new Vue({
        el: '#app',
        data: {
          num: 50,
          classArr: ['blod', 'tc', 'fs50'],
          classObj: {
            bold: true,
            tc: true
          }
        }
      })
   </script>
 </body>
</html>

注意:

  • :class 动态绑定类名 class 原生属性,可以同时存在,他们包含的所有类会合并在一起生效。

v-bind绑定style

目标

知道如何动态绑定style,去操作行内样式

两种方式:

  • 对象形式
    • styleArr: [{color: ‘#ccc’}, {border: “10px solid black”}]
  • 数组形式
    • styleObj: { color: ‘#ccc’, border: “10px solid black”}

示例

v-bind绑定style使用对象

<!DOCTYPE html>
<html lang="zh">
<head>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>学习v-bind处理style</title>
 <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
 <style>
   body{
     background-color: #eee;
   }
   #app{
     background-color:#fff;
     width: 500px;
     margin: 50px auto;
     box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);padding:2em;
    }
   .box{
     padding: 1em;
     border:1px solid #ccc;
     margin:1em;
   }
   </style>
 </head>
 <body>
   <div id="app">
     <h2>学习v-bind处理style</h2>
     <!-- 1. style中写一个对象 -->
     <p style="background-color: pink;" v-bind:style="{color: 'red', 'font-size': '80px'}">
      p1
      </p>

      <p style="background-color: pink;" v-bind:style="styleObj">
        p2
      </p>
     <!-- 2. style中写一个数组 -->

      <p style="background-color: pink;" v-bind:style="styleArr">
        p3
      </p>
   </div>
   <script>
      var vm = new Vue({
        el: '#app',
        data: {
          styleArr: [{color: '#ccc'}, {border: "10px solid black"}],
          styleObj: {
            color: '#ccc',
            border: "10px solid black"
          }
        }
      })
   </script>
 </body>
</html>

总结:

  • :style 使用对象 {css属性名:css属性值}
  • :style 使用数组 [{css属性名:css属性值}]

体验vue的响应式特点

响应式

vue 会根据 data中的数据和el中的模板生成对应的视图

在这里插入图片描述

当数据变化后,视图也会自动更新

当数据变化后,视图也会自动更新

当数据变化后,视图也会自动更新

在这里插入图片描述

这种特点就称为响应式的特点。

示例代码

<div id="app">
  <h2>体现vue响应式的特点</h2>
  <h4>{{name}}</h4>
  <p v-for="abc in goods">{{abc.name}}:{{abc.price}}</p>
</div>
<script>
  // 结论:数据变了,视图会跟着变化
  // 通过代码去修改数据项,则会发现视图也自动变化
  const vm = new Vue({
    el: '#app',
    data: {
      name: '张三',
      goods: [
        { name: '手机', price: 3000},
        { name: '电脑', price: 6000}
      ]
    }
  })
  // 如何去修改数据?
  // 1. 在代码中,通过 "实例.数据项名=新值"
  //  console.log(vm.name)
  //  vm.name = '李四'
  //  setInterval( () => {
  //    vm.name = '张三' + Date.now()
  //  }, 2000)
  // 2. 在控制台中,也可以通过 "实例.数据项名=新值"
  // vm.goods.splice(0,1)
</script>

通过vm.XXX的方法来访问数据项

安装使用vue-devtools

为了方便我们调试vue程序,vue官方提供了一个针对vue项目的调试工具。

重要,重要,重要

使用效果

在这里插入图片描述

使用

这个插件并不会时时有效,只有当满足如下两个条件时,它会才生效

  • 当前的网页中有使用vue.js

  • 使用的vue.js是开发模式下的版本

    如果你使用的是生产版本( https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js)这个插件也不会工作。

观察

运行某个用vue开发的程序,打开浏览器调试工具

鼠标可拖动位置

在这里插入图片描述

修改数据项

在这里插入图片描述

可以直接在调试工具中修改数据项的值

计算属性

data: {
   goods: [ {name: '手机', price: 2000 }, {name:'电脑': price: 5000}]
}

提问,计算商品总价?

使用场景

如果:

  • 需要使用的数据在data中并不能直接找到
  • 需要依赖data中的数据,但是需要经过一些逻辑处理,才能得到你想要的数据。

此时就可以使用计算属性。

例如:计算列表项中的总价

<div id="app">
  	<p v-for="item in goods">
      {{item.name}} - {{item.price}}
  	</p>
    <div>总价格是:{{ }}</div>
  </div>
  <script src="./vue.js"></script>
  <script>
    const vm = new Vue({
      el: '#app',
      data: {
        goods: [ {name: '手机', price: 2000 }, {name:'电脑': price: 5000}]
      }
    })
  </script>

定义格式

在vue实例中,补充computed配置项。computed 是vue的配置选项,它的值是一个对象,其中可定义多个计算属性,每个计算属性就是一个函数。

new Vue({
    // 声明计算属性
    computed: {
        //属性名字(计算属性名称)
        //属性的值(计算属性处理函数)
        计算属性名1 () {
            // 对依赖的数据进行处理,且进行return
            return 
        }计算属性名2 () {
            // 对依赖的数据进行处理,且进行return
            return 
        }
    }
})
  • 属性名称: 计算属性的名称
  • 属性的值:是一个函数,对需要依赖的数据,进行逻辑上的处理处理完毕后,return结果,它返回的值就是计算属性的值
    • 这个函数并不需要入参
    • 在这个函数内部可以直接使用this.XXX的方式来获取data中的数据项值

使用格式

在两个地方使用:

  • 模板
    • 插值表达式 {{ 计算属性名 }}
    • 其它指令
  • 在实例内
    • this.计算属性名

示例

<!DOCTYPE html>
<html lang="zh">
<head>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>计算属性.html</title>
 <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
 <style>
   body{
     background-color: #eee;
   }
   #app{
     background-color:#fff;
     width: 500px;
     margin: 50px auto;
     box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);padding:2em;
    }
   .box{
     padding: 1em;
     border:1px solid #ccc;
     margin:1em;
   }
   </style>
 </head>
 <body>
   <div id="app">
     <h2>计算属性.html</h2>

     <p v-for="(item,index) in goods">
       {{item.name}}:{{item.price}}
     </p>

     <!-- 我们需要一个新的数据项 总价,它的特点是:要依赖于已有数据项计算出来 -->
     <p>总价:{{totalMoney + '元'}}</p>
   </div>
   <script>
      // 计算属性
      const vm = new Vue({
        el: '#app',
        data: {
          goods:[
            {name: '电脑', price: 5010},
            {name: '手机', price: 2205},
            {name: '平板', price: 1599}
          ]
        },
        // 它是一个对象,用来收集所有的计算属性
        computed: {
          totalMoney: function(){
            // const m = this.goods[0].price + this.goods[1].price + this.goods[2].price
            let m = 0
            this.goods.forEach( item => m+=item.price)
            return m
          }
          //属性名(计算属性名称) 属性值(计算属性处理函数)
          // 计算属性名1 () {
          //     // 对依赖的数据进行处理,且进行return
          //     return 
          // },
          // 计算属性名2 () {
          //     // 对依赖的数据进行处理,且进行return
          //     return 
          // }

     
          // 1. 响应式的特点
          // 2. 它在视图中使用起来,就像是一个普通的数据项一样
          // 3. 它本质上是一个函数,在函数内部:
          //    (1) 通过this.数据项名 来访问data中定义的数据
          //    (2) 一定要有return
        }
      })
   </script>
 </body>
</html>

总结

  • 什么时间用:需要对数据进行复杂的逻辑加工,产生新的数据时。

  • 定义: 就是一个特殊的配置项computed。其中有多个函数。

  • 使用:计算属性的使用方式与data中的数据项一致;

    • 计算属性-计算:这个值是对原数据进行计算之后得到的新的数据
    • 计算属性-属性:它的使用方法与原数据一样。this.计算属性名,{{计算属性名}}
  • 执行的时机: 如果计算属性中依赖的数据项变化时,它会自动调用。

computed有缓存

如果在模板中使用多次,但依赖的数据不发生改变,计算属性对应的函数不会重新执行。

  • 计算属性会做缓存,提高渲染的性能。

在这里插入图片描述

缓存:把上一次的执行结果保存下来,以便后续再次使用。

示例

<!DOCTYPE html>
<html lang="zh">
<head>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>html页面</title>
 <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
 <style>
   body{
     background-color: #eee;
   }
   #app{
     background-color:#fff;
     width: 500px;
     margin: 50px auto;
     box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);padding:2em;
    }
    .btn{
      float:right;
      cursor: pointer;
    }
   .box{
     padding: 1em;
     border:1px solid #ccc;
     margin:1em;
   }
   </style>
 </head>
 <body>
   <!-- 
     作用: 根据已有数据项(data中定义)生成新的数据项
     
     特点:
     1. 计算属性在使用时,就好像是在使用data中定义的数据一样,并不需要加()
     2. 在计算属性内部,可以通过this.XXX的格式来访问data中的数据
     3. 如果计算属性中使用到的数据发生了变化,则计算函数会自动重新执行
    -->
  <div id="app">
    <!-- {{ 中的值会在data, computed中去找}} -->
    <div>{{num}}</div>
    <div>计算属性:{{cDoubleNum}}</div>
    <div>计算属性:{{cDoubleNum}}</div>
    <div>计算属性:{{cDoubleNum}}</div>
    <div>计算属性:{{cDoubleNum}}</div>
    <p>计算num的两倍是:{{2 * num }}</p>

    <p>a: {{a}}</p>
  </div>


  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        a: 1, 
        num: 100
      },
      computed: {
        // cDoubleNum: function () {
        //   return this.num * 2
        // },
        cDoubleNum () {
          console.log('计算属性 cDoubleNum 执行了一次')
          // this.num 用来访问data中定义的数据num
          return this.num * 2 + this.a
        }
      }
    })
  </script>
 </body>
</html>

总结:

  • 计算属性有缓存,提高渲染性能。
  • 如果在页面上需要用到 对现有的数据进行加工得到新数据,则时要使用计算属性

过滤器

目标:

  • 理解过滤器的作用和使用场景
  • 掌握定义和使用过滤器。

问题导入

要显示从后端取回来的某用户的订单数据

// 
var orders =  [
  { 
    name: '裤子', price:271.195, timeStamp: 1602945572736, status: 1,
  }, 
  {
    name: '电脑', price:6699.398, timeStamp: 1602975572756,  status: 2,
  },
  {
    name: '方便面', price:15.507, timeStamp: 1602945672536,  status: 3 
}]

基本实现代码

<div id="app">
     <h2>过滤器</h2>
     <p v-for="(order,index) in orders">
      序号:{{index+1}}, 
      {{order.name}}, 
      价格:{{order.price}},
      订单日期:{{ order.timeStamp}}, 
      状态:{{order.status}}
     </p>
   </div>
   
var vm = new Vue({
        el: '#app',
        data: {
          orders:[
            { 
              name: '裤子', price:271.195, timeStamp: 1602945572736, status: 1,
            }, 
            {
              name: '电脑', price:6699.398, timeStamp: 1602975572756,  status: 2,
            },
            {
              name: '方便面', price:15.507, timeStamp: 1602945672536,  status: 3 
          }]
        }
      })

产品的需求

说明如下:

  • timeStamp是一个时间戳,显示时需要转成2020-10-10这种格式
  • status表示状态, 其中: 1表示 待付款, 2 表示 待发货 , 3表示 待收货
  • price表示价格,在显示在页面上时,要四舍5入到1位小数
  • 另外,如果状态是待付款,则在页面上要显示成高亮的颜色(例如红色)

理解过滤器

过滤器的作用:转换格式。

格式

定义的格式
new Vue({
      // 过滤器的定义
      filters: {
        // 属性名称(过滤器名称):属性的值(过滤器处理函数)
        myFilter:function(value,其它参数){
             return 过滤后的结果
        }
      }
    })
使用的格式
// 不带参数
{{ msg | 过滤器}}
// 带参数
{{ msg | 过滤器(参数)}}
// 串联
{{ msg | 过滤器1 | 过滤器2 }}

示例1:不带参数的过滤器

把时间戳转成年-月-日格式

filters: {
          // 过滤器的定义
          // 属性名称(过滤器名称):属性的值(过滤器处理函数)
          // myFilter: function(value,其它参数) {

          // }
          fFormatDate: function (val) {
            console.log('val', val)
            // val 就是需要被过滤器处理的值
            // return '你被过滤了' + val
            const dt = new Date(val)
            return dt.getFullYear() + '-' + (dt.getMonth()+1) + '-' + dt.getDate()
          }
        
        },

调用:

<div id="app">
     <h2>过滤器-{{formatedDate}}</h2>
     <p v-for="(order,index) in orders">
      序号:{{index+1}}, 
      {{order.name}}, 
      价格:{{order.price}},
      订单日期:{{ order.timeStamp | fFormatDate}}, 
      状态:{{order.status}}
     </p>
   </div>

注意:

  • 它的工作过程就是函数的调用执行过程。

  • 过滤器本质是一个函数:它的接收的参数是格式化之前的数据及格式化的参数,它的返回值是格式化之间的值

正确显示商品状态

fStatus: function (status) {
            if (status === 1) {
              return '待付款'
            } else if (status === 2) {
              return '待发货'
            } else if (status === 3) {
              return '待收货'
            } else {
              return '未知'
            }
          }

视图

状态:{{order.status | fStatus}}

示例2:带参数过滤器

在这里插入图片描述

把价格,按1位小数,四舍五入。

453.123 --> 453.1 ; 453.663 --> 453.7

<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>过滤器</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
    <style>
      body{
        background-color: #eee;
      }
      #app{
        background-color:#fff;
        width: 500px;
        margin: 50px auto;
        box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);padding:2em;
      }
      .box{
        padding: 1em;
        border:1px solid #ccc;
        margin:1em;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <h2>过滤器- {{price | fPrice(0) }}</h2>
      <p v-for="(order,index) in orders">
        序号:{{index+1}}, 
        {{order.name}}, 
        价格:{{order.price | fPrice}},
        订单日期:{{ order.timeStamp | fFormatDate}}, 
        状态:{{order.status | fStatus}}
      </p>
    </div>
    <script>
      var vm = new Vue({
        el: '#app',
        data: {
          price: 167.891,
          orders:[
            { 
              name: '裤子', price:271.195, timeStamp: 1602945572736, status: 1,
            }, 
            {
              name: '电脑', price:6699.398, timeStamp: 1602975572756,  status: 2,
            },
            {
              name: '方便面', price:15.507, timeStamp: 1602945672536,  status: 3 
            }]
        },
        filters: {
          // 过滤器的定义
          // 属性名称(过滤器名称):属性的值(过滤器处理函数)
          // myFilter: function(value,其它参数) {

          // }
          // dig = 1 是参数的默认值
          fPrice: function(val, dig=1) {
            return Number(val).toFixed(dig)
          },

          fFormatDate: function (val) {
            console.log('val', val)
            // val 就是需要被过滤器处理的值
            // return '你被过滤了' + val
            const dt = new Date(val)
            return dt.getFullYear() + '-' + (dt.getMonth()+1) + '-' + dt.getDate()
          },
          fStatus: function (status) {
            if (status === 1) {
              return '待付款'
            } else if (status === 2) {
              return '待发货'
            } else if (status === 3) {
              return '待收货'
            } else {
              return '未知'
            }
          }
        },
        computed: {
          formatedDate () {
            // this.date1  ------> 2020-10-10
            const dt = new Date(this.date1)
            return dt.getFullYear() + '-' + (dt.getMonth()+1) + '-' + dt.getDate()
          }
        }
      })
    </script>
  </body>
</html>

示例3:过滤器串联

在这里插入图片描述

特殊显示类

状态:<label :class="order.status | fStatus | fCss">{{order.status | fStatus}} </label>


fCss: function (status) {
            if (status === '待付款') {
              return 'no-pay'
            } else if (status === '待发货') {
              return 'no-send'
            } else if (status === '待收货') {
              return 'no-rece'
            }
          }

小结

过滤器使用场景: 对于拿到的数据在显示到页面的过程中,如果发现格式不是我们想要的,可以通过过滤器去转下格式。它一定需要入参,入参表示要转格式的数据。

计算属性:对于拿到的数据进行加工,以得到新的数据项。它不需要入参,因为它需要的数据在函数内部都可以通过this.XXX来获取到。

在vue3中已经不再支持过滤器了

事件绑定指令v-on

功能

v-on指令的作用是:监听在当前元素上发生的事件,并在触发时执行回调。

格式

在一个标签上绑定事件有三种使用格式

# 格式一:把要执行的代码直接写在""内部。适合少量代码
<标签 v-on:事件名="要执行的代码"></标签>

# 格式二:指向methods中的定义的函数
<标签 v-on:事件名="methods中的函数"></标签>

# 格式三:指向methods中的定义的函数,并传入参数
<标签 v-on:事件名="methods中的函数(实参)"></标签>

说明:

示例代码

<!DOCTYPE html>
<html lang="zh">
<head>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>html页面</title>
 <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
 <style>
   body{
     background-color: #eee;
   }
   #app{
     background-color:#fff;
     width: 500px;
     margin: 50px auto;
     box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);padding:2em;
    }
   .box{
     padding: 1em;
     border:1px solid #ccc;
     margin:1em;
   }
   </style>
 </head>
 <body>
   <div id="app">
     <h2>html页面</h2>
     工资:{{salary}}
     <!-- 点击之后: 更新到10500 -->
     <button v-on:click="salary=10500">增加500</button>
     <!-- 每次点击: +500 -->
     <button v-on:click="salary+=500">增加500</button>

     <!-- 函数()可以写也可以不写 -->
     <button v-on:click="hAdd500()">+500并税后</button>
     <button v-on:click="hAdd500">+500并税后</button>
     
     
     <button v-on:click="hAdd(2000)">随机奖金2000</button>
     <button v-on:click="hAdd(1000)">随机奖金1000</button>
   </div>
   <script>
    // 给某个元素绑定事件的三种格式
    // # 格式一:把要执行的代码直接写在""内部。适合少量代码
    // <标签 v-on:事件名="要执行的代码"></标签>

    // # 格式二:指向methods中的定义的函数
    // <标签 v-on:事件名="methods中的函数"></标签>

    // # 格式三:指向methods中的定义的函数,并传入参数
    // <标签 v-on:事件名="methods中的函数(实参)"></标签>
      var vm = new Vue({
        el: '#app',
        data: {
          salary: 10000
        },
        methods: {
          hAdd (val) {
            this.salary = this.salary + val
          },
          hAdd500 () {
            // this.salary = this.salary + 500

            this.salary = (this.salary + 500) * 0.95 // -5%
          }
        }
      })
   </script>
 </body>
</html>

总结:

  1. 绑定函数的时候带不带括号?

    1. 如果你需要自己来进行传参,需要带括号,自己传实参
    2. 如果什么参数都不要,带不带无所谓
  2. methods是固定名字。

案例-折叠面板

效果图

在这里插入图片描述

静态结构

<!DOCTYPE html>
<html lang="zh">
<head>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>html页面</title>
 <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
 <style>
   body{
     background-color: #eee;
   }
   #app{
     background-color:#fff;
     width: 500px;
     margin: 50px auto;
     box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);padding:2em;
    }
    .btn{
      float:right;
      cursor: pointer;
    }
   .box{
     padding: 1em;
     border:1px solid #ccc;
     margin:1em;
   }
   </style>
 </head>
 <body>
  <div id="app">
    <h2>案例:折叠面板</h2>
    <div class="box">
        <div class="title" >
          <span class="btn">收起 |展开</span>  
          <h4>我是标题</h4>
        </div>
        <div class="container">
            我是内容
            <br>
            我是内容
        </div>
    </div>
  </div>

   <script>
    var vm = new Vue({
        el: '#app',
        data: {
        }
    })
</script>
 </body>
</html>

分析

  • 使用一个bool值来记录当前的状态:展开,收起。
  • 用v-show 来隐藏和显示内容部分
  • 用插值表达式来决定按钮上的文案
  • 给按钮添加点击事件,并切换状态值

参考代码

<div id="app">
    <h2>案例:折叠面板</h2>
    <div class="box">
        <div class="title" >
            <h4>我是标题</h4><span class="btn" @click="isOpen=!isOpen">{{isOpen ? '收起' : '展开'}} </span>  
        </div>
        <div class="container" v-show="isOpen">
            我是内容
            <br>
            我是内容
        </div>
    </div>
</div>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            isOpen: false
        }
    })
</script>

事件修饰符

部分事件有副作用:例如a标签上的click会导致跳转;form中的button上的click会导致表单提交;子元素上的点击会冒泡;

上面的问题都可以借用事件修饰符来解决。

格式

<元素 v-on:事件名.修饰符1.修饰符2="回调">

作用

修饰符有具定的名字及作用。

  • .stop: 阻止冒泡
  • .prevent:阻止默认行为(a, form)
  • .once: 只执行一次
  • .self :当前元素自身时触发处理函数时才会触发函数

示例代码

<!DOCTYPE html>
<html lang="zh">
<head>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>html页面</title>
 <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
 <style>
   body{
     background-color: #eee;
   }
   #app{
     background-color:#fff;
     width: 500px;
     margin: 50px auto;
     box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);padding:2em;
    }
   .box{
     padding: 1em;
     border:1px solid #ccc;
     margin:1em;
   }
   </style>
 </head>
 <body>
   <div id="app">
     <h2>html页面</h2>
     <div v-on:click="hClickDiv">
       父元素
       <button v-on:click="hClickBtn">子元素</button>
       <button v-on:click.stop="hClickBtn">子元素-不会冒泡</button>
       <a href="http://www.baidu.com" v-on:click="hClickA">百度</a> 
       <a href="http://www.baidu.com" v-on:click.prevent="hClickA">百度-不会跳转</a> 
       <a href="http://www.baidu.com" v-on:click.stop.prevent="hClickA">百度-不会跳转-不会冒泡</a> 
       <button v-on:click.once="hClickBtn">子元素-它的动作只执行一次</button>
       <!-- <button v-on:click.stop.once="hClickBtn">子元素-它的动作只执行一次-不冒泡</button> -->

     </div>
     <hr>
     <div v-on:click.self="hClickDiv">
        父元素,只有在父元素点击才会执行,如果你是在子元素上点击,再冒泡到父元素,则不会执行
        <button v-on:click="hClickBtn">子元素</button>
      </div>
   </div>
   <script>
      // .stop: 阻止冒泡
      // .prevent:阻止默认行为
      // .once: 只执行一次
      // .self :当前元素自身时触发处理函数时才会触发函数

     // vue中的事件也有冒泡机制。
        // 在子元素上的点击事件,也会触发父元素上的点击事件。
        
        // 如果要阻止冒泡,只需给事件添加个修饰符.stop
        //     格式: 事件名.stop
        //     示例:click.stop

        // 如果要阻止默认行为,只需给事件添加个修饰符.prevent
        //     格式: 事件名.prevent
        //     示例:click.prevent

        // 如果需要,也可以一起使用,相当于两个效果叠加
        //     格式: 事件名.prevent.stop
        //     示例:click.prevent.stop

        // v-on: ----简写成---> @
      var vm = new Vue({
        el: '#app',
        methods: {
          hClickDiv () {
            console.log('点击了父元素 div')
          },
          hClickBtn () {
            console.log('点击了子元素 button')
          },
          hClickA () {
            alert('a')
          }
        }
      })
   </script>
 </body>
</html>

有些修饰符是可以同时生效的: @click.prevent.stop="hSubmit"

案例:购物车

素材代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        h4,
        p {
            margin: 0;
            font-weight: normal;
        }
        a{text-decoration: none;}

        body{
            background-color: #eee;
        }
        #app{
            background-color:#fff;
            width: 900px;
            margin: 50px auto;
            text-align: center;
            box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);
            padding:1em 2em;
        }

        .list {
            padding: 1em;
            width: 100%;
            box-sizing: border-box;
            display: flex;
            flex-wrap:wrap;
        }

        .book {
            position: relative;
            flex-basis: 22%;
            margin-right:2%;
            margin-bottom:1em;
            text-align: center;
            background-color: #fff;
            padding: 1em 0;
            transition: all .2s linear;
        }

        .book:hover {

            box-shadow: 0 15px 30px rgba(0, 0, 0, .1);
            transform: translate3d(0, -2px, 0);

        }

        .title {
            line-height: 2em;
            margin: 0;
            padding: 0;
        }

        .figure img {
            width: 150px;
            height: 150px;
        }

        .desc {
            margin: 0;
            font-size: 16px;
        }

        .price {
            margin: 0;
            font-size: 14px;
        }

        .btn {
            cursor: pointer;
            position: absolute;
            right: 0.8em;
            top: 0.8em;
            border: none;
            color: #ff6700;
            padding: .5em .5em;
            font-size: 12px;
        }

        .btn:active {
            border: none;
            user-select: none;
      			outline: 0 none !important;
        }

        .btn:hover {
            background-color: #ff6700;
            color: #fff
        }
        .selected {
      		border: 2px solid #ccc;
      		box-shadow: 0 15px 30px rgba(0, 0, 0, .3);
    		}

        .cart {
            border: 1px solid #eee;
            background-color: #fff;
            text-align: center;
            position: fixed;
            padding: 1em;
            right: 100px;
            top: 200px;
        }
    </style>
</head>

<body>
    <div id="app">
        <h3>我的购物车</h3>
        <div class="list">
            <div class="book" title="当你学会独处">
                <a href="">
                    <div class="figure">
                        <img src="http://img3m8.ddimg.cn/70/15/28522168-1_l_3.jpg" alt="当你学会独处(学会独处,学会与自己谈话)">
                    </div>
                    <h4 class="title" title="当你学会独处">当你学会独处</h4>
                    <p class="desc"></p>
                    <p class="price"><span class="num">29.9</span></p>
                </a>
                <button class='btn' title="移出购物车">-</button>
            </div>
            <div class="book" title="JavaScript高级程序设计">
                <a href="">
                    <div class="figure">
                        <img src="http://img3m3.ddimg.cn/2/21/22628333-1_b_2.jpg"
                            alt=" JavaScript高级程序设计">
                    </div>
                    <h4 class="title" title="JavaScript高级程序设计">JavaScript高级程序设计</h4>
                    <p class="desc"></p>
                    <p class="price"><span class="num">78</span></p>
                </a>
                <button class='btn' title="移出购物车">-</button>
            </div>
            <div class="book" title="断舍离">
                <a href="">
                    <div class="figure">
                        <img alt="断舍离" src="http://img3m3.ddimg.cn/68/20/23271503-1_l_10.jpg"
                            title="断舍离">
                    </div>
                    <h4 class="title" title="断舍离">断舍离</h4>
                    <p class="desc"></p>
                    <p class="price"><span class="num">29.9</span></p>
                </a>
                <button class='btn' title="移出购物车">-</button>
            </div>
            <div class="book" title="王子与贫儿">
                <a href="">
                    <div class="figure">
                        <img src="http://img3m0.ddimg.cn/67/20/28535530-1_l_3.jpg"
                            alt="王子与贫儿">
                    </div>
                    <h4 class="title" title="王子与贫儿">王子与贫儿</h4>
                    <p class="desc"></p>
                    <p class="price"><span class="num">39.9</span></p>
                </a>
                <button class='btn' title="移出购物车">-</button>
            </div>
            <div class="book" title="高效能人士的七个习惯">
                <a href="">
                    <div class="figure">
                        <img src="http://img3m2.ddimg.cn/3/4/25253022-1_b_11.jpg"
                            alt="高效能人士的七个习惯">
                    </div>
                    <h4 class="title" title="高效能人士的七个习惯">高效能人士的七个习惯</h4>
                    <p class="desc"></p>
                    <p class="price"><span class="num">59.3</span></p>
                </a>
                <button class='btn' title="移出购物车">-</button>
            </div>
        </div>
    </div>
    <script>
        var bookList = [
            {
                name: '当你学会独处',
                price: 29.9,
                picSrc: 'http://img3m8.ddimg.cn/70/15/28522168-1_l_3.jpg'
            },
            {
                name: 'JavaScript高级程序设计',
                price: 78,
                picSrc: 'http://img3m3.ddimg.cn/2/21/22628333-1_b_2.jpg'
            },
            {
                name: '断舍离',
                price: 29.9,
                picSrc: 'http://img3m3.ddimg.cn/68/20/23271503-1_l_10.jpg'
            },
            {
                name: '王子与贫儿',
                price: 39.9,
                picSrc: 'http://img3m0.ddimg.cn/67/20/28535530-1_l_3.jpg'
            },
            {
                name: '高效能人士的七个习惯',
                price: 59.3,
                picSrc: 'http://img3m2.ddimg.cn/3/4/25253022-1_b_11.jpg'
            }
        ]
    </script>
</body>

</html>

部分实现

v-for

v-if /v-else : 没有商品的内容提示

过滤器:给价格的前面加上 $

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        h4,
        p {
            margin: 0;
            font-weight: normal;
        }
        a{text-decoration: none;}

        body{
            background-color: #eee;
        }
        #app{
            background-color:#fff;
            width: 900px;
            margin: 50px auto;
            text-align: center;
            box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);
            padding:1em 2em;
        }

        .list {
            padding: 1em;
            width: 100%;
            box-sizing: border-box;
            display: flex;
            flex-wrap:wrap;
        }

        .book {
            position: relative;
            flex-basis: 22%;
            margin-right:2%;
            margin-bottom:1em;
            text-align: center;
            background-color: #fff;
            padding: 1em 0;
            transition: all .2s linear;
        }

        .book:hover {

            box-shadow: 0 15px 30px rgba(0, 0, 0, .1);
            transform: translate3d(0, -2px, 0);

        }

        .title {
            line-height: 2em;
            margin: 0;
            padding: 0;
        }

        .figure img {
            width: 150px;
            height: 150px;
        }

        .desc {
            margin: 0;
            font-size: 16px;
        }

        .price {
            margin: 0;
            font-size: 14px;
        }

        .btn {
            cursor: pointer;
            position: absolute;
            right: 0.8em;
            top: 0.8em;
            border: none;
            color: #ff6700;
            padding: .5em .5em;
            font-size: 12px;
        }

        .btn:active {
            border: none;
            user-select: none;
      			outline: 0 none !important;
        }

        .btn:hover {
            background-color: #ff6700;
            color: #fff
        }
        .selected {
      		border: 2px solid #ccc;
      		box-shadow: 0 15px 30px rgba(0, 0, 0, .3);
    		}

        .cart {
            border: 1px solid #eee;
            background-color: #fff;
            text-align: center;
            position: fixed;
            padding: 1em;
            right: 100px;
            top: 200px;
        }
    </style>
</head>

<body>
  <div id="app">
      <h3 title="哈哈哈我是一个h3">我的购物车</h3>
        <div class="list" v-if="bookList.length>0">
          <!-- title: 设置鼠标的悬浮提示 -->
          <!-- 格式:
            <元素 v-for="(循环变量,循环下标) in 可循环的目标"></元素> -->

            <!-- book <=== bookList[idx] -->
          <div class="book"
            :title="book.name"
            v-for="(book, idx) in bookList"
           >
            <a href="">
              <div class="figure">
                <!-- img标签的alt属性有什么用? 图片失败的提示文字  -->
                <img v-bind:src="book.picSrc" v-bind:alt="book.name">
              </div>
              <h4 class="title" :title="book.name">{{book.name}}</h4>
              <p class="price"><span class="num">{{book.price | fPrice}}</span></p>
            </a>
            <button class='btn' title="移出购物车">-</button>
          </div>
        </div>

        <div v-else>
          一件商品也没有,赶快去<a href="http://www.jd.com">购买</a></div>
  </div>
  <!-- 引入VUE核心js文件 -->
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>

    <script>
      // 目标:用vue来实现模板渲染
      // 需求 1. 如果一本书都没有,购物车是空的,应该要给一个提示(吸引用户去购买)
      // 实现 1. 用v-if, v-else来区分出两种情况
      //    坑点:不要在一个元素上同时使用v-if,v-for
      var bookList = [
          {
              name: '当你学会独处',
              price: 29.9,
              picSrc: 'http://img3m8.ddimg.cn/70/15/28522168-1_l_3.jpg'
          },
          {
              name: 'JavaScript高级程序设计',
              price: 78,
              picSrc: 'http://img3m3.ddimg.cn/2/21/22628333-1_b_2.jpg'
          },
          {
              name: '断舍离',
              price: 29.9,
              picSrc: 'http://img3m3.ddimg.cn/68/20/23271503-1_l_10.jpg'
          },
          {
              name: '王子与贫儿',
              price: 39.9,
              picSrc: 'http://img3m0.ddimg.cn/67/20/28535530-1_l_3.jpg'
          },
          {
              name: '高效能人士的七个习惯',
              price: 59.3,
              picSrc: 'http://img3m2.ddimg.cn/3/4/25253022-1_b_11.jpg'
          }
        ]
      new Vue({
        el: '#app', 
        data: {
          bookList: bookList
        },
        // 给一个数据包装一下,换下格式 ----》过滤器
        // 1. 本质是一个函数,值会自动传入
        // 2. 一定要有return , 就是返回之后的结果
        filters: {
          // 过滤器名: function (要过滤器的数据) {
          //   return 过滤之后的数据
          // }
          fPrice: function(val) {
            console.log('要过滤的值,它会自动被传入', val)
            return '¥' + val
          }
        }
      })
    </script>
</body>

</html>

实现删除

  • v-on:click绑定点击
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        h4,
        p {
            margin: 0;
            font-weight: normal;
        }
        a{text-decoration: none;}

        body{
            background-color: #eee;
        }
        #app{
            background-color:#fff;
            width: 900px;
            margin: 50px auto;
            text-align: center;
            box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);
            padding:1em 2em;
        }

        .list {
            padding: 1em;
            width: 100%;
            box-sizing: border-box;
            display: flex;
            flex-wrap:wrap;
        }

        .book {
            position: relative;
            flex-basis: 22%;
            margin-right:2%;
            margin-bottom:1em;
            text-align: center;
            background-color: #fff;
            padding: 1em 0;
            transition: all .2s linear;
        }

        .book:hover {

            box-shadow: 0 15px 30px rgba(0, 0, 0, .1);
            transform: translate3d(0, -2px, 0);

        }

        .title {
            line-height: 2em;
            margin: 0;
            padding: 0;
        }

        .figure img {
            width: 150px;
            height: 150px;
        }

        .desc {
            margin: 0;
            font-size: 16px;
        }

        .price {
            margin: 0;
            font-size: 14px;
        }

        .btn {
            cursor: pointer;
            position: absolute;
            right: 0.8em;
            top: 0.8em;
            border: none;
            color: #ff6700;
            padding: .5em .5em;
            font-size: 12px;
        }

        .btn:active {
            border: none;
            user-select: none;
      			outline: 0 none !important;
        }

        .btn:hover {
            background-color: #ff6700;
            color: #fff
        }
        .selected {
      		border: 2px solid #ccc;
      		box-shadow: 0 15px 30px rgba(0, 0, 0, .3);
    		}

        .cart {
            border: 1px solid #eee;
            background-color: #fff;
            text-align: center;
            position: fixed;
            padding: 1em;
            right: 100px;
            top: 200px;
        }
    </style>
</head>

<body>
  <div id="app">
      <h3 title="哈哈哈我是一个h3">我的购物车</h3>
        <div class="list" v-if="bookList.length>0">
          <!-- title: 设置鼠标的悬浮提示 -->
          <!-- 格式:
            <元素 v-for="(循环变量,循环下标) in 可循环的目标"></元素> -->

            <!-- book <=== bookList[idx] -->
          <div class="book"
            :title="book.name"
            v-for="(book, idx) in bookList"
           >
            <a href="">
              <div class="figure">
                <!-- img标签的alt属性有什么用? 图片失败的提示文字  -->
                <img v-bind:src="book.picSrc" v-bind:alt="book.name">
              </div>
              <h4 class="title" :title="book.name">{{book.name}}</h4>
              <p class="price"><span class="num">{{book.price | fPrice}}</span></p>
            </a>
            <button class='btn' title="移出购物车" @click="hRemove(idx)">-</button>
          </div>
        </div>

        <div v-else>
          一件商品也没有,赶快去<a href="http://www.jd.com">购买</a></div>
  </div>
  <!-- 引入VUE核心js文件 -->
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>

    <script>
      // 目标:用vue来实现模板渲染
      // 需求 1. 如果一本书都没有,购物车是空的,应该要给一个提示(吸引用户去购买)
      // 实现 1. 用v-if, v-else来区分出两种情况
      //    坑点:不要在一个元素上同时使用v-if,v-for
      var bookList = [
          {
              name: '当你学会独处',
              price: 29.9,
              picSrc: 'http://img3m8.ddimg.cn/70/15/28522168-1_l_3.jpg'
          },
          {
              name: 'JavaScript高级程序设计',
              price: 78,
              picSrc: 'http://img3m3.ddimg.cn/2/21/22628333-1_b_2.jpg'
          },
          {
              name: '断舍离',
              price: 29.9,
              picSrc: 'http://img3m3.ddimg.cn/68/20/23271503-1_l_10.jpg'
          },
          {
              name: '王子与贫儿',
              price: 39.9,
              picSrc: 'http://img3m0.ddimg.cn/67/20/28535530-1_l_3.jpg'
          },
          {
              name: '高效能人士的七个习惯',
              price: 59.3,
              picSrc: 'http://img3m2.ddimg.cn/3/4/25253022-1_b_11.jpg'
          }
        ]
      new Vue({
        el: '#app', 
        data: {
          bookList: bookList
        },
        // 给一个数据包装一下,换下格式 ----》过滤器
        // 1. 本质是一个函数,值会自动传入
        // 2. 一定要有return , 就是返回之后的结果
        filters: {
          // 过滤器名: function (要过滤器的数据) {
          //   return 过滤之后的数据
          // }
          fPrice: function(val) {
            console.log('要过滤的值,它会自动被传入', val)
            return '¥' + val
          }
        },
        methods: {
          // handler --> 处理。
          // 个人习惯是:如果这个方法是因为用户的交互而被直接调用的,我就会在取方法名时,加一个h
          
          // 基本业务流程:在用户删除,退出,清空 等这些操作之前,一般要先提示用户确认
          hRemove (idx) {
            // window.confirm("你确定要删除吗?") 确定是true 取消是false

            // if(window.confirm("你确定要删除吗?")) {
            //   console.log('你要删除的是下标为' + idx)
            //   // 问题:如何在数组中删除下标为idx那个元素?
            //   this.bookList.splice(idx,1)
            // }
            
            // 好处在于:1 减少了嵌套, 2. 尽早返回
            if(!window.confirm("你确定要删除吗?")) {
              return
            }
            console.log('你要删除的是下标为' + idx)
            // 问题:如何在数组中删除下标为idx那个元素?
            this.bookList.splice(idx,1)
          }
        }
      })
    </script>
</body>

</html>
  • 在删除之前要确认。
  • 注意: 1 减少了嵌套, 2. 尽早返回

计算总价

在这里插入图片描述

补充一个计算属性即可。

示例代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        h4,
        p {
            margin: 0;
            font-weight: normal;
        }
        a{text-decoration: none;}

        body{
            background-color: #eee;
        }
        #app{
            background-color:#fff;
            width: 900px;
            margin: 50px auto;
            text-align: center;
            box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);
            padding:1em 2em;
        }

        .list {
            padding: 1em;
            width: 100%;
            box-sizing: border-box;
            display: flex;
            flex-wrap:wrap;
        }

        .book {
            position: relative;
            flex-basis: 22%;
            margin-right:2%;
            margin-bottom:1em;
            text-align: center;
            background-color: #fff;
            padding: 1em 0;
            transition: all .2s linear;
        }

        .book:hover {

            box-shadow: 0 15px 30px rgba(0, 0, 0, .1);
            transform: translate3d(0, -2px, 0);

        }

        .title {
            line-height: 2em;
            margin: 0;
            padding: 0;
        }

        .figure img {
            width: 150px;
            height: 150px;
        }

        .desc {
            margin: 0;
            font-size: 16px;
        }

        .price {
            margin: 0;
            font-size: 14px;
        }

        .btn {
            cursor: pointer;
            position: absolute;
            right: 0.8em;
            top: 0.8em;
            border: none;
            color: #ff6700;
            padding: .5em .5em;
            font-size: 12px;
        }

        .btn:active {
            border: none;
            user-select: none;
      			outline: 0 none !important;
        }

        .btn:hover {
            background-color: #ff6700;
            color: #fff
        }
        .selected {
      		border: 2px solid #ccc;
      		box-shadow: 0 15px 30px rgba(0, 0, 0, .3);
    		}

        .cart {
            border: 1px solid #eee;
            background-color: #fff;
            text-align: center;
            position: fixed;
            padding: 1em;
            right: 100px;
            top: 200px;
        }
    </style>
</head>

<body>
  <div id="app">
      <h3 title="哈哈哈我是一个h3">我的购物车</h3>
        <div class="list" v-if="bookList.length>0">
          <!-- title: 设置鼠标的悬浮提示 -->
          <!-- 格式:
            <元素 v-for="(循环变量,循环下标) in 可循环的目标"></元素> -->

            <!-- book <=== bookList[idx] -->
          <div class="book"
            :title="book.name"
            v-for="(book, idx) in bookList"
           >
            <a href="">
              <div class="figure">
                <!-- img标签的alt属性有什么用? 图片失败的提示文字  -->
                <img v-bind:src="book.picSrc" v-bind:alt="book.name">
              </div>
              <h4 class="title" :title="book.name">{{book.name}}</h4>
              <p class="price"><span class="num">{{book.price | fPrice}}</span></p>
            </a>
            <button class='btn' title="移出购物车" @click="hRemove(idx)">-</button>
          </div>
        </div>

        <div v-else>
          一件商品也没有,赶快去<a href="http://www.jd.com">购买</a></div>

        <p>总价:{{ cTotalMoney }}</p>
  </div>
  <!-- 引入VUE核心js文件 -->
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>

    <script>
      // 目标:用vue来实现模板渲染
      // 需求 1. 如果一本书都没有,购物车是空的,应该要给一个提示(吸引用户去购买)
      // 实现 1. 用v-if, v-else来区分出两种情况
      //    坑点:不要在一个元素上同时使用v-if,v-for
      var bookList = [
          {
              name: '当你学会独处',
              price: 29.9,
              picSrc: 'http://img3m8.ddimg.cn/70/15/28522168-1_l_3.jpg'
          },
          {
              name: 'JavaScript高级程序设计',
              price: 78,
              picSrc: 'http://img3m3.ddimg.cn/2/21/22628333-1_b_2.jpg'
          },
          {
              name: '断舍离',
              price: 29.9,
              picSrc: 'http://img3m3.ddimg.cn/68/20/23271503-1_l_10.jpg'
          },
          {
              name: '王子与贫儿',
              price: 39.9,
              picSrc: 'http://img3m0.ddimg.cn/67/20/28535530-1_l_3.jpg'
          },
          {
              name: '高效能人士的七个习惯',
              price: 59.3,
              picSrc: 'http://img3m2.ddimg.cn/3/4/25253022-1_b_11.jpg'
          }
        ]
      new Vue({
        el: '#app', 
        data: {
          bookList: bookList
        },
        
        computed: {
          // 计算属性名 () {
          //   ..... 加工数据
          //   return xxxxx
          // }
          cTotalMoney () {
            console.log('cTotalMoney执行一次')
            // 1. 对数组bookList中的price进行求和
            let totalMoney = 0
            this.bookList.forEach( item =>{
              totalMoney += item.price
            })
            // 2. return
            return totalMoney.toFixed(2)
            // return this.bookList[0].price + this.bookList[1].price + this.bookList[2].price + this.bookList[3].price + this.bookList[4].price
          }
        },
        // 给一个数据包装一下,换下格式 ----》过滤器
        // 1. 本质是一个函数,值会自动传入
        // 2. 一定要有return , 就是返回之后的结果
        filters: {
          // 过滤器名: function (要过滤器的数据) {
          //   return 过滤之后的数据
          // }
          fPrice: function(val) {
            console.log('要过滤的值,它会自动被传入', val)
            return '¥' + val
          }
        },
        methods: {
          // handler --> 处理。
          // 个人习惯是:如果这个方法是因为用户的交互而被直接调用的,我就会在取方法名时,加一个h
          
          // 基本业务流程:在用户删除,退出,清空 等这些操作之前,一般要先提示用户确认
          hRemove (idx) {
            // window.confirm("你确定要删除吗?") 确定是true 取消是false

            // if(window.confirm("你确定要删除吗?")) {
            //   console.log('你要删除的是下标为' + idx)
            //   // 问题:如何在数组中删除下标为idx那个元素?
            //   this.bookList.splice(idx,1)
            // }
            
            // 好处在于:1 减少了嵌套, 2. 尽早返回
            if(!window.confirm("你确定要删除吗?")) {
              return
            }
            console.log('你要删除的是下标为' + idx)
            // 问题:如何在数组中删除下标为idx那个元素?
            this.bookList.splice(idx,1)
          }
        }
      })
    </script>
</body>

</html>

双向绑定 v-model

v-model是一个指令,用于获取用户的输入数据,并保存在vue的数据项中。

目标

知道如何绑定多种类型的表单元素

理解双向绑定

v-model一般是用在表单元素上:

  1. 可以把data中最初数据渲染到表单元素上。
  2. 用户在表单元素上的输入数据,会保存到data中的数据项。
<!DOCTYPE html>
<html lang="zh">
<head>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>html页面</title>
 <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
 <style>
   body{
     background-color: #eee;
   }
   #app{
     background-color:#fff;
     width: 500px;
     margin: 50px auto;
     box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);padding:2em;
    }
   .box{
     padding: 1em;
     border:1px solid #ccc;
     margin:1em;
   }
   </style>
 </head>
 <body>
   <div id="app">
     <h2>html页面</h2>
     <!-- <p>
       年龄:<input  value="28"/>
     </p> -->
     <p>
      年龄:<input v-bind:value="age1"/>(从数据到视图)
    </p>
    <p>
      年龄:<input v-model="age2"/>(1.从数据到视图. 2. 从视图到数据)
    </br>{{age2}}
    </p>
   </div>
   <script>
     
      var vm = new Vue({
        el: '#app',
        data: {
          age1: 18,
          age2: 28
        }
      })
   </script>
 </body>
</html>

v-model可以用来修改数据。

v-model在各类表单元素上的使用

  • 输入框 input
  • 文本域 textare
  • 复选框 checkbox
  • 单选框 radio
  • 下拉框 select
<!DOCTYPE html>
<html lang="zh">
<head>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>学习v-model在表单元素中的使用</title>
 <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
 <style>
   body{
     background-color: #eee;
   }
   #app{
     background-color:#fff;
     width: 500px;
     margin: 50px auto;
     box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);padding:2em;
    }
   .box{
     padding: 1em;
     border:1px solid #ccc;
     margin:1em;
   }
   </style>
 </head>
 <body>
   <div id="app">
     <h2>学习v-model在表单元素中的使用</h2>
      <div class="box">
          <!-- 1. 普通文本框  -->
          用户名:<input v-model="name"/>
      </div>

      <div class="box">
          <!-- 2. 单选  
          由value来决定用户选中的值 -->
          性别:
          <input type="radio" value="man" v-model="gender">帅哥
          <input type="radio" value="woman" v-model="gender">美女
      </div>

      <div class="box">
          <!-- 3. 复选  
          由value来决定用户选中的值,结果放在一个数组中 -->
          爱好:
          <input type="checkbox" value="读书" v-model="hobby">读书
          <input type="checkbox" value="sport" v-model="hobby">运动
          <input type="checkbox" value="k歌" v-model="hobby">k歌
      </div>

      <div class="box">
          <!-- 4. 文本域  
          注: 不能把内容写在元素中: 
          <textarea v-model="info">abc</textarea>
      -->
          个人说明:<textarea v-model="info"></textarea>
      </div>

      <div class="box">
      <!-- 5. 下拉
         选中的值是以value为准
      -->
          位置: 
          <select v-model="home">
              <option value="1">北京</option>
              <option value="2">上海</option>
              <option value="3">广州</option>
          </select>
      </div>

      <div class="box">
          <!-- 6. 单个复选框(是否同意)
              如果为true,则选中,false,不选中
          -->
          愿意和我一起吃早餐吗?<input type="checkbox" v-model="isAgree">
      </div>
      <button @click="hPrint">打印结果</button>
  </div>
  <script>
      // v-model 
      // 是用来对表单元素进行双向绑定的。
      // 1. 普通文本框 
      // 2. 单选
      // 3. 复选
      // 4. 文本域
      // 5. 下拉
      // 6. 单个复选框(是否同意)
      const vm = new Vue({
        el: "#app",
        data: {
          name: '张三',
          info: '法外狂徒!!!!',
          gender: 'woman',
          hobby: ['读书', 'k歌'],
          home: 3, // "1"> 北京  "2"  上海  "3">广州
          isAgree: false
        },
        methods: {
          hPrint () {
            console.log('名字', this.name)
            console.log('个人介绍', this.info)
            console.log('性别', this.gender)
            console.log('爱好', this.hobby)
            console.log('老家', this.home)
            console.log('是否同意', this.isAgree)
          }
        }
      })
      
  </script>
   </div>
 </body>
</html>

注意:

  • 表单元素radio(select)的value表示这个元素被选中后要绑定在v-model上的值。

  • 多选得到的结果放在一个数组中

v-model指令上的修饰符

如下三个修饰符,有各自的含义

  • lazy
  • number
  • trim

lazy

在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步。你可以添加 lazy 修饰符,从而转为在 change 事件_之后_进行同步:

<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg">

number

如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:

<input v-model.number="age" type="number">

这通常很有用,因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值。

trim

如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:

<input v-model.trim="msg">

参考代码

<!DOCTYPE html>
<html lang="zh">
<head>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>学习v-model上的修饰符</title>
 <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
 <style>
   body{
     background-color: #eee;
   }
   #app{
     background-color:#fff;
     width: 500px;
     margin: 50px auto;
     box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);padding:2em;
    }
   .box{
     padding: 1em;
     border:1px solid #ccc;
     margin:1em;
   }
   </style>
 </head>
 <body>
   <div id="app">
     <h2>学习v-model上的修饰符</h2>
      姓名:<input v-model.trim="name" /> 
      <br>
      工资:<input v-model.number="salary" />
      <br>
      奖金:<input v-model.number="bonus" />
      <br>
      交补:<input v-model.number.lazy="allowance" />
      <br>
      合计:{{ bonus + salary + allowance }}
      <br>
      合计:{{ cTotalMoney }}
   </div>
   <script>
      // - lazy: 在输入框中值输入内容时,数据不会立即更新,而是在输入框失去焦点时,去更新数据。
      // - number: 确保内容是数值
      // - trim: 去掉内容当中首尾空格 "   curry  " ---> "curry"
      var vm = new Vue({
        el: '#app',
        data: {
          name: 'curry',
          salary: 10000,
          bonus: 2* 10000,
          allowance: 200
        },
        computed: {
          cTotalMoney () {
            return this.salary + this.bonus + this.allowance
          }
        }
      })
   </script>
 </body>
</html>

案例-资产管理列表

  • 数据展示
  • 计算属性
  • 过滤器
  • 数据筛选
  • v-model双向绑定

在这里插入图片描述

渲染列表

目标:完成表格列表渲染。

需求:

  • 根据data中的数据,进行tr的渲染。
  • 当没有数据的时候,显示暂无数据。

静态页面:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
  <div id="app">
    <div class="container">
      <table class="table table-bordered table-hover">
        <thead>
          <tr>
            <th>编号</th>
            <th>资产名称</th>
            <th>创建时间</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>1</td>
            <td>宝马</td>
            <td>20000</td>
            <td><a href="#">删除</a></td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</body>
</html>

准备数据:

list: [
    { id: 1, name: '外套', price: 99 },
    { id: 2, name: '裤子', price: 34 },
    { id: 3, name: '鞋', price: 25.4 },
    { id: 4, name: '头发', price: 19900 }
]

完整的功能实现:

  <div id="app">
    <div class="container">
      <table class="table table-bordered table-hover">
        <thead>
          <tr>
            <th>编号</th>
            <th>资产名称</th>
            <th>创建时间</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <!-- <tr v-for="(循环变量,索引变量) in 数据"> -->
          <tr v-for="(item, idx) in list">
            <td>{{item.id}}</td>
            <td>{{item.name}}</td>
            <!-- 如果价格超过100,就有red这个类 -->
            <!-- <td class="red">{{item.price}}</td> -->

            <!-- 三元
            -->
            <!-- <td :class='item.price > 100 ? "red" : ""'>{{item.price}}</td> -->
            
            <!-- 放一个对象
               如果对象中的属性值是true,
               则把对象中的属性名添加到类名中
            -->
            <td :class="{red:item.price>100}">{{item.price}}</td>
           
            <td><a href="#" @click.prevent="hDel(idx)">删除</a></td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
  <script src="./vue.js"></script>
  <script>
    const vm = new Vue({
      el: '#app',
      data: {
        list: [
            { id: 1, name: '外套', price: 99 },
            { id: 2, name: '裤子', price: 34 },
            { id: 3, name: '鞋', price: 25.4 },
            { id: 4, name: '头发', price: 19900 }
        ]
      }
    })
  </script>

删除功能

目标:完成表格一行删除。

需求:

  • 点击删除按钮,确认框提示
  • 点击确认后,再进行删除
  • 绑定事件,指定处理函数的时候,传入ID
  • 在处理函数中,根据ID进行删除(id===>index====>根据索引删除)

实现:

<td><a @click.prevent="hDel(idx)" href="#">删除</a></td>
methods: {
    hDel (index) {
            console.log('要删除的下标是',index)
            // 删除数组中指定位置的元素
            this.list.splice(index, 1)
          }
}

添加资产

目标:完成表格添加资产。

大致的实现步骤:

  1. 准备一个表单,包含一个输入框,包含一个提交按钮。
  2. 监听提交事件,提取输入框中的内容
  3. 根据输入的内容来组织 资产信息对象 追加到数组中即可
    1. 数据会驱动视图的更新
  4. 清空输入框中的内容

在案例中的代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
  <div id="app">
    <div class="container">
      <table class="table table-bordered table-hover">
        <thead>
          <tr>
            <th>编号</th>
            <th>资产名称</th>
            <th>价值</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody v-if="list.length">
          <tr v-for="(item,idx) in list">
            <td>{{idx+1}}</td>
            <td>{{item.name}}</td>
            <td>{{item.price}}</td>
            <td><a href="#" @click.prevent="hDel(idx)">删除</a></td>
          </tr>
        </tbody>
        <tbody v-else>
          <tr>
            <td colspan="4">没有数据!</td>
          </tr>
        </tbody>
      </table>

      <!-- 添加资产 -->
      <form class="form-inline">
        <div class="form-group">
            <div class="input-group">
              <input type="text" v-model.trim="asset.name" class="form-control" placeholder="资产名称">
            </div>
            <div class="input-group">
              <input type="text" v-model.trim.number="asset.price" class="form-control" placeholder="价值">

            </div>
       
            <button class="btn btn-primary" @click.prevent="hAdd">添加资产</button>
        </div>
      </form>
    </div>
  </div>

 <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>

  <script>
    // 1. v-if, v-else. 是否显示表格内容,或者是提示。 
    // 2. v-for. 循环列表
    // 3. {{ }} 显示数据
    // 4. @click.prevent
    const vm = new Vue({
      el: '#app',
      data:{
        list: [
          { id: 1, name: '外套', price: 99 },
          { id: 2, name: '裤子', price: 34 },
          { id: 3, name: '鞋子', price: 25.4 },
          { id: 4, name: '手机', price: 1299 },
          { id: 5, name: '头发', price: 19900 },
          { id: 6, name: '电脑', price: 15500 }
        ],
        asset: {
          name: '本子',
          price: '2'
        }
      },
      methods: {
        // 1. 命名: h+有意义的动词。不要随意!不要随意!不要随意!
        // 2. 做删除,要询问
        hDel (idx) {
          // alert('a!!!!')
          if(confirm(`你真的要删除${this.list[idx].name}吗?`)){
            this.list.splice(idx, 1)
          }
        },
        // 1. 添加之前一定要判断。
        // 2. 如果数据项比较多,可以装一个对象中
        hAdd () {
          const { name, price } = this.asset
          // 1. 判断一下
          console.log(name)
          console.log(price)
          if(name === '') {
            return
          }

          if(price === '') {
            return
          }

          // 2. 添加到list中去
          // TODO作用: 要求id是自动增长的: 每一次添加新的资产,其id都是最后一个元素的id+1.
          this.list.push({
            id: 1,  // 作用: 要求id是自动增长的: 每一次添加新的资产,其id都是最后一个元素的id+1.
            name,
            price,
          })
        }
      }
    })
  </script>
</body>
</html>

用过滤器完成货币格式化显示

注意:下面的代码来自vue1中的源码。在vue1中提供了货币金额显示格式化的过滤器currency,请在这里https://cdn.bootcdn.net/ajax/libs/vue/1.0.11/vue.js搜索currency关键字。

在这里插入图片描述

filters: {
    $:function(value, _currency) {
        // 全局匹配: 三位连续,且之后也是数值 的数值
        var digitsRE = /(\d{3})(?=\d)/g;
        value = parseFloat(value);
        if (!isFinite(value) || !value && value !== 0) return '';
        _currency = _currency != null ? _currency : '¥';
        // 保留两位小数,并整体转成字符串
        var stringified = Math.abs(value).toFixed(2);

        // 获取整数部分。-3表示倒数3位的位置
        var _int = stringified.slice(0, -3);

        // 整数部分以3为基准长度划分,剩下几位
        var i = _int.length % 3;
        // 取出头部。
        // 如:12345 ----> 12,
        var head = i > 0 ? _int.slice(0, i) + (_int.length > 3 ? ',' : '') : '';
        // 取出小数点部分
        // 如:12345.67 ----> .67

        var _float = stringified.slice(-3);
        var sign = value < 0 ? '-' : '';
        return _currency + sign + head + _int.slice(i).replace(digitsRE, '$1,') + _float;
    }
}

筛选功能

在这里插入图片描述

输入框结构:

      <!-- 搜索 -->
      <form class="form-inline" style="padding: 20px 0">
        <input type="text" class="form-control" placeholder="输入关键字进行搜索">
      </form>
      <!-- 表格 -->

参考代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
  <div id="app">
    <div class="container">
      <!-- 搜索 -->
      <form class="form-inline" style="padding: 20px 0">
        搜索:<input v-model.trim="keyword" type="text" class="form-control" placeholder="输入关键字进行搜索">
      </form>


      <!-- 表格 -->
      <table class="table table-bordered table-hover">
        <thead>
          <tr>
            <th>编号</th>
            <th>资产名称</th>
            <th>价值</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody v-if="cFilterList.length">
          <tr v-for="(item,idx) in cFilterList">
            <td>{{idx+1}}</td>
            <td>{{item.name}}</td>
            <td>{{item.price | fCurrency}}</td>
            <td><a href="#" @click.prevent="hDel(idx)">删除</a></td>
          </tr>
        </tbody>
        <tbody v-else>
          <tr>
            <td colspan="4">没有数据!</td>
          </tr>
        </tbody>
        <tfoot>
          <tr>
            <td colspan="4">汇总:{{cTotalMoney | fCurrency}}</td>
          </tr>
        </tfoot>
        
      </table>

      <!-- 添加资产 -->
      <form class="form-inline">
        <div class="form-group">
            <div class="input-group">
              <input type="text" v-model.trim="asset.name" class="form-control" placeholder="资产名称">
            </div>
            <div class="input-group">
              <input type="text" v-model.trim.number="asset.price" class="form-control" placeholder="价值">
            </div>
       
            <button class="btn btn-primary" @click.prevent="hAdd">添加资产</button>
        </div>
      </form>
    </div>
  </div>

 <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>

  <script>
    // 1. v-if, v-else. 是否显示表格内容,或者是提示。 
    // 2. v-for. 循环列表
    // 3. {{ }} 显示数据
    // 4. @click.prevent
    // 5. 计算属性算总价
    // 6. 过滤器,处理货币金额
    // 7. 列表数据筛选
    //    通过设计计算属性,让它来决定表格区域的数据。
    const vm = new Vue({
      el: '#app',
      data:{
        keyword: '', // 搜索关键字
        list: [
          { id: 1, name: '外套', price: 99 },
          { id: 2, name: '裤子', price: 34 },
          { id: 3, name: '鞋子', price: 25.4 },
          { id: 4, name: '手机', price: 1299 },
          { id: 5, name: '头发', price: 19900 },
          { id: 6, name: '电脑', price: 15500 }
        ],
        asset: {
          name: '本子',
          price: '2'
        }
      },
      computed: {
        // 根据关键字和 list 进行计算,得到新的数据列表
        // 计算的规则是:list中的某一项,如果它的name中包含了搜索关键字,则保留下来
        cFilterList () {
          if(this.keyword === ''){
            return this.list
          }

          const rs = this.list.filter(item => {
            return item.name.includes(this.keyword)
          })
          return rs
        },
        cTotalMoney () {
          let m = 0
          // 在一个计算属性中访问另一个计算属性
          this.cFilterList.forEach(item => m+= Number(item.price))
          return m
        }
      },
      filters: {
        fCurrency:function(value, _currency) {
          // 全局匹配: 三位连续,且之后也是数值 的数值
          var digitsRE = /(\d{3})(?=\d)/g;
          value = parseFloat(value);
          if (!isFinite(value) || !value && value !== 0) return '';
          _currency = _currency != null ? _currency : '¥';
          // 保留两位小数,并整体转成字符串
          var stringified = Math.abs(value).toFixed(2);

          // 获取整数部分。-3表示倒数3位的位置
          var _int = stringified.slice(0, -3);

          // 整数部分以3为基准长度划分,剩下几位
          var i = _int.length % 3;
          // 取出头部。
          // 如:12345 ----> 12,
          var head = i > 0 ? _int.slice(0, i) + (_int.length > 3 ? ',' : '') : '';
          // 取出小数点部分
          // 如:12345.67 ----> .67

          var _float = stringified.slice(-3);
          var sign = value < 0 ? '-' : '';
          return _currency + sign + head + _int.slice(i).replace(digitsRE, '$1,') + _float;
        }
      },
      methods: {
        // 1. 命名: h+有意义的动词。不要随意!不要随意!不要随意!
        // 2. 做删除,要询问
        hDel (idx) {
          // alert('a!!!!')
          if(confirm(`你真的要删除${this.list[idx].name}吗?`)){
            this.list.splice(idx, 1)
          }
        },
        // 1. 添加之前一定要判断。
        // 2. 如果数据项比较多,可以装一个对象中
        hAdd () {
          const { name, price } = this.asset
          // 1. 判断一下
          console.log(name)
          console.log(price)
          if(name === '') {
            return
          }

          if(price === '') {
            return
          }

          // 2. 添加到list中去
          // TODO作业: 要求id是自动增长的: 每一次添加新的资产,其id都是最后一个元素的id+1.
          this.list.push({
            id: 1,  // 作业: 要求id是自动增长的: 每一次添加新的资产,其id都是最后一个元素的id+1.
            name,
            price,
          })
        }
      }
    })
  </script>
</body>
</html>

v-if和v-for不要一起用

原因:v-for的优先级要比v-if高,导致在代码执行的过程中,每个v-if都是嵌套在循环内部的,这样会导致执行效率低。

在这里插入图片描述

另外,如果v-if后面来还跟着v-else,则v-else也会被嵌入到v-for的内部。

在这里插入图片描述

可以用 vm.$options.render 输出当前模板对应的渲染函数,从渲染函数中可以验证上面的结论。

<div id="app">
    <div v-if="isOk" v-for="i in 3">div</div>
    <span v-else>span</span>
  </div>

  <!-- <div id="app">
      <div v-if="isOk">
        <div v-for="i in 3">div</div>
      </div>
  </div> -->

  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        isOk: false
      }
    })
    // vm.$options.render: 输出模板对应的 渲染函数
// ƒ anonymous() {
//  with(this){return _c('div',{attrs:{"id":"app"}},_l((3),function(i){return (isOk)?_c('div',[_v("div")]):_e()}),0)}
// }
    console.log(vm.$options.render)
  </script>

解决方法:在v-for的父元素上使用v-if.

自定义指令

vue的指令用来增加元素的功能,如果你对现有的指令不满意,还可以自己定制指令。

指令(directive)vue提供了v-开头的特殊属性,称之为指令(v-for, v-if, v-else, v-else-if,v-show,v-bind, v-on, v-text,v-html,v-model)。它提供的指令是有限,如果遇见内置指令无法给你实现的功能,自己封装一个指令(自定义指令)。

格式

定义

// 局部指令
directives: {
   指令名: {
     // 这个函数会自动被调用
     inserted (el) {
       // el会指向使用这个指令的元素
     }
   }
}

使用

<元素 v-指令名 >

示例

定义一个v-focus的指令,作用让input自动获取焦点

var vm = new Vue({
    el: '#app',

    // directives: {
    //   focus: {
    //     // 这个函数会自动被调用
    //     inserted (el) {
    //       el.style.height= "200px"
    //       // el.focus()
    //       console.log('inserted', el)
    //     }
    //   }
    // }
})

如有不足,请多指教,
未完待续,持续更新!
大家一起进步!

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

東三城

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

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

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

打赏作者

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

抵扣说明:

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

余额充值