# 02 插值的操作

# 01 Mustache语法

<body>
  <div id ="app">
    <h2>{{message}}</h2>
    <h2>{{message}},李银河</h2>
    <h2>{{firstName + lastName}}</h2>
    <h2>{{firstName + ' ' + lastName}}</h2>
    <h2>{{firstName}} {{lastName}}</h2>
    <h2>{{counter * 2}}</h2>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        message: '你好啊',
        firstName: 'kobe',
        lastName: 'bryant',
        counter: 100
      },
      methods: {}
    });
  </script>
</body>

# 02 v-once

v-once不动态加载

<div id ="app"> 
    <h2>{{message}}</h2>
    <h2 v-once>{{message}}</h2>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({ 
      el: '#app',
      data: {
        message: "你好啊!" 
      },
      methods: {}
    });
  </script>

# 03 v-html

直接展示html

 <div id ="app">
    <h2 v-html="url"></h2>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        message: "你好啊",
        url: '<a href="http://www.baidu.com">baidu.com</a>'
      },
      methods: {}
    });
  </script>

# 04 v-text

<div id ="app"> 
    <h2>{{message}},李银河!</h2>
    <h2 v-text='message'>,李银河</h2>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        message: '你好啊'
      },
      methods: {}
    });
  </script>

# 05 v-pre

原封不动的解析

<div id ="app">
    <h2>{{message}}</h2>
    <h2 v-pre>{{message}}</h2>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        message: '你好啊'
      },
      methods: {}
    });
  </script>

# 06 v-cloak

<div id ="app">
  <h2 v-cloak>{{message}}</h2>
    
  </div>

  <script>
    //在Vue解析之前,div中有一个属性v-cloak
    //在vue解析之后,div中没有一个属性v-cloak
    var app = new Vue({
      el: '#app',
      data: {
        message: '你好啊'
      },
      methods: {}
    });
  </script>

# 03 绑定属性

# 01 v-bind基本使用

  <div id ="app">
  <!-- 错误的做法,这里不可以使用mustache语法 -->
    <!-- <img src="{{imgURL}}" alt=""> -->
    <!-- 正确的做法:使用v-bind指令 -->
    <img v-bind:src="imgURL" alt="">
    <a v-bind:href="aHref">{{aMessage}}</a>

    <!-- 语法糖的写法 -->
    <img :src="imgURL" alt="">
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        message: '你好啊',
        imgURL: 'https://img20.360buyimg.com/babel/s590x470_jfs/t1/145861/20/3841/76376/5f1e3ca8Efa134bcf/ecce5b402f1274d0.jpg.webp',
        aHref: 'http://www.baidu.com',
        aMessage: '百度一下'
      },
      methods: {}
    });
  </script>

# 02 v-bind动态绑定class(对象语法)

    <div id ="app"> 
    <!-- <h2 class="active">{{message}}</h2>
    <h2 :class="active">{{message}}</h2> -->

    <!-- <h2 v-bind:class="{key1: value1, key2: value2}">{{message}}</h2> -->
    <!-- <h2 v-bind:class="{类名: boolean, 类名2: boolean}">{{message}}</h2> -->
    <h2 class="title" v-bind:class="{active: isActive, line: isLine}">{{message}}</h2>
    <h2 class="title" v-bind:class="getClasses()">{{message}}</h2>
    <button @click="btnClick()">按钮</button>  //这里的btnClick方法可以省略()
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        message: '你好啊',
        active: 'active',
        isActive: 'true',
        isLine: 'false'
      },
      methods: {
        btnClick: function (){
          this.isActive = !this.isActive;
        },
        getClasses: function() {
          return {active: this.isActive, line: this.isLine};
        }
      }
    });
  </script>

# 03 v-bind动态绑定class(数组语法)

  <div id ="app">
    <!-- 加单引号是字符串,不加单引号会作为变量解析 -->
    <h2 class="title" :class="['active', 'line']">{{message}}</h2>
    <h2 class="title" :class="getClasses()">{{message}}</h2>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        message: '你好啊',
        active: 'aaaa',
        line: 'bbbbbb'
      },
      methods: {
        getClasses: function () {
          return ['active', 'line'];
        }
      }
    });
  </script>

# 04 作业(v-for和v-bind结合)

  <!-- 点击列表中的哪一项,哪一项就变成红色 -->
  <div id ="app"> 
    <ul>
      <li :class="{active: isActive}" @click="insertClass()" v-for="(item,index) in movies">{{index}} - {{item}}</li>
    </ul>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        movies: ['海王', '海尔兄弟', '火影忍者', '进击的巨人'],
        isActive: true
      },
      methods: {
        insertClass: function () {
          
        }
      }
    });
  </script>

# 05 v-bind动态绑定style(对象语法)

<div id ="app">
    <!-- <h2 :style="{key(css属性名): value(属性值)}"></h2> -->
    <!-- 注意这里50px要加引号,不加会当作变量,前面的key可以不用加,因为就是个key -->
    <h2 :style="{fontSize: '50px'}">{{message}}</h2>
    <!-- 这里是直接引用变量 -->
    <h2 :style="{fontSize: finalSize}">{{message}}</h2>
    <h2 :style="{fontSize: finalNum + 'px', color: finalColor}">{{message}}</h2>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        message: '你好啊',
        finalSize: '100px',
        finalNum: 100,
        finalColor: 'red'
      },
      methods: {}
    });
  </script>

# 06 v-bind动态绑定style(数组语法)

<div id ="app"> 
    <h2 :style="[baseStyle,baseStyle1]">{{message}}</h2>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        message: '你好啊',
        baseStyle: {backgroundColor: 'red'},
        baseStyle1: {fontSize: '100px'},
      },
      methods: {}
    });
  </script>

# 04 计算属性

# 01 计算属性的基本使用

  <div id ="app"> 
    <h2>{{firstName + ' ' + lastName}}</h2>
    <h2>{{firstName}} {{lastName}}</h2>
    <h2>{{getFullName()}}</h2>
    <h2>{{fullName}}</h2>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        firstName: 'Lebron',
        lastName: 'James'
      },
      methods: {
        getFullName() {
          return this.firstName + ' ' + this.lastName;
        }
      },
      computed: {
        fullName: function() {
          return this.firstName + ' ' + this.lastName;
        }
      }
    });
  </script>

# 02 计算属性的复杂操作

  <div id ="app"> 
    <h2>总价格:{{totalPrice}}</h2>
  </div>
  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        books: [
          {id: 110, name: 'Unix变成艺术', price: 119},
          {id: 111, name: '代码大全', price: 105},
          {id: 112, name: '深入立即计算机原理', price: 98},
          {id: 113, name: '现代操作系统', price: 87},
        ]
      },
      computed: {
        totalPrice: function () {
          return this.books.reduce((total,book) => {
            return total += book.price;
          },0)
        }
      },
      methods: {}
    });
  </script>

# 03 计算属性的setter和getter

  <div id ="app">
    <h2>{{firstName}}</h2>
    <h2>{{lastName}}</h2>
    <h2>{{fullName}}</h2>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        firstName: 'kobe',
        lastName: 'bryant'
      },
      computed: {
        fullName: {
          get() {
            console.log('--调用了fullName的get');
            return this.firstName + ' ' + this.lastName;
          },
          set(newValue) {  //set不常用
            console.log('---调用了fullName的set');
            const name = newValue.split(' ');
            this.firstName = name[0];
            this.lastName = name[1];
          }
        }
      },
      methods: {}
    });
  </script>

# 04 计算属性的缓存

计算属性会进行缓存,如果多次使用时,计算属性只会调用一次。而methods是调用一次就会执行一次。

  <div id ="app">
    <h2>{{firstName}}</h2>
    <h2>{{lastName}}</h2>
    <h2>{{fullName}}</h2>
    <h2>{{fullName}}</h2>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        firstName: 'kobe',
        lastName: 'bryant'
      },
      computed: {
        fullName: {
          get() {
            console.log('--调用了fullName的get');
            return this.firstName + ' ' + this.lastName;
          },
          set(newValue) {
            console.log('---调用了fullName的set');
            const name = newValue.split(' ');
            this.firstName = name[0];
            this.lastName = name[1];
          }
        }
      },
      methods: {}
    });
  </script>

# 05 事件监听

# 01 v-on的基本使用

  <div id ="app">
    <h2>{{counter}}</h2>
    <!-- <button v-on:click="increment">+</button>
    <button v-on:click="decrement">-</button> -->

    <button @click="increment">+</button>
    <button @click="decrement">-</button>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        counter: 0
      },
      methods: {
        increment(){
          this.counter++;
        },
        decrement() {
          this.counter--;
        }
      }
    });
  </script>

# 02 v-on的参数问题

  <div id ="app">
    <!--1.事件调用的方法没有参数 -->
    <button @click="btn1Click()">按钮1</button>
    <button @click="btn1Click">按钮1</button>
    
    <!--2.在事件定义时,写方法时省略了小括号,但是方法本身是需要一个参数的,
    这个时候,vue会默认将浏览器产生的event事件对象作为参数传入到方法。
    如果写了括号,但是不传参数的话,函数会接收到undefined。
      -->
    <button @click="btn2Click">按钮2</button>
    <button @click="btn2Click()">按钮2</button>
    <button @click="btn2Click('abc')">按钮2</button>

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

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {},
      methods: {
        btn1Click() {
          console.log('按钮被点击!');
        },
        btn2Click(event) {
          console.log('-------', event);
        },
        btn3Click(par,event){
          console.log('-------', par, event);
        }
      }
    });
  </script>

# 03 v-on的修饰符

  <div id ="app">
    <!-- 1. .stop阻止冒泡 -->
    <div @click = "divClick">
      <span @click.stop = "spanClick">冒泡测试</span>
    </div>

    <!-- 2. .prevent阻止默认事件 -->
    <br>
    <form action="baidu">
      <input type="submit" value="提交" @click.prevent="formClick">
    </form>

    <!-- 3. .enter监听键盘某个按键 -->
    <input type="text" @keyup.enter="keyUp">

    <!-- 4. once只触发一次回调 -->
    <button @click.once="btnOnce">按钮2</button>
    
    <!-- 5. native监听组件根元素的原生事件-->
    <!-- 监听<back-top/>组件的点击事件 -->
    <back-top @click.native="backTopClick" ></back-top>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        message: '你好啊'
      },
      methods: {
        divClick() {
          console.log('divClick');
        },
        spanClick() {
          console.log('spanClick');
        },
        formClick() {
          console.log('formClick');
        },
        keyUp() {
          console.log('keyup');
        },
        btnOnce() {
          console.log('btnOnce');
        }
      }
    });
  </script>

# 06 条件的判断

# 01 v-if的使用

  <div id ="app">
    <div v-if="state">
      <div>内容</div>
      <div>内容</div>
      <div>内容</div>
    </div>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        state: true
      },
      methods: {}
    });
  </script>

# 02 v-if和v-else的使用

  <div id ="app">
    <div v-if="state">
      <span>v-if显示的内容</span>
    </div>
    <div v-else>v-else显示的内容</div>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        state: true
      },
      methods: {}
    });
  </script>

# 03 v-if和v-else-if和v-else的使用

  <div id ="app">
    <div v-if="score > 90">优秀</div>
    <div v-else-if="score > 80">良好</div>
    <div v-else-if="score > 60">及格</div>
    <div v-else>不及格</div>
    <div>{{level}}</div>
  </div>
  
  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        score: 100
      },
      methods: {},
      computed: {
        level() {
          let state = '优秀';
          if(this.score > 90){
            state = '优秀';
          }else if(this.score > 90){
            state = '良好';
          }else if(this.score > 60) {
            state = '及格';
          }else{
            state = '不及格';
          }
          return state;
        }
      }
    });
  </script>

# 04 用户登陆案例切换

  <div id ="app">
    <span v-if="isUser">
      <label for="username">用户账号</label>
      <input type="text" id="username" placeholder="用户账号">
    </span>
    <span v-else=>
      <label for="email">用户邮箱</label>
      <input type="text" id="email" placeholder="用户邮箱">
    </span>
    <button @click=" isUser = !isUser">切换类型</button>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        isUser: true
      },
      methods: {}
    });
  </script>

# 06 v-show的使用

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

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

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        message: '你好啊',
        isShow: true
      },
      methods: {}
    });
  </script>

# 07 循环遍历

# 01 v-for循环遍历数组

  <div id ="app">
    <!-- 遍历数组,只拿到值 -->
    <ul v-for="item in names">
      <Li>{{item}}</Li>
    </ul>

    <!-- 遍历数组,拿到值和下标 -->
    <ul v-for="(item,index) in names">
      <Li>{{index}}.{{item}}</Li>
    </ul>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        names: ['猪八戒', '孙悟空', '唐僧', '沙僧']
      },
      methods: {}
    });
  </script>

# 02 v-for循环遍历对象

<body>
  <div id ="app">
    <!-- 遍历对象,如果只获取一个值的话,只拿到value -->
    <ul v-for="item in info">
      <li>{{item}}</li>
    </ul>

    <!-- 遍历对象,如果获取两个值的话,参数:(value,key) -->
    <ul v-for="(value,key) in info">
      <li>{{key}}-{{value}}</li>
    </ul>

    <!-- 遍历对象,如果获取到三个值的话,参数(value,key,index) -->
    <ul v-for="(value,key,index) in info">
      <li>{{index+1}}.{{key}}-{{value}}</li>
    </ul>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        info: {
          name: 'chengyi',
          age: 18,
          height: 1.88
        }
      },
      methods: {}
    });
  </script>

# 03 v-for使用过程添加key

这里使用key的原因是因为虚拟dom的原因,diff算法。

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

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        letters: ['a', 'b', 'c', 'd', 'e']
      },
      methods: {}
    });
  </script>

# 04 哪些数组的方法是响应式的

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

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        letters: ['a','c','b','d']
      },
      methods: {
        btnClick() {
          //1.push方法
          // this.letters.push('aaa');
          // this.letters.push('bbb','bbb','ccc');

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

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

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

          //5.splice作用:删除元素/插入元素/替换元素
          //删除元素:第二个参数传入你要删除几个元素(如果没有传,就删除后面所有的元素)
          //替换元素:第二个参数,表示我们要替换几个元素,后面是用于替换前面的元素
          //插入元素:第二个参数,传入0,并且后面跟上要插入的元素
          //this.letters.splice(1,3,'m','n','l','x');

          //5.sort()
          // this.letters.sort();

          //6.reverse()
          // this.letters.reverse();

          //注意:通过索引值修改数组中的元素
          // this.letters[0] = 'bbbb'; //这种方法不会动态响应,因为vue没有添加这种方式
          //通过以下两种方式修改
          // this.letters.splice(0,1,'bbbb');
          // set(要修改的对象,索引值,修改后的值)
          Vue.set(this.letters, 0, 'bbb');
        }
      }
    });
  </script>

# 05 作业讲解

  <div id ="app">
    <ul>
      <li v-for="(item,index) in movies" :class="{active: currentIndex === index}" @click="liClick(index)">{{item}}</li>
    </ul>
  </div>

  <script>
    //列表中,点击谁谁就变成唯一的红色
    var app = new Vue({
      el: '#app',
      data: {
        movies: ['海王', '海贼王', '加勒比海盗', '海尔兄弟'],
        currentIndex: 0
      },
      methods: {
        liClick(index) {
          this.currentIndex = index;
        }
      }
    });
  </script>

# 08 书籍购物车案例

这里面使用了filters语法

<div id="app">
  <div v-if="books.length">
    <table>
      <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(index)" v-bind:disabled="item.count <= 1">-</button>
          {{item.count}}
          <button @click="increment(index)">+</button>
        </td>
        <td><button @click="removeHandle(index)">移除</button></td>
      </tr>
      </tbody>
    </table>
    <h2>总价格: {{totalPrice | showPrice}}</h2>
  </div>
  <h2 v-else>购物车为空</h2>
</div>
<script> 
	const app = new Vue({
  el: '#app',
  data: {
    books: [
      {
        id: 1,
        name: '《算法导论》',
        date: '2006-9',
        price: 85.00,
        count: 1
      },
      {
        id: 2,
        name: '《UNIX编程艺术》',
        date: '2006-2',
        price: 59.00,
        count: 1
      },
      {
        id: 3,
        name: '《编程珠玑》',
        date: '2008-10',
        price: 39.00,
        count: 1
      },
      {
        id: 4,
        name: '《代码大全》',
        date: '2006-3',
        price: 128.00,
        count: 1
      },
    ]
  },
  methods: {
    // getFinalPrice(price) {
    //   return '¥' + price.toFixed(2)
    // }
    increment(index) {
      this.books[index].count++
    },
    decrement(index) {
      this.books[index].count--
    },
    removeHandle(index) {
      this.books.splice(index, 1)
    }
  },
  computed: {
    totalPrice() {
      let totalPrice = 0
      for (let i = 0; i < this.books.length; i++) {
        totalPrice += this.books[i].price * this.books[i].count
      }
      return totalPrice

      // for (let i in/of this.books)
      // reduce
    }
  },
  filters: {
    showPrice(price) {
      return '¥' + price.toFixed(2)
    }
  }
})
</script>

# 09 v-model的使用

# 01 v-model基本使用

v-model双向绑定

  <div id ="app">
    <input type="text" v-model="message">
    {{message}}
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        message: 'hello world'
      },
      methods: {}
    });
  </script>

# 02 v-model的原理

  <div id ="app"> 
    <!-- <input type="text" :value="message" @input="changeInput">
    {{message}} -->

    <input type="text" :value='message' @input="message = $event.target.value">
    {{message}}
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        message: 'hello world'
      },
      methods: {
        changeInput(event) {
          this.message = event.target.value;
        }
      }
    });
  </script>

# 03 v-model结合radio类型

  <div id ="app"> 
    <label for="male">
      <input type="radio" id="male" value="" name="sex" v-model="sex"></label>
    <label for="female">
      <input type="radio" id="female" value="" name="sex" v-model="sex"></label>
    <h2>您当前选中的是:{{sex}}</h2>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        sex: '男'
      },
      methods: {}
    });
  </script>

# 04 v-model结合checkbox类型

  <div id ="app">
    <!-- 单选 -->
    <!-- <label for="agree">
      <input type="checkbox" name="" id="agree" v-model="isAgree">请同意协议
    </label>
    <h2>您的选择是:{{isAgree}}</h2>
    <button :disabled="!isAgree">下一步</button> -->

    <!-- 多选 -->
    <input type="checkbox" name="" value="篮球" id="" v-model="hobbies">篮球
    <input type="checkbox" name="" value="乒乓球" id="" v-model="hobbies">乒乓球
    <input type="checkbox" name="" value="羽毛球" id="" v-model="hobbies">羽毛球
    <input type="checkbox" name="" value="足球" id="" v-model="hobbies">足球
    <h2>您的爱好是:{{hobbies}}</h2>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        isAgree: false,
        hobbies: []
      },
      methods: {}
    });
  </script>

# 05 v-model结合select类型

  <div id ="app">
    <!-- 1. 选择一个 -->
    <select name="abc" id=""  v-model="fruit">
      <option value="苹果">苹果</option>
      <option value="香蕉">香蕉</option>
      <option value="葡萄">葡萄</option>
      <option value="榴莲">榴莲</option>
      <option value="橘子">橘子</option>
    </select>
    <h2>我选中的是: {{fruit}}</h2>

    <!-- 2. 选择多个 -->
    <select name="abc" id=""  v-model="mySelect" multiple>
      <option value="苹果">苹果</option>
      <option value="香蕉">香蕉</option>
      <option value="葡萄">葡萄</option>
      <option value="榴莲">榴莲</option>
      <option value="橘子">橘子</option>
    </select>
    <h2>我选中的是: {{mySelect}}</h2>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        fruit: '苹果',
        mySelect: []
      },
      methods: {}
    });
  </script>

# 06 v-model修饰符的使用

  <div id ="app"> 
    <!-- 1. lazy -->
    <input type="text" name="" id="" v-model.lazy="message">
    <h2>您输入的:{{message}}</h2>

    <!-- 2. number -->
    <input type="number" name="" id="" v-model.number="num">
    <h2>{{num}}-{{typeof num}}</h2>

    <!-- 3.修饰符 .trim -->
    <input type="text" name="" id="" v-model.trim="blockString">
    <h2>去重后的字符串:{{blockString}}</h2>
  </div>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        message: 'hello world',
        num: 0,
        blockString: ''
      },
      methods: {}
    });
  </script>

# 10 组件化开发

# 01 组件化的基本使用

  <div id ="app"> 
    <!-- 3.使用组件 -->
    <my-cpn></my-cpn>
    <my-cpn></my-cpn>
    <my-cpn></my-cpn>
    <my-cpn></my-cpn>
  </div>

  <script>
    // 1.创建组件为构造器对象
    const cpnC = Vue.extend({
      template: `
      <div>
        <h2>我是标题</h2>
        <p>我是内容,哈哈哈!</p>
        <p>我是内容,呵呵呵! </p>
      </div>
      `
    })

    // 2.注册组件
    Vue.component('my-cpn',cpnC);

    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {},
      methods: {}
    });
  </script>

# 02 全局组件和局部组件

  <div id ="app">
    <cpn></cpn>
    <cpn></cpn>
    <cpn></cpn>
  </div>

  <script>
    // 1. 创建组件构造器
    const cpnC = Vue.extend({
      template: `
      <div>
        <h2>我是标题</h2>
        <p>我是内容,哈哈哈!</p>
        <p>我是内容,呵呵呵! </p>
      </div>
      `
    })

    // 2. 注册组件
    // Vue.component('cpn',cpnC);

    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {},
      methods: {},
      // 局部组件
      components: {
        // cpn为使用组件时的标签名
        cpn: cpnC
      }
    });
  </script>

# 03 父组件和子组件

  <div id ="app"> 
    <cpn></cpn>
  </div>

  <script>
    // 1.创建第一个组件
    const cpnC1 = Vue.extend({
      template: `
      <div>
        <h2>我是标题1</h2>
        <p>我是内容,呵呵</p>
      </div>
      `
    })

     // 2.创建第二个组件
    const cpnC2 = Vue.extend({
      template: `
      <div>
        <h2>我是标题2</h2>
        <p>我是内容,呵呵</p>
        <cpn></cpn>
      </div>
      `,
      // 将子组件挂载到父组件
      components: {
        cpn: cpnC1
      }
    })

    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {},
      methods: {},
      components: {
        cpn: cpnC2
      }
    });
  </script>

# 04 组件的语法糖注册方式

主要是省去了调用Vue.extend()的步骤,而是可以直接使用一个对象来代替

  <div id ="app"> 
    <cpn1></cpn1>
    <cpn2></cpn2>
  </div>

  <script>
    // 1.全局组件注册的语法糖
    // 1.创建组件构造器
    // const cpnC = Vue.extend();

    // 2.注册局部组件的语法糖
    Vue.component('cpn1', {
      template:  `
      <div>
        <h2>我是标题</h2>
        <p>我是内容,哈哈哈!</p>
        <p>我是内容,呵呵呵! </p>
      </div>
      `
    })

    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {},
      methods: {},
      components: {
        cpn2: {
      template:  `
      <div>
        <h2>我是标题2</h2>
        <p>我是内容,哈哈哈!</p>
        <p>我是内容,呵呵呵! </p>
      </div>
      `
    }
      }
    });
  </script>

# 05 组件模版的分离写法

  <div id ="app"> 
    <cpn1></cpn1>
  </div>

  <!--1.script标签, 注意:类型必须是text/x-template-->
  <!-- <script type="text/x-template" id="cpn">
    <div>
      <h2>我是标题</h2>
      <p>我是内容,哈哈哈</p>
    </div>
  </script> -->

  <!-- 2.template标签 -->
  <template id="cpn">
    <div>
      <h2>我是标题</h2>
      <p>我是内容,哈哈哈</p>
    </div>
  </template>
  <script>
    // 1.注册一个全局组件
    Vue.component('cpn1',{
      template: '#cpn'
    });

    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {},
      methods: {}
    });
  </script>

# 06 组件中数据的存放问题

data属性必须是一个函数 而且这个函数返回一个对象,对象内部保存着数据

  <div id ="app"> 
    <cpn></cpn>
  </div>
  <!-- 1. 创建了一个组件 -->
  <template id="cpnC">
    <div>
      <h2>{{title}}</h2>
      <p>我是内容,呵呵呵</p>
    </div>
  </template>
  <script>
    Vue.component('cpn', {
      template: '#cpnC',
      data() {
        return {
          title: '我是标题'
        }
      }
    })

    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {},
      methods: {}
    });
  </script>

# 07 组件中的data为什么必须是函数

原因是在于Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。

  <div id ="app">
    <cpn></cpn>
  </div>

  <template id="cpnC">
    <div>
      <h2>当前计数:{{counter}}</h2>
      <button @click="increment">+</button>
      <button @click="decrement">-</button>
    </div>
  </template>
  <script>
    Vue.component('cpn', {
      template: '#cpnC',
      data() {
        return {
          counter: 0
        }
      },
      methods: {
        increment() {
          this.counter++;
        },
        decrement() {
          this.counter--;
        }
      }
    })

    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {},
      methods: {}
    });
  </script>

# 08 父组件向子组件传递数据

在组件中,使用选项props来声明需要从父级接收到的数据。

props的值有两种方式:

  • 方式一:字符串数组,数组中的字符串就是传递时的名称。
  • 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。

除了数组之外,我们也可以使用对象,当需要对props进行类型等验证时,就需要对象写法了。

当我们有自定义构造函数时,验证也支持自定义的类型

  <div id ="app">
    <cpn v-bind:cmovies="movies" :cmessage="message"></cpn>
  </div>

  <template id="cpnC">
    <div>
      <ul>
        <li v-for="item in cmovies">{{item}}</li>
      </ul>
      <h2>{{cmessage}}</h2>
    </div>
  </template>

  <script>
    const cpn = {
      template: '#cpnC',
      // props: ['cmovies', 'cmessage'],
      data() {
        return {
          title: '模版父传子数据测试'
        }
      },
      props: {
        // 1.类型限制
        // cmovies: Array,
        // cmessage: String,

        // 2.提供一些默认值, 以及必传值
        cmessage: {
          type: String,
          default: '程意',
          required: true
        },
        // 类型是对象或者数组时, 默认值必须是一个函数
        cmovies: {
          type: Array,
          default() {
            return []
          }
        }
      }
    }
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        message: '你好啊',
        movies: ['海王','三十而立','二十不惑']
      },
      methods: {},
      components: {
        cpn
      }
    });
  </script>

img

# 09 组件通信-父传子(props中的驼峰命名)

  <div id ="app">
    <!-- 使用-连接,组件中可使用驼峰命名接受到数据  -->
    <cpn :c-info='info' :child-my-message="message"></cpn>
  </div>
  <template id="cpn">
    <div>
      <h2>{{cInfo}}</h2>
      <h2>{{childMyMessage}}</h2>
    </div>
  </template>


  <script>
    const cpn = {
      template: '#cpn',
      props: {
        cInfo: {
          type: Object,
          default() {
            return {}
          }
        },
        childMyMessage: {
          type: String,
          default: ''
        }
      }
    }

    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        info: {
          age: '18',
          name: '程意',
          height: '1.88'
        },
        message: '你好啊'
      },
      methods: {},
      components: {
        cpn
      }
    });
  </script>

# 10 组件通信-子传父自定义事件

  <!-- 1.父组件模版 -->
  <div id ="app"> 
    <cpn @item-click="cpnClick"></cpn>
  </div>

  <!-- 2.子组件模版 -->
  <template id="cpn">
    <div>
       <button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button>
    </div>
  </template>

  <script>
    // 1. 子组件
    const cpn = {
      template: '#cpn',
      data() {
        return {
          categories: [
            {id: '1', name: '手机数码'},
            {id: '2', name: '家用电器'},
            {id: '3', name: '计生情趣'},
            {id: '4', name: '男士服装'}
          ]
        }
      },
      methods: {
        // 发射事件: 自定义事件
        btnClick(item) {
          this.$emit("item-click",item)
        }
      }
    }

    // 2.父组件
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {},
      methods: {},
      components: {
        cpn
      },
      methods: {
        cpnClick(item) {
          console.log("cpnClick",item);
        }
      }
    });
  </script>

# 11 组件通信-父子组件通信案例

题目要求:1.子组件的变量双向绑定。2.子组件变量的修改影响父组件的值。3.第一个变量是第二个变量的十分之一

  <div id ="app">
    <cpn :dnumber1='number1' :dnumber2='number2' @inputnumber1='inputnumber1' @inputnumber2='inputnumber2'></cpn>
  </div>

  <template id="cpn">
    <div>
      <h2>props: {{dnumber1}}</h2>
      <h2>data: {{number1}}</h2>
      <!-- <input type="text" v-model="number1"> -->
      <input type="text" :value="number1" @input="inputNumber1">
      <h2>props: {{dnumber2}}</h2>
      <h2>data: {{number2}}</h2>
      <!-- <input type="text" v-model="number2"> -->
      <input type="text" :value="number2" @input="inputNumber2">
    </div>
  </template>

  <script>
    let cpn = {
      template: '#cpn',
      props: {
        dnumber1: Number,
        dnumber2: Number
      },
      data() {
        return {
          number1: this.dnumber1,
          number2: this.dnumber2
        }
      },
      methods: {
        inputNumber1(event) {
          // 1.将input中的value赋值到dnumber中
          this.number1 = parseFloat(event.target.value);
          // 2.为了让父组件可以修改值, 发出一个事件
          this.$emit('inputnumber1', event.target.value);
          // 3.同时修饰dnumber2的值
          this.number2 = this.number1 * 100;
          this.$emit('inputnumber2', this.number2);
        },
        inputNumber2(event) {
          this.number2 = event.target.value;
          this.$emit('inputnumber2', event.target.value);
          // 同时修改dnumber2的值
          this.number1 = this.number2 / 100;
          this.$emit('inputnumber1', this.number1);
        }
      }
    }
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        number1: 0,
        number2: 1
      },
      methods: {
        inputnumber1(value) {
          this.number1 =  parseFloat(value);
        },
        inputnumber2(value) {
          this.number2 = parseFloat(value);
        }
      },
      components: {
        cpn
      }
    });
  </script>

# 12 组件通信-父子组件通信案例(watch实现)

<div id="app">
  <cpn :number1="num1"
       :number2="num2"
       @num1change="num1change"
       @num2change="num2change"/>
</div>

<template id="cpn">
  <div>
    <h2>props:{{number1}}</h2>
    <h2>data:{{dnumber1}}</h2>
    <input type="text" v-model="dnumber1">
    <h2>props:{{number2}}</h2>
    <h2>data:{{dnumber2}}</h2>
    <input type="text" v-model="dnumber2">
  </div>
</template>

<script src="../js/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      num1: 1,
      num2: 0
    },
    methods: {
      num1change(value) {
        this.num1 = parseFloat(value)
      },
      num2change(value) {
        this.num2 = parseFloat(value)
      }
    },
    components: {
      cpn: {
        template: '#cpn',
        props: {
          number1: Number,
          number2: Number,
          name: ''
        },
        data() {
          return {
            dnumber1: this.number1,
            dnumber2: this.number2
          }
        },
        watch: {
          dnumber1(newValue) {
            this.dnumber2 = newValue * 100;
            this.$emit('num1change', newValue);
          },
          dnumber2(newValue) {
            this.number1 = newValue / 100;
            this.$emit('num2change', newValue);
          }
        }
      }
    }
  })
</script>

# 13 组件反问-父访问子-children-refs

  <div id ="app">
    <cpn ref="aaa"></cpn>
    <button @click="clickBtn">按钮</button>
  </div>

  <template id='cpn'>
    <div>我是子组件</div>
  </template>
  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {},
      methods: {
        clickBtn() {
          // console.log(this.$children);  //那到的是一个数组
          // console.log(this.$children[0]);  //拿到的是一个VueComponents对象
          // console.log(this.$children[0].name); //我是子组件的name
          
          console.log(this.$refs);  //拿到的是一个键值对对象
          console.log(this.$refs.aaa);  //拿到组件ref属性为aaa的组件
        }
      },
      components: {
        cpn: {
          template: '#cpn',
          data() {
            return {
              name: '我是子组件的name'
            }
          },
          methods: {
            cpnClick() {
              console.log('cpnclick');
            }
          }
        }
      }
    });
  </script>

# 14 组件访问-子访问父-parent-root

  <div id ="app"> 
    <cpn></cpn>
  </div>
  <template id="cpn">
    <div>
      <h2>我是cpn组件</h2>
      <ccpn></ccpn>
    </div>
  </template>

  <template id="ccpn">
    <div>
      <h2>我是子组件</h2>
      <button @click="btnClick">按钮</button>
    </div>
  </template>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {},
      methods: {},
      components: {
        cpn: {
          template: '#cpn',
          data() {
            return {
              name: '我是组件cpn'
            }
          },
          components: {
            ccpn: {
              template: '#ccpn',
              methods: {
                btnClick() {
                  console.log(this.$parent);  //VueComponent
                  console.log(this.$parent.name);  //我是组件cpn

                  console.log(this.$root);  //Vue(直接拿到根组件)
                }
              }
            }
          }
        }
      }
    });
  </script>

# 11 组件化高级开发

# 01 slot-插槽的基本使用

  <!-- 
    1.插槽的基本使用 <slot></slot>
    2.插槽的默认值 <slot>button</slot>
    3.如果有多个值,同时放入到组件进行替换时,一起作为替换元素
   -->
  <div id ="app"> 
    <cpn><button>按钮</button></cpn>
    <cpn><span>哈哈哈</span></cpn>
    <cpn>
      <i>呵呵呵</i>
      <div>我是div元素</div>  
      <p>我是p元素</p>
    </cpn>
    <cpn></cpn>
  </div>
  <template id="cpn">
    <div>
      <h2>组件化开发</h2>
      <p>我是组件内容</p>
      <!-- <slot></slot> -->
      <!-- 给插槽添加默认值 -->
      <slot><button>按钮</button></slot>  
    </div>
  </template>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {},
      methods: {},
      components: {
        cpn: {
          template: '#cpn'
        }
      }
    });
  </script>

# 02 slot-具名插槽的使用

给插槽添加一个name属性

  <div id ="app">
    <!-- 2.在组件实例中添加slot属性,确定修改哪个插槽 -->
    <cpn>
      <span slot="center">标题</span>
      <button slot="left">></button>
    </cpn>
  </div>
  <!-- 1.给插槽添加name属性 -->
  <template id="cpn">
    <div>
      <slot name="left">左边</slot>
      <slot name="center">中间</slot>
      <slot name="right">右边</slot>
    </div>
  </template>
  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {},
      methods: {},
      components: {
        cpn: {
          template: '#cpn'
        }
      }
    });
  </script>

# 03 什么是编译的作用域

  <div id ="app"> 
    <!-- 这里起作用的app.isShow -->
    <cpn v-show='isShow'></cpn>
  </div>

  <template id='cpn'>
    <div>
      <h2>我是子组件</h2>
      <p>我是子组件的内容</p>
      <!-- 这里起用的是组件的isShow -->
      <button v-show="isShow">按钮</button>
    </div>
  </template>

  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {
        isShow: true
      },
      methods: {},
      components: {
        cpn: {
          template: '#cpn',
          data() {
            return {
              isShow: false
            }
          }
        }
      }
    });
  </script>

# 04 作用域插槽的案例

父组件替换插槽的标签,但是内容由子组件来提供。

   <div id ="app"> 
    <cpn></cpn>
    <cpn>
      <!-- 2.通过slot-scope属性拿到组件的数据,新版本vue使用v-slot,而且不在需要template-->
      <template slot-scope="slot">
        <!-- <span v-for="item in slot.data">{{item}} -</span> -->
        <!-- 3.调用数据 -->
        <span>{{slot.data.join('-')}}</span>
      </template>
    </cpn>
  </div>

  <template id='cpn'>
    <div>
      <!-- 1.将组件中的数组对外开放 -->
      <slot :data="pLanguages">
        <ul>
          <li v-for="item in pLanguages">{{item}}</li>
        </ul>
      </slot>
    </div>
  </template>
  <script>
    //创建Vue实例,得到 ViewModel
    var app = new Vue({
      el: '#app',
      data: {},
      methods: {},
      components: {
        cpn: {
          template: '#cpn',
          data() {
            return {
              pLanguages: ['javascript','python','c++','Java','c#']
            }
          }
        }
      }
    });
  </script>

# 12 补充

# 01 时间总线

  • 概念:因为组件就是Vue的一个实例,通过给Vue对象的prototype添加一个属性,属性值依然是一个Vue实例,然后就可以在所有组件中使用属性的$emit和$on。

    1. 创建时间总线

      //在main.js当中
      
      Vue.prototype.$bus = new Vue() 
      
    2. 在组件当中使用总线的$emit发送事件

      this.$bus.$emit('goodsItemLoad');
      
    3. 在其他组件中监听事件总线发出的事件

      created() {
          this.$bus.$on('goodsItemLoad', () => {
            console.log('成功监听到时间总线')
          })
        }
      

# 02 防抖操作

  • 防抖debounce/节流throttle(课下研究一下)

  • 防抖函数起作用的过程:

    • 如果我们直接执行refresh, 那么refresh函数会被执行30次.
    • 可以将refresh函数传入到debounce函数中, 生成一个新的函数.
    • 之后在调用非常频繁的时候, 就使用新生成的函数.
    • 而新生成的函数, 并不会非常频繁的调用, 如果下一次执行来的非常快, 那么会将上一次取消掉
    mounted() {
        const refresh = this.debounce(this.$refs.scroll.refresh, 300);
        this.$bus.$on('goodsItemLoad', () => { 
          refresh();
        })
      },
      methods: {
        debounce(func, delay) {
          let timer = null;
          return function(...args) {
            if(timer) clearTimeout(timer)
            timer = setTimeout(() => {
              func.apply(this, args)
            }, delay)
          }
        }
      }
    
Last Updated: 11/20/2024, 2:55:49 PM