# 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存储在服务器。

## 九、作业

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gb.akanote.cn/vue/day02.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
