[物語を忘れました]°のブログへようこそ

Vue 学习笔记 [Part 2]

作者:故事我忘了
个人微信公众号:程序猿的月光宝盒

一. 计算属性

1.1. 计算属性的本质

  • 每个计算属性都包含一个getter和一个setter

  • 一般情况下我们写的都是getter方法

  • 在某些情况下,你也可以提供一个setter方法(不常用)

  • 在需要写setter的时候,代码如下

    图片

1.2. 计算属性和methods对比

methods和computed看起来都可以实现我们的功能,

那么为什么还要多一个计算属性这个东西呢?

原因:

  • 计算属性在多次使用时, 只会调用一次.

  • 它是有缓存的

    图片

图片

〇.ES6补充

0.1. let/var

事实上var的设计可以看成JavaScript语言设计上的错误. 但是这种错误多半不能修复和移除, 以为需要向后兼容.

​ 大概十年前, Brendan Eich就决定修复这个问题, 于是他添加了一个新的关键字: let.

​ 我们可以将let看成更完美的var

块级作用域

​ JS中使用var来声明一个变量时, 变量的作用域主要是和函数的定义有关.

​ 针对于其他块定义来说是没有作用域的,比如if/for等,这在我们开发中往往会引起一些问题。

而在for中使用var时,我们往往需要这样,因为在ES5中 var 只有function有作用域

图片

但是在ES6后,用let就简单多了

图片

0.2 const的使用

const这个关键字

​ 在很多语言中已经存在, 比如C/C++中, 主要的作用是将某个变量修饰为常量.

​ 在JavaScript中也是如此, 使用const修饰的标识符为常量, 不可以再次赋值.

什么时候使用?

​ 修饰的标识符不会被再次赋值时, 就可以使用const来保证数据的安全性.

0.2.1. 人生建议

在ES6开发中,优先使用const, 只有需要改变某一个标识符的时候才使用let.

使用注意点

const a = 20;
a = 30; //错误:不可以被修改
const name;//错误:没有赋值

0.3 对象字面量增强写法

​ ES6中,对对象字面量进行了很多增强。

​ 属性初始化简写和方法的简写:

图片

二. 事件监听 v-on

v-on介绍

作用:绑定事件监听器

语法糖:@

预期:Function | Inline Statement | Object

参数:event

2.1. 事件监听基本使用

这里,我们用一个监听按钮的点击事件,来简单看看v-on的使用

​ 下面的代码中,使用了v-on:click="counter++”

​ 另外,我们可以将事件指向一个在methods中定义的函数

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <h2>{{counter}}</h2>
  <!--<h2 v-bind:title></h2>-->
  <!--<h2 :title></h2>-->
  <!--<button v-on:click="counter++">+</button>-->
  <!--<button v-on:click="counter&#45;&#45;">-</button>-->
  <!--<button v-on:click="increment">+</button>-->
  <!--<button v-on:click="decrement">-</button>-->
  <button @click="increment">+</button>
  <button @click="decrement">-</button>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      counter: 0
    },
    methods: {
      increment() {
        this.counter++
      },
      decrement() {
        this.counter--
      }
    }
  })
</script>

</body>
</html>

2.2. 参数问题

  • btnClick
  • btnClick(event)
  • btnClick(abc, event) -> $event 传入
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <!--1.事件调用的方法没有参数-->
  <button @click="btn1Click()">按钮1</button>
  <button @click="btn1Click">按钮1</button>

  <!--2.在事件定义时, 写方法时省略了小括号, 但是方法本身是需要一个参数的, 这个时候, Vue会默认将浏览器生产的event事件对象作为参数传入到方法-->
  <!--<button @click="btn2Click(123)">按钮2</button>-->
  <!--<button @click="btn2Click()">按钮2</button>-->
  <button @click="btn2Click">按钮2</button>

  <!--3.方法定义时, 我们需要event对象, 同时又需要其他参数-->
  <!-- 在调用方式, 如何手动的获取到浏览器参数的event对象: $event-->
  <button @click="btn3Click(abc, $event)">按钮3</button>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      abc: 123
    },
    methods: {
      btn1Click() {
        console.log("btn1Click");
      },
      btn2Click(event) {
        console.log('--------', event);
      },
      btn3Click(abc, event) {
        console.log('++++++++', abc, event);
      }
    }
  })

  // 如果函数需要参数,但是没有传入, 那么函数的形参为undefined
  // function abc(name) {
  //   console.log(name);
  // }
  //
  // abc()
</script>
</body>
</html>

2.3. 修饰符

  • .stop 调用 event.stopPropagation() 停止冒泡

  • .prevent 阻止默认行为

  • .enter 键修饰符,键别名

  • .once 只监听一次

  • .native 监听组件根元素的原生事件

    图片

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <!--1. .stop修饰符的使用,调用 event.stopPropagation() 就是点了之后不显示点div-->
  <div @click="divClick">
    aaaaaaa
    <button @click.stop="btnClick">按钮</button>
  </div>

  <!--2. .prevent修饰符的使用 调用 event.preventDefault() 阻止默认行为,以下 是阻止表单默认提交-->
  <br>
  <form action="baidu">
    <input type="submit" value="提交" @click.prevent="submitClick">
  </form>

  <!--3. .监听某个键盘的键帽,这里是监听回车按键 把回车按键去掉就都监听了-->
  <input type="text" @keyup.enter="keyUp">

  <!--4. .once修饰符的使用 只触发一次回调-->
  <button @click.once="btn2Click">按钮2</button>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    methods: {
      btnClick() {
        console.log("btnClick");
      },
      divClick() {
        console.log("divClick");
      },
      submitClick() {
        console.log('submitClick');
      },
      keyUp() {
        console.log('keyUp');
      },
      btn2Click() {
        console.log('btn2Click');
      }
    }
  })
</script>
</body>
</html>

三. 条件判断

3.1. v-if/v-else-if/v-else

​ 这三个指令与JavaScript的条件语句if、else、else if类似。

​ Vue的条件指令可以根据表达式的值在DOM中渲染或销毁元素或组件

简单案例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <h2 v-if="score>=90">优秀</h2>
  <h2 v-else-if="score>=80">良好</h2>
  <h2 v-else-if="score>=60">及格</h2>
  <h2 v-else>不及格</h2>
  <!--计算属性 直接计算好了返回 不写在dom里-->
  <h1>{{result}}</h1>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      score: 99
    },
    computed: {
      result() {
        let showMessage = '';
        if (this.score >= 90) {
          showMessage = '优秀'
        } else if (this.score >= 80) {
          showMessage = '良好'
        }
        // ...
        return showMessage
      }
    }
  })
</script>
</body>
</html>

v-if的原理:

​ v-if后面的条件为false时,对应的元素以及其子元素不会渲染。

​ 也就是根本不会有对应的标签出现在DOM中。

3.2. 登录小案例

用户再登录时,可以切换使用用户账号登录还是邮箱地址登录。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>模拟登录方式切换</title>
</head>
<body>
<div id="vm">
  <div v-if="showTag">
    <label for="userName">用户账号</label>
    <input id="userName" placeholder="用户账号">
    <!--<input id="userName" placeholder="用户账号" key="name">-->
  </div>
  <div v-else>
    <label for="userMail">用户邮箱</label>
    <input id="userMail" placeholder="用户邮箱">
    <!--<input id="userMail" placeholder="用户邮箱" key="email">-->
  </div>
  <button @click="showTag = !showTag">切换登录方式</button>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
  const vm = new Vue({
    el:'#vm',
    data:{
      showTag:true
    }
  })
</script>
</html>

小问题:

​ 如果我们在有输入内容的情况下,切换了类型,我们会发现文字依然显示之前的输入的内容。

​ 但是按道理讲,我们应该切换到另外一个input元素中了。

​ 在另一个input元素中,我们并没有输入内容。

为什么会出现这个问题呢?

​ 问题解答:

​ 这是因为Vue在进行DOM渲染时,出于性能考虑,会尽可能的复用已经存在的元素,而不是重新创建新的元素。

​ 在上面的案例中,Vue内部会发现原来的input元素不再使用,直接作为else中的input来使用了。

​ 解决方案:

如果我们不希望Vue出现类似重复利用的问题,可以给对应的input添加key

并且我们需要保证key的不同

3.3. v-show

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <!--v-if: 当条件为false时, 包含v-if指令的元素, 根本就不会存在dom中-->
  <h2 v-if="isShow" id="aaa">{{message}}</h2>

  <!--v-show: 当条件为false时, v-show只是给我们的元素添加一个行内样式: display: none-->
  <h2 v-show="isShow" id="bbb">{{message}}</h2>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      isShow: true
    }
  })
</script>

</body>
</html>

v-show的用法和v-if非常相似,也用于决定一个元素是否渲染:

v-if和v-show对比

​ 相同:

v-ifv-show都可以决定一个元素是否渲染

​ 不同:

v-if当条件为false时,压根不会有对应的元素在DOM中。

v-show当条件为false时,仅仅是将元素的display属性设置为none而已。

开发中如何选择呢?

​ 当需要在显示与隐藏之间切换频繁时,使用v-show

​ 当只有一次切换时,通过使用v-if

四. 循环遍历

4.1. 遍历数组

当我们有一组数据需要进行渲染时,我们就可以使用v-for来完成。

​ v-for的语法类似于JavaScript中的for循环。

​ 格式如下:item in items的形式。

如果在遍历的过程中不需要使用索引值

​ v-for="movie in movies"

​ 依次从movies中取出movie,并且在元素的内容中,我们可以使用Mustache语法,来使用movie

如果在遍历的过程中,我们需要拿到元素在数组中的索引值呢?

​ 语法格式:v-for=(item, index) in items

​ 其中的index就代表了取出的item在原数组的索引值。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <!--1.在遍历的过程中,没有使用索引值(下标值)-->
  <ul>
    <li v-for="item in names">{{item}}</li>
  </ul>

  <!--2.在遍历的过程中, 获取索引值-->
  <ul>
    <li v-for="(item, index) in names">
      {{index+1}}.{{item}}
    </li>
  </ul>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      names: ['why', 'kobe', 'james', 'curry']
    }
  })
</script>

</body>
</html>

4.2. 遍历对象中的属性

比如某个对象中存储着你的个人信息,我们希望以列表的形式显示出来。

  • value

  • value, key

  • value, key, index

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <body>
    
    <div id="app">
      <!--1.在遍历对象的过程中, 如果只是获取一个值, 那么获取到的是value-->
      <ul>
        <li v-for="item in info">{{item}}</li>
      </ul>
    
    
      <!--2.获取key和value 格式: (value, key) -->
      <ul>
        <li v-for="(value, key) in info">{{value}}-{{key}}</li>
      </ul>
    
    
      <!--3.获取key和value和index 格式: (value, key, index) -->
      <ul>
        <li v-for="(value, key, index) in info">{{value}}-{{key}}-{{index}}</li>
      </ul>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      const app = new Vue({
        el: '#app',
        data: {
          info: {
            name: 'kin',
            age: 18,
            height: 1.88
          }
        }
      })
    </script>
    
    </body>
    </html>
    

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

    ​ 为什么需要这个key属性呢(了解)?

    ​ 这个其实和Vue的虚拟DOM的Diff算法有关系。

    ​ 这里我们借用React’s diff algorithm中的一张图来简单说明一下:

    图片

    ​ 当某一层有很多相同的节点时,也就是列表节点时,我们希望插入一个新的节点

    ​ 我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的。

    ​ 即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?

    ​ 所以我们需要使用key来给每个节点做一个唯一标识

    ​ Diff算法就可以正确的识别此节点

    ​ 找到正确的位置区插入新的节点。

    所以一句话,key的作用主要是为了高效的更新虚拟DOM。

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <body>
    
    <div id="app">
      <ul>
        <li v-for="item in letters" :key="item">{{item}}</li>
      </ul>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      const app = new Vue({
        el: '#app',
        data: {
          letters: ['A', 'B', 'C', 'D', 'E']
        }
      })
    </script>
    
    </body>
    </html>
    

4.3. 数组哪些方法是响应式的

push():在最后面添加元素

unshift():在数组最前面添加元素

pop():删除数组中的最后一个元素

shift():删除数组中的第一个元素

splice():删除,插入,替换

sort() :排序

reverse():倒序

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <ul>
    <li v-for="item in letters">{{item}}</li>
  </ul>
  <button @click="btnClick">按钮</button>
</div>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      letters: ['a', 'b', 'c', 'd']
    },
    methods: {
      btnClick() {
        // 1.push方法在最后面添加元素
        // this.letters.push('aaa')
        // this.letters.push('aaaa', 'bbbb', 'cccc')

        // 2.pop(): 删除数组中的最后一个元素
        // this.letters.pop();

        // 3.shift(): 删除数组中的第一个元素
        // this.letters.shift();

        // 4.unshift(): 在数组最前面添加元素
        // this.letters.unshift()
        // this.letters.unshift('aaa', 'bbb', 'ccc')

        // 5.splice作用: 删除元素/插入元素/替换元素
        //      第一个参数,index开始位置
        // 删除元素: 第二个参数传入你要删除几个元素(如果没有传,就删除后面所有的元素)
        // 替换元素: 第二个参数, 表示我们要替换几个元素, 
        //      后面的参数:用于替换前面的元素
        // 插入元素: 第二个参数, 传入0, 
        //        后面的参数:要插入的元素
        // splice(start)
        // splice(start):
        this.letters.splice(1, 3, 'm', 'n', 'l', 'x')
        // this.letters.splice(1, 0, 'x', 'y', 'z')

        // 6.sort() 排序
        // this.letters.sort()

        // 7.reverse() 倒序
        // this.letters.reverse()

        // 注意: 通过索引值修改数组中的元素
        // this.letters[0] = 'bbbbbb';
        // this.letters.splice(0, 1, 'bbbbbb')
        // set(要修改的对象, 索引值, 修改后的值)
        // Vue.set(this.letters, 0, 'bbbbbb')
      }
    }
  })


  // function sum(num1, num2) {
  //   return num1 + num2
  // }
  //
  // function sum(num1, num2, num3) {
  //   return num1 + num2 + num3
  // }
  // function sum(...num) {
  //   console.log(num);
  // }
  //
  // sum(20, 30, 40, 50, 601, 111, 122, 33)

</script>

</body>
</html>

五. 书籍购物车案例

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <link rel="stylesheet" href="index.css">
</head>
<body>
<div id="vm">
  <!--判断有没有长度,没有就是空的-->
  <div v-if="books.length">
    <table>
      <thead>
      <tr>
        <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>
        <!--showPrice 是过滤器-->
        <td>{{item.price | showPrice}}</td>
        <td>
          <!--v-bind:disabled 条件true时 让按钮失效-->
          <button @click="sub(index)" v-bind:disabled="item.count <= 1">-</button>
          {{item.count}}
          <button @click="add(index)">+</button>
        </td>
        <td>
          <button @click="remove(index)">移除</button>
        </td>
      </tr>
      </tbody>
    </table>
    <h2>总价格:{{totalPrice | showPrice}}</h2>
  </div>

  <h2 v-else>购物车为空</h2>
</div>
</body>
<script src="../js/vue.js"></script>
<script src="main.js"></script>
</html>

main.js

const vm = new Vue({
  el:'#vm',
  data:{
    books:[
      {
        id: 1,
        name:'「人間失格」',
        price: 85.42,
        count:1
      },
      {
        id: 2,
        name:'「白夜行」',
        price: 89.00,
        count:1
      },
      {
        id: 3,
        name:'「解忧杂货铺」',
        price: 65.00,
        count:1
      },
      {
        id: 4,
        name:'「天龙八部」',
        price: 185.00,
        count:1
      }
    ]
  },
  methods:{
    sub(index){
      this.books[index].count--
    },
    add(index){
      this.books[index].count++
    },
    remove(index){
      this.books.splice(index,1)
    }
  },
  computed:{
    totalPrice(){
      let totalPrice = 0;
      for (let book of this.books){
        totalPrice += book.price * book.count
      }
      return totalPrice;
    }
  },
  //过滤器
  filters:{
    // 自动把参数传进来
    showPrice(price){
      // .toFixed(2)保留两位小数
      return '¥' + price.toFixed(2)
    }
  }
});

index.css

table {
  border: 1px solid #e9e9e9;
  border-collapse: collapse;
  border-spacing: 0;
}
th, td {
  padding: 8px 16px;
  border: 1px solid #e9e9e9;
  text-align: left;
}
th {
  background-color: #f7f7f7;
  color: #5c6b77;
  font-weight: 600;
}

点我进演示地址(外服,有点慢)

posted @ 2020-05-14 10:58  故事我忘了°  阅读(274)  评论(0)    收藏  举报