> For the complete documentation index, see [llms.txt](https://gb.akanote.cn/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://gb.akanote.cn/vue/day02.md).

# Part2-常用指令

## 一、属性绑定(掌握)

### 1、v-bind介绍

> 前面我们都是通过指令来将值插入到我们的标签内容中，但除了内容外，我们还需要动态绑定某些属性，比如：a标签中的href属性，img标签中的src属性；这个时候我们就可以使用v-bind指令，它的作用就是可以动态绑定属性。

例子：

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
  <div id="app">
    <img :src="src"/>
    <a :href="href">Vue官网</a>
  </div>
</body>
<script>
  new Vue({
    el: '#app',
    data: {
      src: 'https://vuejs.org/images/logo.png',
      href: 'https://vuejs.org/'
    }
  })
</script>
</html>
```

**注意点**

> `v-bind` 和 `v-on` 一样也有简写的方式，`v-bind:src=""` 可以简写成 `:src=""`

### 2、v-bind绑定class

> 很多时候，我们希望能动态切换class，选中字体颜色变红，初始状态字体为黑色；绑定class有两种方式：对象语法，数组语法。

```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title></title>
    <style>
        .current{
            color: blue;
        }
        .italicFont{
            font-style: italic;
        }
        .line{
            text-decoration: underline;
        }
    </style>
</head>
<body>
<div id="app">
    <ul>
        <li>普通一行文字</li>
        <li class="current">添加了current类名的文字</li>
        <li v-bind:class="{'current': isCurrent}">对象格式，由isCurrent判断是否添加current类名</li>
        <li class="line" :class="{'current': isCurrent, 'italicFont': isItalic}">多个类名</li>
        <li :class="isCurrent ? 'current line' : 'line'">三元运算符决定类名</li>
        <li :class="['current', 'line']">数组形式的多类名</li>
        <li :class="getClass()">通过方法来返回类名</li>
    </ul>
</div>
</body>
</html>
<script src="./lib/vue.js"></script>
<script>
new Vue({
    el: "#app",
    data: {
        isCurrent: true,
        isItalic: true
    },
    methods: {
        getClass(){
            return {
                'current': this.isCurrent
            }
        }
    }
})
</script>
```

### 3、v-bind绑定style

> 我们可以利用v-bind:style来绑定一个内嵌样式。
>
> 注意：
>
> 1. 我们可以使用驼峰式语法：比如font-size ---> `fontSize`
> 2. 或短横线分隔 `font-size`
>
> 绑定class有两种方式：
>
> 1. 对象语法
> 2. 数组语法

```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title></title>
</head>
<body>
<div id="app">
    <ul>
        <li style="background: skyblue;">通过style标签属性写的样式</li>
        <li :style="{background: 'pink',fontStyle: 'italic'}">通过v-bind来绑定样式</li>
        <li :style="{background: bgColor,color: fontColor}">数据来自data</li>
        <li :style="[colorStyle, bgStyle]">数组形式</li>
        <li :style="getStyle()">方法形式</li>
    </ul>
</div>
</body>
</html>
<script src="./lib/vue.js"></script>
<script>
new Vue({
    el: "#app",
    data: {
        bgColor: 'orange',
        fontColor: '#cfc',
        colorStyle: {color: 'pink'},
        bgStyle: {background: 'skyblue'}
    },
    methods: {
        getStyle(){
            return [this.colorStyle, this.bgStyle]
        }
    }
})
</script>
```

## 二、循环遍历v-for、key(掌握)

> v-for格式：item in items形式

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        <div>
            <input type="text" v-model="inputVal"/>
            <button @click="add">添加</button>
        </div>
        
        <ul>
            <li v-for="(item,index) in arr" :key="index">
                <input type="checkbox"/>
                {{item.name}}
            </li>
        </ul>
    </div>
</body>
<script>
    new Vue({
        el: '#app',
        data: {
            arr: [
                {id: 1, name: 'Vue'},
                {id: 2, name: 'React'},
                {id: 3, name: 'Angular'}
            ],
            inputVal: ''
        },
        methods: {
            add() {
                this.arr.unshift({id: this.arr.length + 1, name: this.inputVal})
            }
        }
    })
</script>
</html>
```

**组件的key属性**

> 官方推荐我们使用v-for时，给对应的元素或组件添加一个key属性。
>
> 为什么需要这个key属性呢（了解）？
>
> 1. 这个其实和Vue的虚拟DOM的Diff算法有关系。
>
> 当某一层有很多相同节点时，也就是列表节点时，我们插入一个新的节点到列表中
>
> 1. 在B和C之间加一个F，Diff算法默认执行起来时这样的；即把C更新成F，D更新成C，E更新成D，最后再插入一个新的E，这样的效率也太低了。
>
> 当我们使用key来为每一个节点做唯一标识
>
> 1. Diff算法会以key作为标识来识别此节点，找到正确的位置区插入新的节点，所以**key的作用是为了高效的更新虚拟DOM**

[![1568794853883](https://i.loli.net/2021/03/19/ZloVF2GypHkN69a.png)](https://i.loli.net/2021/03/19/ZloVF2GypHkN69a.png)

[![1568794866532](https://i.loli.net/2021/03/19/yF1OChIl3UQnpmS.png)](https://i.loli.net/2021/03/19/yF1OChIl3UQnpmS.png)

[![](https://i.loli.net/2021/03/19/pwSBxv3UKGDIOrF.jpg)](https://i.loli.net/2021/03/19/pwSBxv3UKGDIOrF.jpg)

## 三、计算属性(掌握)

<https://cn.vuejs.org/v2/guide/computed.html>

> 我们知道，在模板中可以直接通过插值语法显示一些data中的数据。但是某些情况下，我们可能需要对数据进行转化后再显示，或者需要将多个数据结合起来进行显示

**例子**

```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title></title>
</head>
<body>
<div id="app">
    <ul>
        <li v-for="item in arr" :key="item.id">
            书名：{{item.name}} -- 价格：{{item.price}}
        </li>
    </ul>
    <h4>总价：{{totalPrice}}</h4>
</div>
</body>
</html>
<script src="./vue.js"></script>
<script>
new Vue({
    el: "#app",
    data: {
        arr: [
            {id: "b01", name: "海底两万里", price: 23},
            {id: "b02", name: "西游记", price: 24},
            {id: "b03", name: "红楼梦", price: 32}
        ]
    },
    computed: {
        totalPrice(){
            let sum = 0;
            this.arr.map(val=>{
                sum+=val.price;
            })
            return "¥ " + sum.toFixed(2) + " 元";
        }
    }
})
</script>
```

> 为什么会有这样的一个属性呢，用methods都可以实现我们的功能，为什么多了这样一个计算属性的东西呢？

### computed与methods的区别

1、computed是属性调用，methods是方法调用

2、computed有缓存功能，多次使用时，该属性只会触发一次调用，而methods是使用一次就触发一次

```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title></title>
</head>
<body>
<div id="app">
    <ul>
        <li v-for="item in arr" :key="item.id">
            书名：{{item.name}} -- 价格：{{item.price}}
        </li>
    </ul>
    <h4>computed计算总结：{{totalPrice1}}</h4>
    <h4>computed计算总结：{{totalPrice1}}</h4>
    <h4>computed计算总结：{{totalPrice1}}</h4>
    <hr>
    <h4>methods计算总价：{{totalPrice()}}</h4>
    <h4>methods计算总价：{{totalPrice()}}</h4>
    <h4>methods计算总价：{{totalPrice()}}</h4>
</div>
</body>
</html>
<script src="./vue.js"></script>
<script>
new Vue({
    el: "#app",
    data: {
        arr: [
            {id: "b01", name: "海底两万里", price: 23},
            {id: "b02", name: "西游记", price: 24},
            {id: "b03", name: "红楼梦", price: 32}
        ]
    },
    computed: {
        totalPrice1(){
            let sum = 0;
            this.arr.map(val=>{
                sum+=val.price;
            })
            console.log('计算属性: '+sum)
            return "¥ " + sum.toFixed(2) + " 元";
        }
    },
    methods: {
        totalPrice(){
            let sum = 0;
            this.arr.map(val=>{
                sum+=val.price;
            })
            console.log('methods方法: '+sum)
            return "¥ " + sum.toFixed(2) + " 元";
        }
    }
})
</script>
```

> 每个计算属性其实都包含一个getter和setter

```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title></title>
</head>
<body>
<div id="app">
    <h3>名：{{firstName}}</h3>
    <h3>姓：{{lastName}}</h3>
    <h3>姓名：{{fullName}}</h3>
</div>
</body>
</html>
<script src="./lib/vue.js"></script>
<script>
var vm = new Vue({
    el: "#app",
    data: {
        firstName: 'Michael',
        lastName: 'Jackson'
    },
    computed: {
        fullName: {
            get(){
                return this.firstName + ' ' + this.lastName;
            },
            set(val){
                const fullName = val.split(' ');
                this.firstName = fullName[0];
                this.lastName = fullName[1];
            }
        }
    },
})
</script>
```

## 四、v-model原理(掌握)

`v-model` 其实是个语法糖，它背后本质上包含了两个操作：

1. `v-bind` 绑定input元素的value属性
2. `v-on` 指令绑定input元素的input事件

```html
<input type="text" v-model="msg"/>
<!-- 等同于 -->
<input type="text" v-bind:value="msg" v-on:input="msg = $event.target.value"/>
```

### 下拉选择框

```html
<body>
  <div id="app">
    <select v-model="mySelect">
      <option value="orange">橙子</option>
      <option value="apple">苹果</option>
      <option value="banana">香蕉</option>
    </select>
  </div>
</body>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      mySelect: 'banana'
    }
  })
</script>
```

## 五、数组操作(掌握)

> 常用操作： push（返回数组长度）、unshift（返回数组长度）、shift（返回删除的值）、pop（返回删除的值）、slice（返回新的数组）、splice、concat（返回新数组）
>
> 新增迭代方法：forEach（没有返回值）、map、filter、reduce

## 六、自定义过滤器(掌握)

> Vue.js 允许你自定义过滤器，可被用于一些常见的文本格式化。过滤器可以用在两个地方：**双花括号插值和 v-bind 表达式** ( 后者从 2.1.0+ 开始支持 )。过滤器应该被添加在 JavaScript 表达式的尾部，由“管道”符号指示：

**格式化价格：**

```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title></title>
</head>
<body>
<div id="app">
    <h2>价格为：{{price | priceFilter}}</h2>
</div>
</body>
</html>
<script src="./vue.js"></script>
<script>
new Vue({
    el: "#app",
    data: {
        price: 25.3
    },
  	// 局部过滤器
    filters: {
        priceFilter(val){
            return "¥ "+val.toFixed(2)+" 元";
        }
    }
})
</script>
```

```js
// 全局过滤器
Vue.filter("priceFilter", function(val){
    return "¥ "+val.toFixed(2)+" 元";
})
```

## 七、图书购物车例子

[![1568800293540](https://i.loli.net/2021/03/19/ParwhNQHVxfiIBT.png)](https://i.loli.net/2021/03/19/ParwhNQHVxfiIBT.png)

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <style>
    table {
      border: 1px solid #e9e9e9;
      border-collapse: collapse;
      border-spacing: 0;
    }

    td,
    th {
      padding: 8px 16px;
      border: 1px solid #e9e9e9;
      text-align: left;
    }
    th {
      background-color: #f7f7f7;
    }
  </style>
</head>

<body>
  <div id="app">
    <table>
      <thead>
        <tr>
          <th v-for="(title,index) in titles" :key="index" v-text="title"></th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(item,index) in books" :key="item.id">
          <td v-text="index"></td>
          <td v-text="book.name"></td>
          <td v-text="book.date"></td>
          <td>{{book.price | priceRule}}</td>
          <td>
            <button @click="sub(index)">-</button>
            <span>{{book.num}}</span>
            <button @click="add(index)">+</button>
          </td>
          <td>
            <button @click="remove(index)">移除</button>
          </td>
        </tr>
      </tbody>
      <tfoot>
        <tr>
          <td colspan="6">总价格：{{totalPrice | priceRule}}</td>
        </tr>
      </tfoot>
    </table>
  </div>
</body>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      titles: ['编号', '书籍名称', '出版日期', '价格', '购买数量', '操作'],
      books: [
        {
          id: 1,
          name: '算法导论',
          date: '2006-9',
          price: 85,
          num: 1
        },
        {
          id: 2,
          name: 'UNIX编程艺术',
          date: '2006-2',
          price: 59,
          num: 1
        },
        {
          id: 3,
          name: 'Vue程序设计',
          date: '2008-10',
          price: 35,
          num: 1
        },
        {
          id: 4,
          name: '颈椎康复',
          date: '2006-3',
          price: 129,
          num: 1
        }
      ]
    },
    filters: {
      priceRule(value) {
        return '￥' + value.toFixed(2)
      }
    },
    computed: {
      totalPrice() {
        return this.books.reduce((prev, current)=> {
          prev +=current.num * current.price
          return prev
        }, 0)
      }
    },
    methods: {
      add(idx){
        this.books[idx].num++
      },
      sub(idx){
        this.books[idx].num--
        if (this.books[idx].num == 0) {
          this.books.splice(idx, 1)
        }
      },
      remove(idx){
        this.books.splice(idx, 1)
      }
    }
  })
</script>

</html>
```

## 八、本地存储(掌握)

### localStorage永久存储

```js
// 添加数据；setItem的value值是字符串类型的数据
localStorage.setItem('name','张三')；
// 获取数据
localStorage.getItem('name'); // 张三
// 清空
localStorage.clear();
```

**注意事项：**

> 1. 除非是主动删除，不然是不会自动删除的
> 2. 一般浏览器存储的大小是5M
>
> 5M = 1024 \* 5kb

### sessionStorage临时会话存储

```js
// 添加数据；setItem的value值是字符串类型的数据
sessionStorage.setItem('name','张三')；
// 获取数据
sessionStorage.getItem('name'); // 张三
// 清空
sessionStorage.clear();
```

**注意事项：**

> 1. 关闭浏览器会自动清空数据
> 2. 一般浏览器存储的大小是5M

### cookie

> 1. cookie:
>
> 网站中，**http请求时无状态**的。也就是第一次登陆成功（发送请求），第二次请求服务器依然不知道是哪一个用户。这时候的cookie就是解决这个问题的，第一次登陆后服务器返回数据（cookie）给浏览器，然后浏览器保存在本地，当该用户发送第二次请求，浏览器自动会把上次请求存储的cookie数据自动带上给服务器，服务器根据客户端的cookie来判断当前是哪一个用户。cookie存储有大小限制，不同浏览器不一样，一般是4kb，所以cookie只能存储小量数据。
>
> 4kb = 4 \* 1024 byte (字节) = 4 \* 1024 \* 8 bit（位）
>
> 服务器会把第一次登陆后服务器返回的cookie存储到浏览器中
>
> ​ 发一个请求给后端（自动带上第一次登陆后的cookie），拿回我自己购物车的列表
>
> 服务端接收到这个请求后，根据浏览器带上的cookie做出判断，判断当前是哪一个用户
>
> 1. session：
>
> session和cookie的作用有点类似，也是存储用户相关信息。不同的是cookie存储在浏览器，而session存储在服务器。

## 九、作业

独自完成图书馆购物车案例。
