# Vue 基础

# 1. Vue.js 简介

在过去 10 年里,浏览器性能变得更加强大。通过 javascript,可以把以前在服务端处理的逻辑放在前端处理。这也造成了网页复杂度越来越高。

随着网页复杂度的增加,需要频繁的操作 DOM,如果使用 jquery 直接操作 DOM,代码会变得难以维护。这是 jquery 命令式操作 DOM 元素带来的弊端,为了解决这个问题,Vue.js 给提供声明式操作 DOM。

# 1.1 什么是 Vue.js

Vue.js 通常简称 Vue,是一款友好的、多用途且高性能的 JavaScript 框架,能够帮助我们创建可维护性更强的代码。它是目前所有主流框架中学习曲线最平缓的框架,非常容易上手,其官方文档也写得非常清晰、易懂。

Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。

渐进性的特性,就好比现在的苹果笔记本(非渐进性)和其他笔记本(渐进性),苹果笔记本有自己的雷电接口和 type-c 接口,你使用我的产品就必须根据我的标准来。而其他笔记本不但提供标准的 USB 接口,还提供雷电接口和 type-c 接口,你插任何设备都有很强的兼容性。

# 1.2 Vue 的现状

Vue2.0 与 Vue1.0 之间内部变化非常大,整个渲染层都重写了,但 API 层的变化却很少,可以看出,Vue 是非常注重用户体验和学习曲线的,它尽量让开发者用起来爽,同时在应用场景上,其他框架能实现的 Vue 都能做到。

到目前为止,Vue 最新版本是 2.6.12,它在 github 上的 star 数超过 17 万,超越 15.6 万的 react。在 npm 上 vue 每周有 170 万次下载量,这表示每周有 170 万开发者使用 Vue 开发应用。

Vue.js 在国内的用户有阿里巴巴、腾讯、百度、字节跳动、美团、新浪、网易、饿了么等等。

所以,作为一名前端工程师,学习 Vue.js 是很有必要的。

# 2. 第一个 Vue 程序

Vue.js 作为一个 javascript 库,使用方法是非常简便的,只需要新建一个 html 页面,在头部引入 Vue 文件,然后按 Vue 的语法编写代码即可,下面是完整的代码。

代码有点像之前学习 art-template,Vue 还提供了很多强大的模版语法,下面我们将逐一学习。

运行结果:

# 2.1 el 挂载点

Vue 实例的作用范围是什么?

我们把模版标签写到 Vue 实例作用范围外,页面未能显示helloworld,所以说 Vue 实例的作用范围只能在 el 标签内。

{{message}}
<div id="test">
  {{message}}
</div>
const app = new Vue({
  el: '#test',
  data: {
    message: 'hello world',
  },
});

运行结果:

是否可以使用其他选择器?

完全没问题,el 可以是 id 选择器,class 选择器,或者标签选择器(但是不能使用 HTML 和 BODY 标签),强烈建议使用 id 选择器,因为每个 Vue 实例最好有一个唯一的元素。

下面代码演示使用 class 选择器。

<div class="myApp">
  {{message}}
</div>
const app = new Vue({
  el: '.myApp',
  data: {
    message: 'hello world',
  },
});

运行结果:

总结

  1. Vue实例只在el挂在点内,在el外的元素Vue没法解析
  2. el可以支持任何js选择器,例如id选择器、class选择器和标签选择器

# 2.2 data 对象

页面上用到的数据,都会在 data 对象中定义,data 对象除了可以定义 string 类型外,还可以定义 javascript 支持的任何类型。下面例子演示定义数组类型和对象类型。

<div id="demo_2_2">
  <p>普通文本:{{message}}</p>
  <p>数字:{{num}}</p>
  <p>对象:{{student.name}}</p>
  <p>数组:{{heros[0]}}</p>
</div>
const app = new Vue({
  el: '#demo_2_2',
  data: {
    message: '普通文本',
    num: 1,
    student: { name: 'tom', age: 18 },
    heros: ['兰陵王', '安琪拉'],
  },
});

运行结果:

总结

  1. data对象可以是任意的javascript对象,例如字符串、对象、数组、数字、布尔值

  2. 在页面上读取数据的方法和普通js无异。`

# 3. 模版语法

我们以前学习的 art-template 有显示、循环、判断等语法,在 Vue 中它也有自己的模版语法,一般以v-开头,我们把这种语法统一称为指令。

例如v-textv-htmlv-onv-showv-ifv-bindv-forv-model

# 3.1 v-text

下面代码演示使用v-text显示data对象中的数据,也可以使用两个花括号输出内容。

一般在开发中,多有两个花括号 Mustache 语法输出内容,v-text标签只作了解。

<div id="demo_3_1">
  <!-- 使用v-text显示内容,标签内的内容会被覆盖 -->
  <p v-text="message">原来的内容</p>
  <!-- 拼接内容 -->
  <p v-text="message + '好劲'">原来的内容</p>
  <!-- 使用{{}}输出内容 -->
  <p>{{message}}</p>
  <!-- 拼接字符串 -->
  <p>{{message + '好耶'}}</p>
</div>
const app = new Vue({
  el: '#demo_3_1',
  data: {
    message: '少林功夫',
  },
});

运行结果:

总结

  1. v-text可以用来显示data中数据
  2. v-text会覆盖标签原本内容
  3. v-text内部也可以运行表达式

# 3.2 v-html

v-html标签用于解析 html 元素,它和v-text最大的区别是v-text会把传入字符串作为普通字符串原形输出,而v-html会把输入字符串作为 html 片段解析后再输出到页面上。

<div id="demo_3_2">
  <!-- 使用v-html指令,会对传入字符串作为html片段进行解析 -->
  <div v-html="message"></div>
  <!-- 使用v-text指令,传入数据会作为普通文本解析 -->
  <div v-text="message"></div>
</div>
const app = new Vue({
  el: '#demo_3_2',
  data: {
    message: '<h1>我系铁头功</h1>',
  },
});

运行结果:

总结

  1. v-html指令会把传入字符串作为html元素解析

# 3.3 v-bind

v-bind 通常用给标签动态设置属性值,例如 id,class,src 等属性,因为 Mustache 语法不支持标签属性。

下面案例在 Vue 实例 data 对象中定义classNameyellowBox,我们希望把className属性应用到 div 的 class 属性中。

使用 Mustache 语法的第一个元素在页面上无法显示。

<style>
  .yellowBox {
    background: yellow;
    width: 80px;
    height: 80px;
    border: 1px solid;
  }
</style>
<div id="demo_3_3">
  <!-- mustache语法没法显示属性,控制台会报错 -->
  <div class="{{className}}"></div>
  <!-- 正确写法是使用v-bind语法 -->
  <div v-bind:class="className">v-bind写法</div>
  <!-- 也可以通过冒号简写,和v-bind等效 -->
  <div :class="className">冒号简写</div>
</div>
<script>
const app = new Vue({
  el: '#demo_3_3',
  data: {
    className: 'yellowBox',
  },
});
</script>

运行结果:

总结

  1. v-bind语法用于标签属性设值

  2. 可以在标签上使用v-bind:属性名设置属性

  3. 可以省略v-bind,直接:属性名设置属性

# 3.4 javascript 表达式

所有的数据绑定,Vue.js 都提供了完全的 javascript 表达式支持,所谓表达式,就是例如运算符、三目表达式、判断符之类的。

<!-- 运算符 -->
<div>{{number + 1}}</div>
<!-- 条件判断 -->
<div>{{gender === '男'}}</div>
<!-- 三目运算符 -->
<div>{{gender === '男' ? '男孩子': '女孩子'}}</div>
<!-- v-bind也可以使用表达式 -->
<div v-bind:id="'list' + number"></div>
const app = new Vue({
  el: '#demo_3_4',
  data: {
    gender: '男',
    number: 1,
  },
});

运行结果:

总结

  1. 无论Mustache语法或者v-bind语法,都支持javascript表达式
  2. 不支持js语句,例如变量定义、if语句之类的。

# 3.5 v-for 列表渲染

我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名

<div id="demo_3_5">
  <li v-for="(item, index) in items">
    {{ index }} {{ item.message }}
  </li>
</div>
const app = new Vue({
  el: '#demo_3_5',
  data: {
    items: [{ message: 'Foo' }, { message: 'Bar' }],
  },
});

运行结果:

总结

  1. v-for指令用于根据数据生成列表结构

  2. 语法是(item,index) in 数组,item表示当前数组数据,index表示数组下标

# 3.5.1 v-for 里使用对象

v-for 语句后面如果是一个对象,例如下例中的 student,在循环的时候会得到三个参数,分别是value表示对象的值,key表示对象的 key 值,index表示循环索引。

<div id="demo_3_5_1">
  <li v-for="(value, key, index) in student">
    value: {{value}} key: {{key}} index: {{index}}
  </li>
</div>
const app = new Vue({
  el: '#demo_3_5_1',
  data: {
    student: {
      name: 'tom',
      age: 18,
      gender: '男',
    },
  },
});

运行结果:

总结

  1. v-for如果接收的是对象,将循环读取对象每个item

  2. 语法:(value, key, index) in 对象,value表示对象的值,key表示对象的key,index表示索引

# 3.6 v-if 条件渲染

v-if用于判断元素是否渲染,只有当指令收到值才会被渲染。

<div id="demo_3_6">
  <div v-if="isShow">Vue好棒棒</div>
</div>

也可以添加一个v-else块,它必须和v-if块一起出现,不能单独使用:

<div id="demo_3_6">
  <div v-if="isShow">Vue好棒棒</div>
  <div v-else>😯opps~</div>
</div>
const app = new Vue({
  el: '#demo_3_6',
  data: {
    isShow: true,
  },
});

运行结果:

总结

  1. v-if标签用于控制元素是否显示
  2. 实际是通过移除DOM元素实现隐藏
  3. 如果有多个条件,可以用v-else实现。

# 3.7 v-show 条件渲染

v-showv-if都可以用来决定元素是否显示,但是v-show是利用display:none的方式把元素隐藏起来,而v-if会在页面移除元素。当你的元素需要频繁的切换可见和非可见状态,建议使用v-show指令。

<div id="demo_3_7">
  <div v-show="isShow">元素显示了吗</div>
</div>
const app = new Vue({
  el: '#demo_3_7',
  data: {
    isShow: true,
  },
});

运行结果:

总结

  1. v-show指令用于切换元素显示状态
  2. 原理是修改元素display实现显示隐藏
  3. 如果元素频繁切换显示隐藏状态,建议使用v-show指令

# 3.8 v-model 双向绑定

你可以用 v-model 指令在表单 <input><textarea><select> 元素上创建双向数据绑定,指令会帮你自动把输入的值更新到数据中。

<div id="demo_3_8">
  <!-- v-model用于实现双向绑定,当用户在输入框输入内容时,会改变data对象的name属性值 -->
  <input type="text" v-model="name" />
  <!-- 这里显示的内容和输入框的内容会保持一致 -->
  <div>用户名:{{name}}</div>
</div>
const app = new Vue({
  el: '#demo_3_8',
  data: {
    name: 'tom',
  },
});

总结

  1. v-model指令用于方便获取表单值
  2. 绑定的数据会和表单形成关联
  3. 可通过读取绑定变量得到表单值

# 3.8.1 单选按钮

<div id="demo_3_8_1">
  <input type="radio" value="" id="man" v-model="gender" />
  <label for="man"></label>
  <input type="radio" value="" id="female" v-model="gender" />
  <label for="female"></label>
  <div>当前选择的值:{{gender}}</div>
</div>
const app = new Vue({
  el: '#demo_3_8_1',
  data: {
    gender: '男',
  },
});

运行效果:

总结

  1. 每个选项都需要绑定相同的数据
  2. 选择的项会自动把value值赋值给绑定数据

# 3.8.2 多项选择

多项选择只要把同一组选择框绑定到相同的v-model即可。

<div id="demo_3_8_2">
  <div>兴趣爱好:</div>
  <input
    type="checkbox"
    name="basketball"
    id="basketball"
    value="篮球"
    v-model="hobby"
  />
  <label for="basketball">篮球</label>
  <input
    type="checkbox"
    name="football"
    id="football"
    value="足球"
    v-model="hobby"
  />
  <label for="football">足球</label>
  <input type="checkbox" name="music" id="music" value="音乐" v-model="hobby" />
  <label for="music">音乐</label>
  <div>选中的爱好:{{hobby}}</div>
</div>
const app = new Vue({
  el: '#demo_3_8_2',
  data: {
    hobby: [],
  },
});

运行效果:

总结

  1. 选项需要绑定相同的数据
  2. 勾选选项后,会把选项value值添加到绑定数据中

# 3.9 事件处理

以前我们通过on事件名给 DOM 元素添加事件,例如添加点击事件就是onclick

<div onclick="login()">登录</div>

在 Vue 中,也可以给元素添加事件,但是他的写法是通过指令v-on:事件名

<div id="demo_3_9">
  <!-- 绑定的事件如果不传递参数,可以省略括号 -->
  <button v-on:click="login">登录</button>
  <!-- 如果要给函数传递参数,直接调用的时候传递即可 -->
  <button v-on:click="register('tom')">注册</button>
  <!-- v-on也可以用关键字@简写 -->
  <button @click="register('tom')">简写注册事件</button>
</div>

被调用的函数要定义在methods对象里面

const app = new Vue({
  el: '#demo_3_9',
  methods: {
    login() {
      alert('元素被点击了');
    },
    register(name) {
      alert(name);
    },
  },
});

运行效果:

总结

  1. 事件通过v-on指令或者@简写,用于给元素添加事件
  2. 绑定的函数需要在methods对象定义

# 4. 黑马记事本案例

下面通过一个案例把前面学习的知识做一个整合

运行效果:

记事本包含功能如下:

  1. 显示任务列表。

    实现思路:1.在data中定义一个todoList变量,用于存储任务列表。2.通过v-for语法把数据todoList显示到页面。

  2. 删除单个任务。

    实现思路:1.在methods中增加一个deleteTask函数。2.在页面上给元素添加点击事件@click 3. 点击元素时,传递删除元素的索引值。

  3. 删除所有任务。

    实现思路:1.在methods中增加一个removeAll函数,函数用于清空todoList数据。2. 给页面的元素添加点击事件并绑定removeAll函数。

  4. 增加任务。

    实现思路:1.使用v-model指令实现双向绑定,自动获取输入框的值。2.通过@keyup.enter事件监听用户按下回车事件。3. 在methods中定义addTask函数,函数实现把用户输入内容添加到数组中。

# 5. 其他特性

# 5.1 指令

除了Vue提供的核心指令,例如v-showv-model等,Vue也支持自定义指令,如果你需要对普通DOM元素进行底层操作,这时候就会用到指令。

思考一个场景,如果页面有一个输入框,我们希望进入页面后输入框自动获取焦点,可以怎么实现呢?

第一种方法:利用autofocus属性,但是该方法在iOS系统不支持。查看文档

<input type="text" autofocus>

第二种方法:调用input元素内置focus方法,在Vue中实现代码如下。查看文档

<div id="demo_5_1">
  <input type="text" id="myInput">
  <button @click="setFocus">获取焦点</button>
</div>
const app = new Vue({
  el: '#demo_5_1',
  methods: {
    setFocus() {
      document.querySelector('#myInput').focus()
    }
  }
});

第三种方法:虽然方法二解决了问题,但是逻辑代码和视图代码有耦合,不利于代码维护和服用,下面利用指令来实现相同效果。

<div id="demo_5_1">
  <input type="text" v-focus />
</div>
const app = new Vue({
  el: '#demo_5_1',
  //  指令都放在directives对象里
  directives: {
    // focus表示指令的名字
    focus: {
      // inserted表示元素显示到页面上的回调
      inserted(el) {
        // 调用focus方法给元素获取焦点
        el.focus();
      },
    },
  },
});

总结

  1. 当你需要操作DOM底层API可以考虑使用指令
  2. 指令统一定义在directives对象中
  3. directives对象里,对象的key值为指令的关键字,例如定义叫focus那页面上就是v-focus
  4. 可以在inserted钩子函数监听元素是否进入页面
  5. inserted函数的el参数是被添加指令的DOM元素

# 5.2 过滤器

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

下面案例通过定义一个名upper 的过滤器,过滤器的作用是把传入的数据变为大写。

<div id="demo_5_2">
 <!-- 双花括号插值 -->
 <div>{{ name | upper }}</div>
 <!-- v-bind使用拦截器 -->
 <div v-bind:id="name | upper"></div>
</div>
const app = new Vue({
  el: '#demo_5_2',
  data: {
    name: 'tom'
  },
  // 所有过滤器都定义在filters对象中
  filters: {
    // upper表示拦截器的名字,value表示拦截器管道前面的值
    upper: function(value) {
      return value.toUpperCase();
    }
  }
});

运行效果:

总结

  1. 过滤器一般用于格式化文本内容
  2. 过滤器统一在filters对象中定义
  3. filters对象里,key值作为过滤器的名字,对象的value值是可以函数,可以在函数中获取过滤器传入数据。
  4. 过滤器函数必须return

# 5.3 计算属性

模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:

<div id="demo_5_3">
  <!-- 第一个方法,在表达式内截取字符串 -->
  <div>{{ phone.substr(0, 4) + '****' + phone.substr(7)}}</div>
  <!-- 第二个方法,用计算属性生成显示属性 -->
  <div>{{ displayPhone }}</div>
</div>
const app = new Vue({
  el: '#demo_5_3',
  data: {
    phone: '18664694721'
  },
  computed: {
    displayPhone: function() {
      return this.phone.substr(0, 4) + '****' + this.phone.substr(7)
    }
  }
});

在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示隐藏中间四位数字的手机号。当你想要在模板中多处显示隐藏手机号,就会更加难以处理。

所以,对于任何复杂逻辑,你都应当使用计算属性

运行结果

总结:

  1. 计算属性一般用于处理界面复杂表达式
  2. 计算属性定义在computed对象
  3. 对象key值为计算后属性名,该值可以用于模版渲染
  4. 对象value值为函数,函数内定义计算逻辑,函数内包含data的属性当出现变化时,会自动重新运行该函数。

# 5.4 侦听属性

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

看下面例子,通过侦听关键字watch侦听data下的num属性,当num属性发生改变时,就会触发侦听函数

<div id="demo_5_4">
  购买数量:<input type="text" v-model="num">
  <div>如果购买数大于5,提示"超出最大购买数"</div>
</div>
const app = new Vue({
  el: '#demo_5_4',
  data: {
    num: 0
  },
  // 把需要侦听的属性放在watch对象中
  watch: {
    // 对象的key值为侦听的属性,该属性是data对象下的任意属性
    // 对象的value值是一个函数,函数会得到两个参数,第一个参数是新的值,第二个参数是旧的值
    num: function (newValue, oldValue) {
      console.log(newValue, 'newValue')
      console.log(oldValue, 'oldValue');
      // 如果num大于10,提示“超出最大购买数”
      const buyNum = parseInt(newValue);
      if (buyNum > 4) {
        alert('超出最大购买数')
      }
    }
  }
});

运行结果

总结:

  1. 侦听属性一般用于侦听现有属性的变化,常用于当需要在数据变化时执行异步或开销较大的操作时。
  2. 监听属性定义在watch对象下。
  3. 对象的key值为侦听的属性,该属性是data对象下的任意属性
  4. 对象的value值是一个函数,函数会得到两个参数,第一个参数是新的值,第二个参数是旧的值

# 5.4.1 深度侦听

侦听属性默认只侦听属性的第一级属性,如果需要侦听嵌套属性,需要加入deep属性。

<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>计算属性</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="demo_5_4_1">购买数量:<input type="text" v-model="a.b" /></div>
  </body>
  <script>
    const app = new Vue({
      el: '#demo_5_4_1',
      data: {
        a: {
          b: 1,
        },
      },
      watch: {
        a: {
          handler: function() {
            console.log('是否能侦听');
          },
          deep: true
        },
      },
    });
  </script>
</html>

# 5.5 计算属性 vs 侦听属性

计算属性和侦听属性特性非常相似,都可以监听属性的变更,很容导致同学们不知道怎么准确的使用。

一般情况下优先考虑计算属性,当计算属性没法满足的场景,或者你根本不需要计算多一个值用于渲染页面时,可以考虑侦听属性

下面例子分别通过侦听属性和计算属性实现名字的拼接。

<div id="demo">{{ fullName }}</div>

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})

上面代码是命令式且重复的。将它与计算属性的版本进行比较:

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})

# 5.6 购物车案例

使用计算属性实现购物车商品数量修改自动计算总价。

const app = new Vue({
  el: '#demo_5_6',
  data: {
    cart: [
      { title: '这是第一个商品', price: 100, amount: 5 },
      { title: '这是第二个商品', price: 200, amount: 3 }
    ],
  },
  computed: {
    totalPrice: function() {
      let total = 0;
      this.cart.forEach(item => {
        total += item.price * item.amount;
      })
      return total;
    }
  },
});

# 5.7 实例生命周期钩子

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。

比如 created 钩子可以用来在一个实例被创建之后执行代码:

new Vue({
  data: {
    a: 1
  },
  created: function () {
    console.log('a is: ' + this.a)
  }
})

# 6. 过渡 & 动画

# 6.1 HTML过渡动画实现

下面演示通过增加和删除class实现元素的淡入淡出动画效果,主要依赖css3过渡属性transition实现。

See the Pen 普通过渡动画 by 蟹老板 (@zhengguorong) on CodePen.

# 6.2 Vue过渡动画实现

因为在Vue中,是不建议直接操作DOM的,所以像上面的通过增加/删除类名的方式在Vue中不建议。

Vue提供了自己的过渡动画实现组件transition。在下列情形中,可以给任何元素和组件添加进入/离开过渡

  • 条件渲染 (使用 v-if)
  • 条件展示 (使用 v-show)
  • 动态组件
  • 组件根节点

这里是一个典型的例子:

# 过渡的类名

在进入/离开的过渡中,会有 6 个 class 切换。

  1. 组件隐藏变为显示, 称为 enter, 会按顺序切换以下 class
    • [动画名]-enter 是在变化开始, 出现之前
    • [动画名]-enter-active 正在变化 正在出现
    • [动画名]-enter-to 变化结束已经出现结束
  2. 组件显示变为隐藏, 称为 leave, 会按顺序切换以下 class
    • [动画名]-leave 是在消失之前
    • [动画名]-leave-active 正在消失
    • [动画名]-leave-to 消失结束

Transition Diagram

对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 <transition>,则 v- 是这些类名的默认前缀。如果你使用了 <transition name="my-transition">,那么 v-enter 会替换为 my-transition-enter

# vue实现过渡动画步骤:
  1. 使用按钮控制 div 的显示和隐藏

    v-if

  2. 使用 transition 标签包裹想要添加动画的 div

  3. 给 transition 标签添加 name 属性作为动画名

  4. 编写 css 样式谈出隐藏 div

  5. 编写 css 淡入显示 div

# 6.3 结合animate.css实现复杂动画

对于复杂的动画,自行编写会比较困难,可以借助animate.css第三方库实现。

# 6.3.1 在html中使用animate.css

1、引入animate.css

<link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
      />

2、给需要执行动画的元素添加类名

<h1 class="animate__animated animate__bounce animate__infinite">一个动画元素</h1>

animate__animated:是必须加入的,声明这是一个动画元素

animate__bounce:动画的类型,可以设置不同的动画名实现不同效果

animate__infinite:循环播放动画

See the Pen html使用animate.css by 蟹老板 (@zhengguorong) on CodePen.

# 6.3.2 在Vue中使用animate.css

Vue使用animate.css和普通页面使用方式是类似的,都是通过控制元素的class名实现。我们上面演示过Vue的transition组件自动会给元素添加默认的类名,例如进入的有v-enterv-enter-activev-enter-to,离开有v-leavev-leave-activev-leave-to。如果需要应用animate.css的动画,那把默认的class名修改成animate.css就可以实现了。

Vue的transition组件提供以下属性实现自定义过渡类名:

enter-class
enter-active-class
enter-to-class
leave-class
leave-active-class
leave-to-class

下面是vue实现例子

1、引入animate.css

<link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
      />

2、通过自定义属性修改过渡动画类名

    <div id="demo_6_3_2">
      <button @click="show = !show">
        切换显示隐藏状态
      </button>
      <transition
        enter-active-class="animate__animated animate__bounce"
        leave-active-class="animate__animated animate__shakeX"
      >
        <h1 v-if="show">一个动画元素</h1>
      </transition>
    </div>

3、控制元素显示状态触发动画

const app = new Vue({
  el: '#demo_6_3_2',
  data: {
    show: true
  }
});

最终效果

# 7. 组件入门

组件化是Vue等现代框架很重要的特性,其目的是把功能相似的代码封装到一个组件统一管理,减少重复代码和提升代码维护性。

# 7.1 基本示例

这里有一个 Vue 组件的示例:通过Vue.component('组件名', 组件配置) 创建组件

// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
  data() {
    return {
      count: 0
    }
  },
  template:
  '<button @click="count++">组件被点击了{{count}}</button>',
});

组件是可复用的 Vue 实例,且带有一个名字:在这个例子中是 <button-counter>。我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用:

<div id="demo_7_1">
  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>
const app = new Vue({
  el: '#demo_7_1',
  data: {}
})

# 7.2 组件里的 data 必须是一个函数

当我们定义这个 <button-counter> 组件时,你可能会发现它的 data 并不是像这样直接提供一个对象:

data: {
  count: 0
}

取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

data: function () {
  return {
    count: 0
  }
}

# 7.3 通过 Prop 向子组件传递数据

我们有一个新闻组件,如果在新闻组件写死内容,那组件就没有意义了,我们希望新闻组件可以根据传递内容显示不同信息,这就要用到prop属性了。

Vue.component('blog-post', {
  // 组件接收多少属性,就要在props对象定义多少个
  props: ['title', 'subtitle'],
  // 注意:template只能包含一个根结点
  template: `<div>
              <h3>{{ title }}</h3>
              <h4>{{ subtitle }}</h4>
            </div>`
})

一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。在上述模板中,你会发现我们能够在组件实例中访问这个值,就像访问 data 中的值一样。

一个 prop 被注册之后,你就可以像这样把数据作为一个自定义 attribute 传递进来:

<div id="demo_7_3">
  <blog-post title="第一条新闻" subtitle="子标题"></blog-post>
  <blog-post title="第二条新闻" subtitle="子标题"></blog-post>
  <blog-post title="第三条新闻" subtitle="子标题"></blog-post>
</div>

运行效果

# 7.4 传递动态数据

如果需要给组件传递动态数据,需要利用关键字bind或者冒号:传递变量。

<div v-for="article in articles">
  <blog-post :title="article.title" :subtitle="article.subtitle"></blog-post>
</div>
const app = new Vue({
  el: '#demo_7_3',
  data: {
    articles: [
      { title: '第一条新闻', subtitle: '第一条新闻子标题' },
      { title: '第二条新闻', subtitle: '第二条新闻子标题' },
      { title: '第三条新闻', subtitle: '第三条新闻子标题' }
    ]
  },
});

运行效果:

# 7.5 监听子组件事件

下面例子演示通过事件监听子组件事件,子元素如果要通知父元素,通过this.$emit('事件名', 参数)触发

See the Pen 监听子组件事件 by 蟹老板 (@zhengguorong) on CodePen.

# 7.6 兄弟组件值传递

See the Pen pobQBbV by 蟹老板 (@zhengguorong) on CodePen.