Vue语法基础

模板语法

插值:mustache语法

数据绑定最常见的形式就是使用Mustache语法 (双大括号) 的文本插值

<span>Message: {{ msg }}</span>
<span>Message: {{ Firstname+lastName }}</span>

插值表达式,可以是一个变量,也可以是一个表达式,可以进行简单的计算

但一般不推荐这么干,Vue中有更优雅的实现方式

v-once

<div id=app>
  <h1 v-once>{{message}}</h1>
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue'
    }
  })
</script>

v-once修饰后,{{message}}只会在初始化获取一次值,后续message的变化,不会影响dom中的数据展示

v-bind

有了插值表达式,可以让dom标签的内容实现动态化,但是我们想让dom标签的属性动态化该怎么办?

我们使用v-bind,将dom标签的属性进行绑定,实现动态

<div id=app>
  <img src="imgUrl" alt="测试">
  <img v-bind:src="imgUrl" alt="测试">
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      imgUrl: 'http://tuchuang.zhangln.com/xbVhJM.png'
    }
  })
</script>

实际被浏览器解析的html如下

因为使用了v-bind指令,src的值可以动data中获取了

  • 缩写

<img v-bind:src="imgUrl" alt="测试">可以缩写成<img :src="imgUrl" alt="测试">

v-bind是可以省略的,保留:即可

案例:v-bind绑定class

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <title>Document</title>
  <style>
    .active1 {
      color: red;
    }

    .active2 {
      color: gold;
    }
  </style>
</head>
<body>
<div id=app>
  <h2 :class="active">{{message}}</h2>
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue',
      active: 'active1'
    }
  })
</script>
</body>
</html>

通过修改active的值,动态的将不同的样式绑定到了h2标签

  • 对象语法
<div id=app>
  <h2 :class="{active1:isActive1,active2:isActive2}">{{message}}</h2>
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue',
      isActive1: true,
      isActive2: false
    }
  })
</script>

active1active2是两个样式,通过isActive1和isActive2的布尔值,确定是否需要将对应的样式添加上来。

如果一次性将一个json对象添加在class上,代码有点复杂,我们可以使用计算属性(computed,后续讲到)或methods中

<div id=app>
  <h2 :class="getClass()">{{message}}</h2>
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue',
      isActive1: true,
      isActive2: false
    },
    methods: {
      getClass: function () {
        return {active1: this.isActive1, active2: this.isActive2};
      }
    }
  })
</script>

当我们的需求中,需要对一个元素不停的切换样式的时候,就可以使用这种办法。

  • 数组语法

作用和对象语法一样,用的比较少,我们来简单做个演示

<div id=app>
  <h2 :class="classes">{{message}}</h2>
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue',
      classes:['active1','active2']
    }
  })
</script>

相当于把两个样式都绑定给了标签

与对象语法一样,数组语法也可以使用计算属性或methods获取

案例:v-bind结合v-for实现一个小需求

需求:在用v-for生成的列表中,点击哪个元素,哪个元素就变色

<body>
<div id=app>
  <ul>
    <li v-for="(item,index) in movies" @click="changeClass(index)" :class="getClass(index)">{{index}} - {{item}}</li>
  </ul>
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      movies: ['海王', '海尔兄弟', '火影忍者', '进击的巨人'],
      isActive: [false, false, false, false]
    },
    methods: {
      getClass: function (index) {
        console.log('计算' + index + '的class');
        return {active1: this.isActive[index]};
      },
      changeClass: function (index) {
        console.log('点击了' + index);
        console.log('当前配置:' + this.isActive);
        //数组的深拷贝
        let newIsActive = JSON.parse(JSON.stringify(this.isActive));
        for (let i = 0; i < newIsActive.length; i++) {
          newIsActive[i] = i == index ? true : false;
        }
        console.log('新的配置:' + newIsActive);
        this.isActive = newIsActive;
      }
    }
  })
</script>
</body>
  • 更简洁的写法
<div id=app>
  <ul>
    <li v-for="(item,index) in movies" @click="changeClass(index)" :class="getClass(index)">{{index}} - {{item}}</li>
  </ul>
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      movies: ['海王', '海尔兄弟', '火影忍者', '进击的巨人'],
      currentIndex: -1
    },
    methods: {
      getClass: function (index) {
        return index == this.currentIndex ? {active1: true} : {active1: false};
      },
      changeClass: function (index) {
        this.currentIndex = index;
      }
    }
  })
</script>

案例:v-bind绑定style

  • 对象语法
<h2 :style="{fontSize:'50px'}">{{message}}</h2>
<div id=app>
  <h2 :style="{fontSize:finalSize+'px'}">{{message}}</h2>
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue',
      finalSize: 50
    }
  })
</script>

觉得style属性太长了的话,可以抽取成方法

<div id=app>
  <h2 :style="getStyles()">{{message}}</h2>
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue',
      finalSize: 50
    },
    methods: {
      getStyles: function () {
        return {fontSize: this.finalSize + 'px'};
      }
    }
  })
</script>
  • 数组语法
<div id=app>
  <h2 :style="[style1,style2]">{{message}}</h2>
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue',
      style1: {fontSize: this.finalSize + 'px'},
      style2: {backgroundColor: 'red'},
    }
  })
</script>

这个数组语法,就和绑定class的数组语法非常像了,只不过class的时候,每个数组元素是一个class样式,这里的每个数组元素是一个对象类型的内联样式

计算属性与侦听器

计算属性:computed

<body>
<div id=app>{{fullName}}</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      firstName: '张',
      lastName: '三'
    },
    computed: {
      fullName: function () {
        return this.firstName + this.lastName;
      }
    }
  })
</script>

当插值表达式中的结果计算逻辑比较复杂的时候,就可以使用计算属性

计算属性的setter和getter方法

每个计算属性其实都包含了一个setter和getter方法

上例中,我们就使用了getter方法来进行读取,有时候也可以提供setter方法进行设置

<div id=app>{{fullName}}</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      firstName: '张',
      lastName: '三'
    },
    computed: {
      fullName: {
        get() {
          console.log("调用了get");
          return this.firstName + " " + this.lastName;
        },
        set(newValue) {
          console.log("调用了set");
          const names = newValue.split(" ");
          this.firstName = names[0];
          this.lastName = names[1];
        }
      }
    }
  })
</script>

setter方法会在设置fullName值的时候被调用

计算属性和methods对比

  • 计算属性会监控所依赖的数据的值是否发生了变化,只有变化了,才会执行计算
  • methods每次模板编译都会执行。只要有响应式属性改变,视图刷新,函数就执行

侦听器:watch

虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

<div id=app>{{fullName}}</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      firstName: '张',
      lastName: '三',
      fullName: ''
    },
    watch: {
        //如果firstName发生变化,则函数执行
      firstName() {
        this.fullName = this.firstName + this.lastName;
      }
    }
  })
</script>

Class和Style绑定

条件渲染

v-ifv-else-ifv-else

<div id=app>
  <div v-if="isA">A</div>
  <div v-else-if="isB">B</div>
  <div v-else>C</div>
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      isA: true,
      isB: false
    }
  })
</script>

案例:切换登陆方式

<div id=app>
  <div v-if="showUserName">
    <input type="text" placeholder="用户名..." key="u1">
    <button>登陆</button>
  </div>
  <div v-else-if="showEmail">
    <input type="text" placeholder="邮箱..." key="u2">
    <button>登陆</button>
  </div>
  <button @click="changeLoginWay">切换</button>
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      showUserName: true,
      showEmail: false
    },
    methods: {
      changeLoginWay() {
        this.showUserName = !this.showUserName;
        this.showEmail = !this.showEmail;
      }
    }

  })
</script>

这里为两个input设置了key,并且设置key的值不同。

是为了不想让Vue复用input。

这里的原理性解释涉及到Vue的虚拟DOM,后续再说

v-show

<div id=app>
  <h2 v-show="isShow">{{message}}</h2>
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue',
      isShow: false
    }
  })
</script>
  • v-show与v-if比较

与v-if类似,v-show也可以用于决定元素的显示与否,但是实现形式是不同的。

v-if是对dom的操作,是在增删dom节点,v-show只是操作

<h2 style="display: none;">Hello Vue</h2>

为元素设置一下内联样式而已

当页面元素显示与隐藏频繁切换的时候,建议使用v-show,如果是一次性操作,则选择使用v-if

列表渲染

v-for

<div id=app>
  <ul>
    <li v-for="item in names">{{item}}</li>
  </ul>
</div>

<script>
  let app = new Vue({
    el: '#app',
    data: {
      names: ['中国', '美国', '英国']
    }
  })
</script>
  • 获取索引值
<li v-for="(item,index) in names">{{index}} - {{item}}</li>
  • 遍历对象
<div id=app>
  <ul>
    <li v-for="(item,key) in info">{{key}} - {{item}}</li>
  </ul>
</div>

<script>
  let app = new Vue({
    el: '#app',
    data: {
      info: {
        name: 'why',
        age: 18,
        height: 1.88
      }
    }
  })
</script>

key就是对象的属性名,item就是对象属性的值

:key属性

官方推荐,使用v-for的时候,为对应的元素或组件添加上一个:key属性,

为节点做一个唯一标识。

在Vue虚拟DOM的Diff算法中会用到。

可以帮助更高效的更新虚拟DOM

<li v-for="(item,index) in names" :key="item">{{key}} - {{item}}</li>

注意,不要用index作为:key的值

事件处理

监听事件:v-on:

v-on:可以简写为@

<div id="example-2">
  <!-- `greet` 是在下面定义的方法名 -->
  <button v-on:click="greet">Greet</button>
</div>

<script>
    var example2 = new Vue({
      el: '#example-2',
      data: {
        name: 'Vue.js'
      },
      // 在 `methods` 对象中定义方法
      methods: {
          //可以简写为greet(){......}
        greet: function (event) {
          // `this` 在方法里指向当前 Vue 实例
          alert('Hello ' + this.name + '!')
          // `event` 是原生 DOM 事件
          if (event) {
            alert(event.target.tagName)
          }
        }
      }
    })
</script>
  • 获取事件参数

上述案例中,我们没有传递event参数,但是function中是可以使用的

可是,如果我们需要传递普通参数,同时也要获取event,那该怎么办?

答案:greet(args,$event)

冒泡问题:.stop修饰符

<div id=app>
  <div @click="divClick">
    哇哈哈
    <button @click="btnClick">点我呀</button>
  </div>
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue'
    },
    methods: {
      divClick() {
        console.log("div被点击了");
      },
      btnClick() {
        console.log("button被点击了");
      }
    }
  })
</script>

在这段代码中,如果点击了button,div上的click也是会被触发的,怎么办呢?

答案:添加.stop修饰符:<button @click.stop="btnClick">点我呀</button>

除了.stop修饰符外,还有很多修饰符可以支持

阻止默认事件.prevent修饰符

比如对于submit类型的input,默认是会提交表单的

<div id=app>
  <form>
    <input type="submit" value="提交" @click.prevent="submitClick">
  </form>
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue'
    },
    methods: {
      divClick() {
        console.log("div被点击了");
      },
      btnClick() {
        console.log("button被点击了");
      },
      submitClick() {
        console.log("点击提交");
      }
    }
  })
</script>

用了修饰符后,就不会自动提交表单了

keyup/keydown事件与.enter修饰符

监听按键按下与弹起,

<div id=app>
  <input type="text" @keyup="keyClickUp">
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue'
    },
    methods: {
      divClick() {
        console.log("div被点击了");
      },
      btnClick() {
        console.log("button被点击了");
      },
      submitClick() {
        console.log("点击提交");
      },
      keyClickUp() {
        console.log("点击了按键");
      }
    }
  })
</script>

如果只想监听回车键,则

<input type="text" @keyup.enter="keyClickUp">

还可以设置要监听的键的代码,如<input type="text" @keyup.13="keyClickUp">

.once修饰符

<button @click.once="btnClick">按钮</button>

事件仅被触发一次

表单输入绑定

v-model

你可以用 v-model 指令在表单 <input><textarea><select> 元素上创建双向数据绑定。

<div id=app>
  输入:<input type="text" v-model="message">
  <br/>
  输出:<input type="text" :value="message">
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      message: ''
    }
  })
</script>

在输入中输入的数据,绑定到message中,message的变化,又反馈到输出中。

最终效果就是输入内容同步显示在输出中。

双向绑定,我们可以这么理解:以前都是template从Vue对象中取数据,现在可以在template中将数据绑定到Vue对象中。同时,如果message对象由于其他因素被改变,输入中也是会随之改变的。

绑定radio

<div id=app>
    <!--可以省略name="sex"-->
  <input type="radio" id="male" name="sex" value="" v-model="sex"><input type="radio" id="female" name="sex" value="" v-model="sex"><hr>
  选中:{{sex}}
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      sex: ''
    }
  })
</script>

绑定checkbox

  • 单选框
<div id=app>
  <label>
    <input type="checkbox" id="agree" v-model="isAgree">同意协议
  </label>
  <hr>
  选中结果:{{isAgree}}
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      isAgree: false
    }
  })
</script>
  • 多选框
<div id=app>
  <label>
    <input type="checkbox" value="足球" v-model="hobbies">足球
    <input type="checkbox" value="篮球" v-model="hobbies">篮球
    <input type="checkbox" value="排球" v-model="hobbies">排球
    <input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球
    <input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球
  </label>
  <hr>
  选中结果:{{hobbies}}
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      hobbies:[]
    }
  })
</script>

绑定select

  • 单选
<div id=app>
  <label>
    <select name="abc" id="" v-model="fruit">
      <option value="苹果">苹果</option>
      <option value="香蕉">香蕉</option>
      <option value="榴莲">榴莲</option>
      <option value="葡萄">葡萄</option>
    </select>
  </label>
  <hr>
  选中结果:{{fruit}}
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      fruit: '香蕉'
    }
  })
</script>
  • 多选
<div id=app>
  <label>
    <select name="abc" v-model="fruits" multiple>
      <option value="苹果">苹果</option>
      <option value="香蕉">香蕉</option>
      <option value="榴莲">榴莲</option>
      <option value="葡萄">葡萄</option>
    </select>
  </label>
  <hr>
  选中结果:{{fruit}}
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      fruit: ['']
    }
  })
</script>

修饰符

  • lazy
输入:<input type="text" v-model.lazy="message">

这样一来,就只有按回车键或光离开输入框后,才会触发绑定。

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

限定输入必须是数字

  • trim
<input type="text" v-model.trim="message">

删除输入中左右两边的空格,这个就非常实用了

购物车实战

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <title>Document</title>
</head>
<body>
<div id=app>
  <table border="1">
    <thead>
    <tr>
      <th>序号</th>
      <th>书籍名称</th>
      <th>出版日期</th>
      <th>价格</th>
      <th>购买数量</th>
      <th>操作</th>
    </tr>
    </thead>
    <tbody>
    <tr v-for="(item,index) in books">
      <td>{{item.id}}</td>
      <td>{{item.name}}</td>
      <td>{{item.date}}</td>
      <td>{{item.price | showPrice}}</td>
      <td>
        <button @click="decrement(item.id)">1</button>
        {{item.count}}
        <button @click="increment(item.id)">+</button>
      </td>
      <td>
        <button @click="removeThisBook(item.id)">移除</button>
      </td>
    </tr>
    </tbody>
  </table>
  <h2>总价:{{totalPrice | showPrice}}</h2>
</div>
<script>
  let app = new Vue({
    el: '#app',
    data: {
      books: [
        {id: 1, name: '《算法导论》', date: '2006-9', price: 85.9, count: 1},
        {id: 2, name: '《UNIX编程艺术》', date: '2006-2', price: 59, count: 1},
        {id: 3, name: '《编程珠玑》', date: '2006-1', price: 39, count: 1},
        {id: 4, name: '《代码大全》', date: '2006-10', price: 120, count: 1}
      ]
    },
    computed: {
      //总价
      totalPrice() {
			//使用reduce汇总计算
        return this.books.reduce((preValue, book) => {
          return preValue += book.price * book.count;
        }, 0);
      }
    },
    methods: {
      //删除指定id的书籍
      removeThisBook(id) {
        console.log("移除id:" + id);
        for (let i = 0; i < this.books.length; i++) {
            //获取对应id在数组中的索引
          if (this.books[i].id === id) {
            //删除选中的元素
            this.books.splice(i, 1);
          }
        }
      },
      //加减书籍数量
      decrement(id) {
        //当前想要操作的书
        for (let i = 0; i < this.books.length; i++) {
          if (this.books[i].id === id) {
            if (this.books[i].count == 0) {
              alert("不能再减少了");
            } else {
              this.books[i].count--;
            }
            break;
          }
        }
      },
      increment(id) {
        //当前想要操作的书
        for (let i = 0; i < this.books.length; i++) {
          if (this.books[i].id === id) {
            this.books[i].count++;
            break;
          }
        }
      }
    },
    filters: {
      showPrice(price) {
        return "¥" + price.toFixed(2);
      }
    }
  })
</script>
</body>
</html>
  • 基本实现思路
    • 页面搭建
    • 数值计算:计算属性、过滤器等
    • 事件函数
发布了102 篇原创文章 · 获赞 12 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/m0_37208669/article/details/104785398