01vue之基础知识

1 概述

1.1 什么是Vue.js

是一套构建用户界面的渐进式框架。主要作用:动态构建用户界面

Vue 只关注视图层, 采用自底向上增量开发的设计

Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件

1.1.1 特点

1、 遵循MVVM模式

2、 编码简洁, 体积小, 运行效率高, 移动/PC端开发

3、 它本身只关注UI(视图层), 采用自底向上增量开发的设计,可以轻松引入vue插件和其它第三库开发项目

4、 它的目标是通过尽可能简单的API实现响应的数据绑定和组合的视图插件

5、 优点

  • Vue最大程度上减少了页面的DOM操作
  • 可以让开发人员更专注于业务操作
  • 通过简洁的指令结合页面结构与逻辑数据
  • 通过组件化方便模板重用以及增加可维护性
  • 代码结构更合理
  • 维护成本低
  • Vue.js解放了传统javascript中频繁的DOM操作

1.1.2 与其它框架的关联

1、 借鉴angular的模板和数据绑定技术

2、 借鉴react的组件化和虚拟DOM技术

1.2 单页面应用程序 SPA

单页面应用程序最主要的功能是实现前后端分离

1.2.1 网站交互方式

1、 经典的多页面

  • 前后端糅合在一起,开发和维护效率低下;以服务端为主导,前后端混合
  • 用户体验一般,一般是点击刷新跳转,等待时间过长
  • 每个页面都需要重新加载渲染,速度慢
  • 有利于SEO搜索引擎搜索

2、 现代式的单页面

  • 前后端分离,开发效率高,可维护性好
  • 前后端分离:服务端不关心页面,只关心数据;客户端不关心数据库和数据操作,只关心如何通过接口获取数据和服务端交互,处理页面
  • 用户体验好,就像一个原生的客户端软件一样使用
  • 只需要加载渲染局部视图即可,不需要整页刷新
  • 单页面技术兼容性一般是高于IE9及其以上
  • 单页面的数据都是异步加载过来,所以一般很难做SEO优化
  • 一般用于手机web网页、后台管理系统的开发等

1.3 vue安装使用方式

1.3.1 独立版本安装

官网上直接下载 vue.min.js 并用 <script> 标签引入

1.3.2 npm 下载安装

npm 相关命令

# 查看 vue-cli脚手架版本
npm -v

# 升级 npm
npm install npm -g

# 升级或安装 cnpm
npm install cnpm -g

下载安装vue

# 最新稳定版
npm install vue

# 如果想要查看当前项目的 vue版本,
# 可以找package.json文件, 找" dependencies "即可查看vue版本

2. MVVM模式

2.1 介绍

MVVM 是 vue 实现数据驱动视图和双向数据绑定的核心原理

  • M: model 模型,data数据对象,表示当前页面渲染时所依赖的数据源
  • V: view 视图 ,模板页面,表示当前页面所渲染的 DOM 结构
  • VM:ViewModel 视图模型 (new Vue的实例对象 var vm = new Vue({})):主要包括DOM监听和数据绑定2个最主要的模块

image

2.2 特点

2.2.1 数据驱动视图

在使用了 vue 的页面中,vue 会监听数据的变化,那么所有绑定该数据的 DOM 都会自动跟着改变重新渲染页面

当页面数据发生变化时,页面会自动重现渲染

数据驱动视图是单向数据绑定

2.2.2 双向数据绑定

在填写表单时,双向数据绑定可以辅助开发者在不操作 DOM 的前提下,自动把用户填写的内容同步到数据源 中;同时,当数据源发生变化时,也会将数据自动渲染到页面中

2.3 MVVM原理

ViewModel 作为 MVVM 的核心,是它把当前页面的数据源(Model)和页面的结构(View)连接在了一起
image

当数据源发生变化时,会被 ViewModel 监听到,VM 会根据最新的数据源自动更新页面的结构

当表单元素的值发生变化时,也会被 VM 监听到,VM 会把变化过后最新的值自动同步到 Model 数据源中

3 vue配置项

Vue实例 vm:每个 Vue 应用都是通过 Vue 构造函数创建一个新的 Vue实例开始的

var vm = new Vue({})

常用配置项

var Child = {
    template: '<h1>定义局部组件!</h1>'
};
var vm = new Vue({
    el: '#demo',
    data: {
        name:"五月",
        
        kilometers: 0, //千米
        meters: 0, //米

        jsonObj: [{
            name: '宋',
            age: '20'
        }, {
            name: '张',
            age: '30'
        }, {
            name: '刘',
            age: '40'
        }],
    },
    // 自定义方法  this是指当前的vue对象
    methods: {
        sayHello: function(name) {
            alert('您好,' + name);
        },
        //加数器
        addCount: function() {
            // 如果当前count字段没有被定义,那么必须使用Vue.set设置: Vue.set(this, 'count', '初始化value值');
            if (!this.count) {
                Vue.set(this, 'count', '1');
            } else {
                this.count++;
            }
        },
    },
    // 计算属性:可以不先在data对象中定义
    computed: { 
        //reversedMessageInfo可以直接当data对象中的数据使用
        reversedMessageInfo: function() {
            //this指向vm实例
            return this.messageInfo.split('').reverse().join('');
        }
    },
    // 监听属性:要监听的属性必须 是data中已经定义的属性
    watch: {
        kilometers: function(val) {
            this.kilometers = val;
            this.meters = val * 1000;
        },
        meters: function(val) {
            this.meters = val;
            this.kilometers = val / 1000;
        },
    },
    // 过滤器
    filters: {
        //首字母大写
        capitalize: function(value) {
            if (!value) return ''
            value = value.toString();
            return value.charAt(0).toUpperCase() + value.slice(1);
        },
        // 全部大写
        UpperCase: function(value) {
            return value.toUpperCase();
        }
    },
    // 局部组件的自定义
    components: {
        'childComponent': Child
    },
    // 局部自定义指令
    directives: {
        inserted: function(el) {
            el.focus();
        }
    }
})

3.1 el

指定dom标签容器的选择器 , Vue就会管理对应的标签及其子标签

3.2 data:初始化数据

1、 指定初始化状态数据的对象或函数,返回一个对象;主要是用来指定对象的初始化状态属性数据

2、 响应式数据,视图中绑定的数据必须是显式的初始化到 data

3、 由于Vue实例代理了data对象上所有的属性,因此访问 vm.a 等同于访问 vm.$data.a

4、 也可以通过 vm.$data访问原始数据对象

3.3 methods对象:自定义方法

1、 包含多个自定义方法的对象 ,可以供页面中的事件指令来绑定回调

2、 回调函数的参数中,如果默认不传参,会有一个默认的event 对象;如果有自定义参数,但是还要使用 evevnt 参数,就要 传递一个名为 $event 的参数,才可以获取到 event 对象

3、 methods 将被混入到 Vue 实例中。可以直接通过 VM 实例访问这些方法,或者在指令表达式中使用。方法中的 this 自动绑定为 Vue 实例

注意,最好不要使用箭头函数来定义 method 函数

是因为箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例

3.4 computed:计算属性

3.4.1 定义

一种带有行为的属性,本质是方法,但是不能当作方法来调用,必须当作属性来使用

3.4.2 特性

1、 计算属性只能当作属性来使用,不能用于事件处理函数

2、 是包含多个方法的对象 ,可以对 数据状态属性 进行计算返回一个新的数据,供页面获取显示

3、 执行时间:一是初始化 ; 二是计算属性里面相关的数据发生变化

4、 只要计算属性中依赖的数据源发生了变化,则计算属性就会自动重新计算

设置反转字符串

<div id="app">
  <p>原始字符串: {{ message }}</p>
  <p>计算后反转字符串: {{ reversedMessage }}</p>
</div>
 
<script>
var vm = new Vue({
  el: '#app',
  data: {
    message: 'Hello'
  },
  computed: {
    // 计算属性的 getter
    // reversedMessage命名必须和data中的属性不同名  
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
})
</script>

get和set

一般情况下,计算属性是一个 只读 的属性:即不可以在计算属性里面操作 this.name=‘XXX’

如果非要在计算属性里面实现数据的读取操作,可以使用set/get方法,实现对数据的设置和监听的变化

// 如何给对象定义get/set属性:
// 在创建对象时指定: 
get name () {return xxx} 
set name (value) {}

// 对象创建之后指定:
Object.defineProperty(obj, age, {get(){}, set(value){}})

计算属性里面的get和set

    //  计算属性FullName3(双向) 是一个对象
    FullName3: {
        //回调函数:当需要读取当前属性值时回调,计算并返回当前属性的值
        get() {
            return this.firstName + ' ' + this.lastName;
        },
        //回调函数:当属性值FullName3发生变化时回调,更新相关属性数据(firstName lastName) ,会监听数据的变化
        set(value) {
            //value就是fullName3的最新输入值
            this.firstName=value.split(' ')[0];
            this.lastName=value.split(' ')[1];
        }
    }  

3.5 计算属性computed与方法methods

3.5.1 computed

  • computed会存在基于它的缓存,只有相关的依赖缓存发生变化时,才会重新取值
  • 执行时间:一是初始化显示时,二是基于它依赖的data属性数据发生变化时
  • computed计算属性里面的回调函数的函数名不是data里面已经存在的属性名,要重新命名,并调用
  • 计算属性存在缓存,多次读取值执行一次getter计算

3.5.2 methods

  • 执行:在重新渲染的时候,函数总会调用执行
  • 每使用一次就调用一次,重复使用效率不高

3.6 watch:监听data属性的变化

监听属性里面的回调函数的函数名必须是 data 里面已经存在的属性名

可以包含多个属性监视的对象,一般有2个参数:第一个参数是属性变化后的新值newVal,第二个参数是原来的老值oldVal

分类:一般监视和深度监视(一般是用于监视 引用类型 的数据,可以监视 引用类型 的子数据)

4.6.1 一般变量的监听

watch:{
    username(newVal,oldVal){
        // TODO Something
    }
}    

4.6.2 对象的监听

watch:{
    userInfo:{
     // handler 处理函数   
     handler(newVal,oldVal) {
    	// TODO Something
      },  
    }
} 

默认情况下,组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要使 用 immediate 选项,表示立即监听

watch: {
    username: {
        handler: async function (newVal) {
           	// TODO Something
        },
        // 表示页面初次渲染好之后,就立即触发当前的 watch 侦听器
        immediate: true
    }
}

如果 watch 侦听的是一个对象,当对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选 项,表示深度监听

data:{
  userInfo:{username:'lisi'}
},
watch:{
    userInfo:{
     // handler 处理函数   
     handler(newVal,oldVal) {
    	// TODO Something
        console.log(newVal.username) 
      },  
      // 深度监听
      // 只要对象中任何一个属性发生变化了,都会触发 对象的监听器    
      deep:true
    }
} 

如果只想监听对象中单个属性的变化,则可以按照如下的方式定义 watch 侦听器

data:{
  userInfo:{username:'lisi'}
},
watch:{
    'userInfo.username':{
     // handler 处理函数   
     handler(newVal,oldVal) {
    	// TODO Something
        // newVal的值就是 username 
        console.log(newVal) 
      }
    }
} 

全局监听器

vm.$watch('xxx', function(value){})  

例如:监听千米和米之前单位的换算

<div id = "computed_props">
    千米 : <input type = "text" v-model = "kilometers">
      米 : <input type = "text" v-model = "meters">
</div>
<p id="info"></p>

<script type = "text/javascript">
    var vm = new Vue({
    el: '#computed_props',
    data: {
        kilometers : 0,
        meters:0
    },
    watch : {
        // 第一种监听方式(推荐)
        // 监听属性 必须在data对象中 先定义
        kilometers:function(val) {
            this.kilometers = val;
            this.meters = this.kilometers * 1000
        },
        meters : function (val) {
            this.kilometers = val/ 1000;
            this.meters = val;
        }
    }
    });
    
    // $watch 是一个实例方法
    // 第二种监听方式
    vm.$watch('kilometers', function (newValue, oldValue) {
    // 这个回调将在 vm.kilometers 改变后调用
    document.getElementById ("info").innerHTML = "修改前值为: " + oldValue + ",修改后值为: " + newValue;
        
})
</script>

3.7 computed和watch 案例

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title>computed计算属性和watch监听属性</title>
	</head>
	<body>
		<div id="demo">
			姓:<input type="text" placeholder="First Name" v-model="firstName"><br>
			名:<input type="text" placeholder="Last Name" v-model="lastName"><br>
			姓名1(计算属性-单向数据绑定):<input type="text" placeholder="full Name1" v-model="FullName1"><br>
			姓名2(监听属性-单向数据绑定):<input type="text" placeholder="full Name2" v-model="FullName2"><br>
			姓名3(计算属性-双向数据绑定):<input type="text" placeholder="full Name3" v-model="FullName3"><br>
		</div>
		<script type="text/javascript" src="lib/vue.js"></script>
		<script type="text/javascript">
			const vm = new Vue({
				el: '#demo',
				data: {
					firstName: 'A',
					lastName: 'B',
					FullName2: 'A B', // watch属性
				},
				computed: {

					//  计算属性FullName1(data中不需要事先定义)
					FullName1: function() {
						return this.firstName + ' ' + this.lastName;
					},

					/*
						上面的写法可以简写为:只是es6的一种简写的方法,没有任何实际意义
						FullName1() {
							return this.firstName + ' ' + this.lastName;
						},
					*/

					// 计算属性FullName3(双向数据绑定) 是一个对象
					FullName3: {
						// 回调函数:当需要读取当前属性值时回调,计算并返回当前属性的值
						get() {
							return this.firstName + ' ' + this.lastName;
						},
						// 回调函数:当属性值FullName3发生变化时回调,更新相关属性数据(firstName lastName)
						set(value) {
							// value就是fullName3的最新输入值
							this.firstName = value.split(' ')[0];
							this.lastName = value.split(' ')[1];
						}
					}
				},
				watch: {

					//第一种方法:配置监视
					//监视firstName属性(data中必须先定义)
					firstName: function(newVal, oldVal) {
						this.FullName2 = newVal + ' ' + this.lastName;
					}
				}
			});

			 /*
			 // 第二种监听的方法
			 // 监视lastName属性(data中必须先定义)
			 vm.$watch('lastName', function(newVal, oldVal) {
					 this.FullName2 = this.firstName + ' ' + newVal;
			 });
			 
			 */
		</script>
	</body>
</html>

3.8 Vue.set 新增对象属性

当我们给一个比如props中或者data中被观测的对象添加一个新的属性时,是不可以直接添加的,必须要通过Vue.set方法来设置

Vue.set方法用来新增对象的属性。如果要增加属性的对象是响应式的,那该方法可以确保属性被创建后也是响应式的,同时会触发视图的更新

// 当前count字段没有被定义,那么必须使用Vue.set设置:
//  Vue.set(this, 'count', '初始化value值');
export default {
	props: {
		food: {
			type: Object
		}
	},
	methods: {
		addCart() {
			if (!this.food.count) {
                // 这里的food对象是没有count属性的,所以我们要给他添加count属性,必须使用Vue.set方法
				Vue.set(this.food, 'count', '1');
			} else {
				this.food.count++;
			}
		}
	}
};

4 vue之基础使用

4.1 模板语法

Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据

Vue.js 的核心是一个允许你采用简洁的模板语法来声明式的将数据渲染进 DOM 的系统

结合响应系统,在应用状态改变时, Vue 能够智能地计算出重新渲染组件的最小代价并应用到 DOM 操作上

4.1.1 内容渲染(插值)

文本内容插值 {

数据绑定最常见的形式就是使用 {{...}}(双大括号)的文本插值

<div id="app">
  <p>{{ message }}</p>
</div>

文本内容插值 v-text

当作纯文本解析,可以解决 {{}} 表达式出现的闪现的问题,更新元素的 textContent

<div id="app">
    <div v-test="message">值是:</div>
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    message: '菜鸟教程'
  }
})
</script>

v-text 插值会把原来元素里面的值 覆盖掉

html插值 v-html

使用 v-html 指令用于输出 html 代码,更新元素的 innerHTML

<div id="app">
    <div v-html="message"></div>
</div>  

<script>
new Vue({
  el: '#app',
  data: {
    message: '<h1>菜鸟教程</h1>'
  }
})
</script>

注意:插值 {{}} 表达式只能用在 内容节点 中,不能用在元素的 属性节点 中

4.1.2 属性绑定 v-bind

如果需要为元素的属性动态绑定属性值,则需要用到 v-bind 属性绑定指令

v-bind 只能绑定属性,它的值是一个 js 表达式,与 {{ }} 里面的语法是一样的;唯一的区别是:{{}}用于标签文本绑定,v-bind 用于标签属性绑定

简写语法:直接用 : 替代 v-bind:

 <div v-bind:class="classname"></div>

 <!-- 注意:此处不是变量的部分一样要加 '' --> 
 <a :href="'todos/id='+item.id+">百度一下</a>

 <!-- 为 img 动态绑定 imgSrc 属性 -->
 <img :src='imgSrc' alt=''>

4.1.3 JS表达式

在 vue 提供的模板渲染语法中,除了支持绑定简单的数据值之外,还支持 Javascript 表达式的运算

<div id="app">
    {{5+5}}<br>
    {{ ok ? 'YES' : 'NO' }}<br>
    {{ message.split('').reverse().join('') }}
    <div v-bind:id="'list-' + id">hello vue</div>
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    ok: true,
    message: 'RUNOOB',
    id : 1
  }
})
</script>

4.1.4 一次性绑定 v-once

通过使用 v-once 指令,可以执行一次性的插值,当数据发生改变时,插值处的内容不会更新。

<span v-once>这个将不会变化 {{msg}} </span>

4.1.5 v-cloak解决表达式闪现问题

使用表达式 {{}} 的方式,容易出现闪现的问题

  1. 方法一:使用 v-text ,但是这种方式比较麻烦

  2. 方法二:使用 v-cloak 指令(推荐)

    v-cloak 原理:浏览器在解析的过程中,发现具有 v-cloak 的属性就隐藏不显示元素了,所以我们就看不见 {{}} 导致的闪现的问题了,当Vue 解析替换完成后,Vue会自动把 v-cloak 样式移除

// 1.  设置css样式
[v-cloak] {
  display: none;
}

// 2. 设置指令到指定的元素
<div v-cloak>
  {{ message }}
</div>

4.2 事件处理器

vue 提供了 v-on 事件绑定指令,用来辅助程序员为 DOM 元素绑定事件监听

<button v-on:click="sayHello">Hello</button>

<!-- 简写 -->
<button @click="sayHello">Hello</button>

案例

<div id="app">
   <button @click="sayHello">Hello</button>
</div>
<script src='vue.min.js'></script>
<script>
    new Vue({
        el: '#app',
        methods: {
            sayHello() {
                alert('Hello')
            }
        }
    })
</script>

4.2.1 event 对象

回调函数的参数中,如果默认不传参,会有一个默认的event 对象

<div id="app">
   <button @click="sayHello">Hello</button>
</div>
<script src='vue.min.js'></script>
<script>
    new Vue({
        el: '#app',
        methods: {
             // 如果不传参数,默认第一个参数就是 event 
            sayHello(event) {
                 // `event` 是原生 DOM 事件
                if (event) {
                    alert(event.target.tagName)
                }
            }
        }
    })
</script>

在原生的 DOM 事件绑定中,可以在事件处理函数的形参处,接收事件参数对象 event,同理,在 v-on 指令 (简写为 @ )所绑定的事件处理函数中,同样可以接收到事件参数对象 event

如果有自定义参数,但是还要使用 evevnt 参数,就要 传递一个名为 $event 的参数,才可以获取到 event 对象

$event 是 vue 提供的特殊变量,用来表示原生的事件参数对象 event

<div id="app">
   <button @click="sayHello('Jack',$event)">Hello</button>
</div>
<script src='vue.min.js'></script>
<script>
    new Vue({
        el: '#app',
        methods: {
            sayHello(name,event) {
                 // `event` 是原生 DOM 事件
                if (event) {
                    alert(event.target.tagName)
                }
            }
        }
    })
</script>

4.2.2 事件修饰符

在事件处理函数中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。因此, vue 提供了事件修饰符的概念,来辅助程序员更方便的对事件的触发进行控制。常用的 5 个事件修饰符如下

事件修饰符 说明
.prevent 阻止默认行为
.stop 停止事件冒泡
.capture 以捕获的模式触发事件处理函数
.once 绑定的事件只触发一次
.self 只有在event.target是当前元素自身时触发事件处理函数
<a href='www.baidu.com' @click.prevent='onClick'>百度一下试试</a>

4.2.3 按键修饰符

在监听键盘事件时,我们经常需要判断详细的按键。此时,可以为键盘相关的事件添加按键修饰符

<!-- enter键触发 -->
<input @keyup.enter="submit" />

<!-- esc键触发 -->
<input @keyup.sec="cancel" />

4.2.4 自定义事件

语法

事件名推荐使用 event-name 的方式

// 绑定监听事件
$on(eventName)

// 触发事件
$emit(eventName,optionalPayload)

4.3 v-model 双向数据绑定

vue 提供了 v-model 双向数据绑定指令,用来辅助开发者在不操作 DOM 的前提下,快速获取表单的数据

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <!-- 希望 Vue 能够控制下面的这个 div,帮我们在把数据填充到 div 内部 -->
  <div id="app">
    <p>用户的名字是:{{ username }}</p>
    <input type="text" v-model="username">
    <hr>
    <input type="text" :value="username">
    <hr>
    <select v-model="city">
      <option value="">请选择城市</option>
      <option value="1">北京</option>
      <option value="2">上海</option>
      <option value="3">广州</option>
    </select>
  </div>

  <!-- 1. 导入 Vue 的库文件,在 window 全局就有了 Vue 这个构造函数 -->
  <script src="./lib/vue-2.6.12.js"></script>
  <!-- 2. 创建 Vue 的实例对象 -->
  <script>
    // 创建 Vue 的实例对象
    const vm = new Vue({
      el: '#app',
      data: {
        username: 'zhangsan',
        city: '2'
      }
    })
  </script>
</body>

</html>

4.3.1 v-model 指令的修饰符

为了方便对用户输入的内容进行处理,vue 为 v-model 指令提供了 3 个修饰符

修饰符 作用 案例
.number 自动将用户的输入值转为数值类型
.trim 自动过滤用户输入的首尾空白字符
.lazy 在“change”时而非“input”时更新

4.4 条件渲染语句 v-if

条件渲染指令用来辅助开发者按需控制 DOM 的显示与隐藏

4.4.1 v-if条件判断

v-if 指令将根据表达式 ok的值(true 或 false )来决定是否插入(渲染或不渲染) 元素;

如果表达式的值为false,根本就不插入元素(即在页面中根本就没有这个元素)

<div id="app">
    <p v-if="ok">数据显示了</p>
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    ok: true
  }
})
</script>

与v-else结合使用

<div id="app">
    <p v-if="ok">数据显示了</p>
    <p v-else>数据不显示</p>
</div>

与v-else-if 结合使用

<div id="app">
    <div v-if="type === 'A'">
      A
    </div>
    <div v-else-if="type === 'B'">
      B
    </div>
    <div v-else-if="type === 'C'">
      C
    </div>
    <div v-else>
      Not A/B/C
    </div>
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    type: 'C'
  }
})
</script>

4.4.2 v-show展示元素

使用 v-show 指令来根据条件展示元素:如果表达式的值是true,就显示元素;否则就不显示;元素会一直存在的,只是显示和隐藏了

<h1 v-show="ok">Hello!</h1>

4.4.3 v-ifv-show的区别

实现原理不同

v-if

v-if 指令会动态地创建或移除 DOM 元素,从而控制元素在页面上的显示与隐藏

v-if 条件为true,就渲染元素到DOM中;条件为false,页面中就不渲染DOM(是指页面中根本就不存在这个元素)

v-show

v-show 指令会动态为元素添加或移除 style="display: none;" 样式,从而控制元素的显示与隐藏

v-show无论条件真假,都渲染到DOM中(页面中元素一直存在),它的显示和隐藏只是元素进行 display='none'display='block'的切换

性能消耗不同

v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销,因此:

  • 如果需要非常频繁地切换,则使用 v-show 较好

  • 如果在运行时条件很少改变,则使用 v-if 较好

4.5 列表渲染 v-for

vue 提供了 v-for 列表渲染指令,用来辅助开发者基于一个数组来循环渲染一个列表结构。v-for 指令需要使 用 item in items 形式的特殊语法

items:待循环的数组 item:被循环的数组的每一项

  <ul>
    <li v-for="item  in items">
      {{ item .name }}
    </li>
  </ul>

当前索引,是可选的第二个参数

  <ul>
    <li v-for="(item,index)  in items">
      {{ item .name }}
    </li>
  </ul>

4.5.1 关键字 key

当列表的数据变化时,默认情况下,vue 会尽可能的复用已存在的 DOM 元素,从而提升渲染的性能。但这种 默认的性能优化策略,会导致有状态的列表无法被正确更新

为了给 vue 一个提示,以便它能跟踪每个节点的身份,从而在保证有状态的列表被正确更新的前提下,提升渲 染的性能。此时,需要为每项提供一个唯一的 key 属性

  <ul>
    <li v-for="(item,index)  in items" :key="item.id">
      {{ item .name }}
    </li>
  </ul>

key注意事项

  • key 的值只能是字符串或数字类型
  • key 的值必须具有唯一性(即:key 的值不能重复)
  • 建议把数据项 id 属性的值作为 key 的值(因为 id 属性的值具有唯一性)
  • 使用 index 的值当作 key 的值没有任何意义(因为 index 的值不具有唯一性)
  • 建议使用 v-for 指令时一定要指定 key 的值(既提升性能、又防止列表状态紊乱)

4.5.2 迭代对象

1个参数:直接获取value

2个参数:获取value key 值

3个参数:获取value key index值

<div id="app">
  <ul>
    <!-- 1. 一个参数:直接获取value  -->
    <li v-for="value in object">
    {{ value }}
    </li>
    
     <!-- 2. 2个参数:获取value key 值  -->
     <li v-for="(value, key) in object">
          {{ key }} : {{ value }}
    </li>
      
     <!-- 3. 3个参数:获取value  key index值  -->
     <li v-for="(value, key, index) in object">
        {{ index }}. {{ key }} : {{ value }}
     </li>
      
  </ul>
</div>
 
<script>
new Vue({
  el: '#app',
  data: {
    object: {
      name: 'Jack',
      age:18
    }
  }
})
</script>

4.5.3 迭代整数

<div id="app">
  <ul>
    <li v-for="n in 10">
       {{ n }}
    </li>
  </ul>
</div>

4.6 样式绑定 v-bind

class 与 style 是 HTML 元素的属性,用于设置元素的样式,我们可以用 v-bind 来设置样式属性

4.6.1 class绑定字符串 :class="XXX"

<p :class="myClass">xxx是字符串变量</p>

<p :class="{classA: hasClassA, classB: hasClassB}">xxx是对象</p>

<p :class="['classA', 'classB']">xxx是数组</p>

XXX可以是:字符串变量、对象、数组

绑定对象1

<!-- isActive为true时,添加一个类名为activeClassName ,否则不添加类名  -->
<!-- hasError为true时,添加一个类名为textDangerClassName ,否则不添加类名  -->
<div v-bind:class="{ 'activeClassName': isActive,'textDangerClassName':hasError }"></div>

绑定对象2

<!-- classObject是一个对象 -->
<div v-bind:class="classObject"></div>
<script>
new Vue({
  el: '#app',
  data: {
    classObject: {
      active: true,
      'text-danger': true
    }
  }
})
</script>

绑定数组-很少用

<div v-bind:class="[activeClass, errorClass]"></div>

<script>
new Vue({
  el: '#app',
  data: {
    activeClass: 'active',
    errorClass: 'text-danger'
  }
})
</script>

4.6.2 内联样式绑定

直接绑定 : v-bind:style

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }">Hello</div>

<script>
new Vue({
  el: '#app',
  data: {
    activeColor: 'green',
	fontSize: 30
  }
})
</script>


绑定对象

<div v-bind:style="styleObject">HELLO</div>

<script>
new Vue({
  el: '#app',
  data: {
    // 样式对象  
    styleObject: {
      color: 'green',
      fontSize: '30px'
    }
  }
})
</script>

绑定数组

<div v-bind:style="[baseStyles, overridingStyles]">HELLO</div>

<script>
new Vue({
  el: '#app',
  data: {
    baseStyles: {
      color: 'green',
      fontSize: '30px'
    },
	overridingStyles: {
      'font-weight': 'bold'
    }
  }
})
</script>

4.7 过滤器 filter

vue.js运行我们自定义一些过滤器,一般会被用作一些常见的文本格式化,有管道符指示

过滤器的主要功能是对要操作的数据进行格式化设置

过滤器一般用在两个地方:插值表达式 和 v-bind 属性绑定

<!-- 在 两个大括号 {{}}  中 -->
{{ message | capitalize }}

<!-- 在 v-bind 指令中 -->
<div v-bind:id="rawId | formatId"></div>

4.7.1 全局过滤器

定义:

第一个参数:过滤器的名称

第二个参数:全局过滤器的 处理函数

Vue.filter(filterName,function(value[,arg1,arg2,...]){
    //进行数据处理
    // 注意:过滤器中,一般一定要有一个返回值
    return newValue
})  

案例:格式化时间

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>过滤器-</title>
  <script type="text/javascript" src="lib/vue.js"></script>
  <script type="text/javascript" src="lib/moment.min.js"></script>
</head>
<body>
<div id="test">
  <h2>格式化日期</h2>
  <p>年-月-日 时:分:秒==={{ date | dateString }}</p>
  <p>年-月-日==={{ date | dateString('YYYY-MM-DD')}}</p>
  <p>时:分:秒==={{ date | dateString('HH:mm:ss')}}</p>
</div>

<script>
  //  自定义一个全局过滤器
  //  value的值就是this.date的值 {{date|dateString}}传值
  //  format是参数  
  Vue.filter('dateString', function (value, format) {
    //moment().format('MMMM Do YYYY, h:mm:ss a') 如果不传参,默认是对当前时间格式化
    return format ? moment(value).format(format) : moment(value).format('YYYY-MM-DD HH:mm:ss');

    //或者是下面的写法 更加简洁
    //return moment(value).format(format||'YYYY-MM-DD HH:mm:ss');
  });

  // //使用ES6语法,设置默认值
  // Vue.filter('dateString', function (value, format='YYYY-MM-DD HH:mm:ss') {
  //     return  moment(value).format(format);
  // });

  new Vue({
    el: '#test',
    data: {
      date: new Date()
    }
  })
</script>
</body>
</html>

4.7.2 局部过滤器

案例:首字母大写

<div id="app">
  {{ message | capitalize }}
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    message: 'runoob'
  },
  filters: {
    capitalize: function (value) {
      if (!value) return ''
      value = value.toString()
      return value.charAt(0).toUpperCase() + value.slice(1)
    }
  }
})
</script>

4.7.3 过滤器传参

过滤器的本质是 JavaScript 函数,因此可以接收参数

<p>{{ message | filterA(arg1,arg2) }}</p>

// 第一个参数:永远都是 管道符 前面待处理的值
// 从第二个参数开始,才是 传递过来的形参
Vue.filter('filterA',(msg,arg1,arg2)=>{
  // TODO
})

vue3中,过滤器已经弃用

4.8 侦听器

watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作

局部侦听器中,都是被定义在watch对象中

侦听器本质上是一个函数,要监视哪个数据的变化,就把数据名作为方法名即可,因此监听属性里面的回调函数的函数名必须是 data 里面已经存在的属性名

  const vm = new Vue({
      el: '#app',
      data: {
        username: 'admin'
      },
      watch: {
        // 新值在前,旧值在后
        username(newVal) {
          // TODO
        }
      }
    })

具体参考 vue配置项

4.9 表单

在vue.js表单中,可以用 v-model 指令在表单控件元素上创建双向数据绑定

v-model 会根据控件类型自动选取正确的方法来更新元素

4.9.1 输入框input和textarea

直接绑定变量即可

<div id="app">
    <h1>input 元素:</h1>
    <!-- 绑定message1数据 -->
    <input v-model="message1" placeholder="编辑我……">
    <p>消息是: {{ message1 }}</p>

    <h1>textarea 元素:</h1>
    <p style="white-space: pre">{{ message2 }}</p>
    <!-- 绑定message2数据 -->
    <textarea v-model="message2" placeholder="多行文本输入……"></textarea>
</div>

<script src='vue.min.js'></script>

<script>
    new Vue({
        el: '#app',
        data: {
            message1: 'hello vue.js',
            message2: '欢迎使用hello vue.js'
        }
    })
</script>

4.9.2 复选框checkbox

复选框如果是一个,就设置一个逻辑值就行;

如果是多个,就绑定到同一个数组中

	
<div id="app">
    <p>单个复选框:</p>
    	<input type="checkbox" v-model="checked">
    	<label>{{ checked }}</label>

    <p>多个复选框:</p>
    	<input type="checkbox" value="苹果" v-model="checkedNames">
    	<label>苹果</label>
    	<input type="checkbox" value="香蕉" v-model="checkedNames">
    	<label>香蕉</label>
    	<input type="checkbox" value="栗子" v-model="checkedNames">
    	<label>栗子</label>
    <br>
    <span>选择的值为: {{ checkedNames }}</span>
</div>

<script src='vue.min.js'></script>

<script>
    new Vue({
        el: '#app',
        data: {
            checked: false, // 单个复选框
            checkedNames: []  // 存储多个复选框里面的选值
        }
    })
</script>

4.9.3 单选按钮radio

<div id="app">
    <input type="radio" value="苹果" v-model="picked">
    <label>苹果</label>
    <br>
    <input type="radio" value="香蕉" v-model="picked">
    <label>香蕉</label>
    <br>
    <span>选中值为: {{ picked }}</span>
</div>

<script src='vue.min.js'></script>

<script>
    new Vue({
        el: '#app',
        data: {
            picked: '苹果'
        }
    })
</script>

4.9.4 select下拉列表

 <div id="app">
    <select v-model="selected" name="fruit">
        <option value="">选择一个水果</option>
        <option value="苹果">苹果</option>
        <option value="香蕉">香蕉</option>
        <option value="梨子">梨子</option>
        <option value="芒果">芒果</option>
    </select>

    <div>
        选择的水果是: {{selected}}
    </div>
</div>

<script src='vue.min.js'></script>

<script>
    new Vue({
        el: '#app',
        data: {
            selected: ''
        }
    })
</script>

5 vue之生命周期

5.1 概述

生命周期(Life Cycle)是指一个组件从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段。 生命周期函数:是由 vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行

每个Vue实例在被创建时,都要经历一系列的初始化过程,例如:设置数据监听、编译模板、将实例挂载到DOM,并在数据发生变化时,实时更新到视图等,这个过程就是Vue的生命周期

生命周期函数:是由 vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行

5.2 生命周期的各个阶段

vue的生命周期可以大致分为:初始化显示、更新显示、销毁死亡
image

image

5.2.1 初始化显示

beforeCreate()

组件实例化之前,还没有创建vue对象,组件里面的 data props mehtods 尚未被创建,都是不可用状态

一般可以用于初始化动画

created()

组件实例化完成,组件里面的 data props mehtods 已经被创建 都是可用的状态; 但是组件的模板结构尚未生成,页面没有显示,页面中的dom也不可以使用

此时会寻找页面中是否存在el属性或$mount:如果都没有,那么生命周期直接结束;如果有el属性或$mount,会接着查找页面中是否有template,如果没有,生命周期结束

一般用于是发送ajax请求获取数据、结束动画,启动定时器等异步任务

beforeMount()

将在内存中编译好的html结构渲染到浏览器中,但是此时浏览器中还没有当前组件的DOM结构

mounted()

已经把内存中编译好的html结构渲染到了浏览器中,此时浏览器中已经包含了当前组件的DOM结构

5.2.2 更新显示

beforeUpdate()

当数据发生变化时,将根据变化后的、最新的数据重新渲染到模板结构

updated()

根据最新的数据,完成了组件DOM的重新渲染

5.2.3 销毁死亡

beforeDestory()

将要销毁此组件,此时尚未销毁,组件还处于正常工作状态

destoryed()

当调用vm.$destory()的时候,当前实例就会被销毁了

组件已经被销毁,此组件在浏览器中对应的DOM结构已经完全被移除

一般是做收尾工作,例如清除定时器等

13. vue之指令

13.1 简介

1、 指令 (Directives) 是带有 v- 前缀的特殊属性。指令属性的值预期是单个 JavaScript 表达式(v-for 是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM

2、 当你不可避免的需要操作 DOM 的时候,可以使用自定义指令来解决

3、 自定义指令的注册分为:全局注册、局部注册

  • 全局注册:在任何组件中都可以使用
  • 局部注册:只能在当前组件使用

13.2 自定义指令

13.2.1 注册全局指令

  • 第一个参数:指令的名称,如果是驼峰命名法myFocus,则在使用的时候需要把驼峰转为小写使用 - 连接起来v-my-focus
  • 第二个参数:配置指令的生命钩子函数:
    • el:作用于该指令的DOM对象
    • binding:包含指令相关信息数据的对象
// 注册一个全局指令myFocus,主要用于在页面加载时,获取元素的焦点
Vue.directive('myFocus', {
    inserted: function(el, binding) {
        el.focus();
    }
})

// 在很多时候,你可能想在 bind 和 update 时触发相同行为,而不关心其它的钩子。比如这样写
Vue.directive('myFocus', function(el, binding) {
    el.focus();
})


// 使用指令 v-my-focus
<div id="app">
    <input type="text" v-my-focus >
</div>

案例展示:模拟 v-show 实现

	
	// 模拟 v-show 实现,根据值的真假来显示或者隐藏作用该指令的元素
			Vue.directive('my-show', {
				bind(el, binding) {},
				inserted(el, binding) {
					if (binding.value) {
						el.style.display = 'block'
					} else {
						el.style.display = 'none'
					}
				},
				//  update 和 componentUpdated 只有在指令的绑定的值发生更新的时候才会触发调用
				// update 和 componentUpdated 的区别是:
				//    update 中获取的是更新的之前的指令所在的 DOM 内容
				//    componentUpdated 获取的是更新之后的最新 DOM 内容
				update(el, binding) {
					if (binding.value) {
						el.style.display = 'block'
					} else {
						el.style.display = 'none'
					}
				},
				componentUpdated(el, binding) {
					console.log('my-show componentUpdated', el.innerHTML)
				},
				unbind() {
					console.log('my-show unbind')
				}
			})
			

			// 如果只关心 bind 和 update,而且bind和update里面的逻辑完全一样,可以这样简写
			Vue.directive('my-show', function(el, binding) {
				if (binding.value) {
					el.style.display = 'block'
				} else {
					el.style.display = 'none'
				}
			})

案例展示: 模拟 v-bind 指令功能

	
// v-bind 作用:动态绑定属性值
Vue.directive('my-bind', {
    bind(el, binding) {
        el.setAttribute(binding.arg, binding.value)
    },
    update(el, binding) {
        el.setAttribute(binding.arg, binding.value)
    }
})

// 很多时候,我们都会在 bind 和 update 中执行相同的代码,可以使用简写的方式
// 直接给一个函数,该函数会被作为 bind 和 update 的时候执行的函数
Vue.directive('my-bind', function(el, binding) {
    el.setAttribute(binding.arg, binding.value)
})

案例展示:模拟v-bind变量和class

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Vue自定义指令 模拟v-bind变量和class</title>
  <style>
    .box {
      background-color: pink;
    }

    .box2 {
      color: red;
    }
  </style>
</head>
<body>
  <div id="app">
    <h1 v-my-bind:title="123">{{ message }}</h1>
    <input type="text" v-model="message">
    <input type="checkbox" v-model="toggleBox"> toggleBox
    <input type="checkbox" v-model="toggleBox2"> toggleBox2
    <div v-my-bind:class="{box: toggleBox, box2: toggleBox2}">
      hello vue
    </div>
  </div>
  <script src="node_modules/vue/dist/vue.js"></script>
  <script>
    // v-bind:class = "{}"
    // v-bind:class = "xxx"
    Vue.directive('my-bind', function (el, binding) {
      if (binding.arg === 'class') {
        for (let key in binding.value) {
          if (binding.value[key]) {
            // 作用类名
            el.classList.add(key)
          } else {
            el.classList.remove(key)
          }
        }
      } else {
        el.setAttribute(binding.arg, binding.value)
      }
    })
    const app = new Vue({
      data: {
        message: 'Hello Vue.js!',
        toggleBox: true,
        toggleBox2: true
      },
      methods: {}
    }).$mount('#app')
  </script>
</body>
</html>

13.2.2 注册局部指令

<div id="app">
    <p>页面载入时,input元素自动获取焦点:</p>
    <input v-focus>
</div>

<script>
    new Vue({
        el:"#app",
        directives : {
            'focus':{
                inserted:function(el){
                    // 聚焦元素
                    el.focus()
                }
            }
        }
    })
</script>    

注意:指令名不可以大写 类似这种方式:caseName

13.2.3 vue常见的内置指令

  • v-text:更新元素的textContent
  • v-html:更新元素的innerHTML
  • v-if:如果为true,当前标签输出到页面
  • v-else:如果为false,当前标签输出到页面
  • v-show:通过控制display样式来显示/隐藏元素
  • v-for:遍历数组/对象
  • v-on:绑定事件监听,一般是简写为@
  • v-bind:强制绑定解析表达式,简写为:
  • v-model:双向数据绑定
  • ref:指定唯一标识(pCont),vue对象通过$refs属性访问这个元素对象 this.$refs.pCont.textContent
  • v-cloak:防止闪现,一般是与css配合 [v-cloak]
  • v-pre : 告诉Vue不要解析该节点及其内部节点;可以提高vue的解析速度,避免非必要的节点解析

案例展示

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title>自定义指令</title>
		<meta name="keywords" content="关键字" />
		<meta name="description" content="描述" />
		<style>
			/* 防止闪现 */
			[v-cloak] {
				display: none;
			}
		</style>
	</head>
	<body>
		<div id="demo" style="width: 1200px;margin: 0 auto">
			<p>页面载入时,input自动获取焦点</p>
			<input type="text" v-focus placeholder="页面载入时,自动获取焦点">

			<p v-upper-case-text="msg">转换为大写的字母:{{msg}}</p>
			<p v-lower-case-text="msg">转换为小写的字母:{{msg}}</p>

			<!--ref设置唯一标识-->
			<h2 ref="pCont">通过v-text获取内容数据</h2>
			<button @click="getCont">点击获取内容数据</button>

			<!-- v-clack防止闪现
				第一次加载的时候,页面是显示{{msg}},需要等待解析完毕再显示相应的内容
				如果加上v-cloak指令,那么他就会在解析完毕以后才会显示相应的内容  -->
			<h2 v-cloak>{{msg}}</h2>
		</div>
		<script type="text/javascript" src="lib/vue.js"></script>
		<script>
			/*
			 * 注意:指令名不可以大写 类似这种方式:caseName
			 * 注册全局指令
			 * Vue.directive("my-directive-name",function(el,binding){
			 *       el.innerHTML=binding.value.toupperCase()
			 * })
			 *
			 * 注册局部指令
			 * directives:{
			 *   'my-directive-name':function(el,binding){
			 *        el.innerHTML=binding.value.toupperCase()
			 *   }
			 * }
			 *
			 * 使用指令
			 * my-directive-name=XXX
			 *
			 * */

			//自定义一个全局指令,获取元素的焦点
			Vue.directive('focus', {
				// 被绑定元素插入父节点时调用
				inserted: function(el) {
					el.focus(); //获取焦点
					console.log('inserted');
				},
				// 只调用一次,指令第一次绑定到元素时调用(经常用于初始化动作)
				bind: function(el, binding) {
					console.log('bind');
				},
				// 被绑定元素所在模板完成一次更新时调用,而不论绑定时是否变化
				update: function() {
					console.log('update');
				},
				// 被绑定元素的所在模板完成一次更新周期时调用
				componentUpdated: function() {
					console.log('componentUpdated');
				},
				// 只调用一次,指令与元素解绑时调用
				unbind: function() {
					console.log('unbind');
				}
			});
			// 大写(这是一种简写的方式)
			Vue.directive('upper-case-text', function(el, binding) {
				el.textContent = binding.value.toUpperCase();
			});

			var vm = new Vue({
				el: '#demo',
				data: {
					msg: 'Hello World'
				},
				methods: {
					getCont() {
						alert(this.$refs.pCont.textContent);
					}
				},
				directives: {
					'lower-case-text': {
						bind: function(el, binding) {
							el.textContent = binding.value.toLowerCase();
						}
					},
				}
			});
		</script>
	</body>
</html>

13.3 指令之钩子函数

13.3.1 钩子函数

指令定义函数提供了几个钩子函数(可选)

bind:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作;另外此时是获取不到父元素的

inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中);此时是可以获取父元素的

bind 和 inserted 的相同之处是一上来都执行一次,以后再也不会执行;异同之处在于,bind 拿不到父元素,inserted 可以拿到父元素

update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新

componentUpdated: 被绑定元素所在模板完成一次更新周期时调用

update 和 componentUpdated 只有在指令绑定的值发生更新的时候才会触发调用;

update 和 componentUpdated 的区别是:

  update 中获取的是更新的之前的指令所在的 DOM 内容,update 拿到的是数据改变视图之前的视图内容
  componentUpdated 获取的是更新之后的最新 DOM 内容,componentUpdated 拿到的是数据改变视图之后的视图内容

unbind: 只调用一次, 指令与元素解绑时调用;一般是做一些收尾工作

13.3.2 钩子函数参数

el: 指令所绑定的元素,可以用来直接操作 DOM

binding: 一个对象,包含以下属性:

  • name: 指令名,不包括 v- 前缀。
  • value: 指令的绑定值, 例如: v-my-directive="1 + 1", value 的值是 2
  • oldValue: 指令绑定的前一个值,仅在 updatecomponentUpdated 钩子中可用。无论值是否改变都可用。
  • expression: 绑定值的表达式或变量名。 例如 v-my-directive="1 + 1" , expression 的值是 "1 + 1"
  • arg: 传给指令的参数。例如 v-my-directive:foo, arg 的值是 "foo"
  • modifiers: 一个包含修饰符的对象。 例如: v-my-directive.foo.bar, 修饰符对象 modifiers 的值是 { foo: true, bar: true

vnode: Vue 编译生成的虚拟节点

oldVnode: 上一个虚拟节点,仅在 updatecomponentUpdated 钩子中可用

<div id="app"  v-runoob:hello.a.b="message">
</div>
 
<script>
Vue.directive('runoob', {
  bind: function (el, binding, vnode) {
    var s = JSON.stringify
    el.innerHTML =
      'name: '       + s(binding.name) + '<br>' +
      'value: '      + s(binding.value) + '<br>' +
      'expression: ' + s(binding.expression) + '<br>' +
      'argument: '   + s(binding.arg) + '<br>' +
      'modifiers: '  + s(binding.modifiers) + '<br>' +
      'vnode keys: ' + Object.keys(vnode).join(', ')
  }
})
new Vue({
  el: '#app',
  data: {
    message: 'hello vue.js'
  }
})
</script>

14. vue之过渡 & 动画

14.1 介绍

  • 利用vue去操控css的transition/animation动画
  • vue会给目标元素自动添加特定的class
  • 过渡的常见类名
XXX-enter-active:指定显示的trasition
XXX-leave-active:指定隐藏的trasition
XXX-leave-to/XXX-enter:指定隐藏时的样式

XXX-enter 显示之前
XXX-enter-to 显示之后
XXX-enter-active 显示过程中

XXX-leave 隐藏之前
XXX-leave-to 隐藏之后
XXX-leave-active 隐藏过程中
 

14.2 模板

使用<transition name='xxx'></transition> 包含带动画的标签

14.3 css样式

.fade-enter-active: 进入过程, 指定进入的transition
.fade-leave-active: 离开过程, 指定离开的transition
.xxx-enter, .xxx-leave-to: 指定隐藏的样式

14.4 案例展示

14.4.1 样式文件

//指定进入、离开过程的样式
    .xxx-enter-active, .xxx-leave-active {
      transition: opacity .5s
    }
    //指定隐藏的样式
    .xxx-enter, .xxx-leave-to {
      opacity: 0
    }
    

14.4.2 html文件

<transition name="xxx">
	<p v-if="show">hello</p>
</transition>
    

15. vue之混入

混入 (mixins)定义了一部分可复用的方法或者计算属性

混入对象可以包含任意组件的选项

当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项

案例展示

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例</title>
<script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script>
</head>
<body>
<div id = "databinding"></div>
    
<script type = "text/javascript">
var vm = new Vue({
	el: '#databinding',
	data: {
	},
	methods : {
	},
});
    
// 定义一个混入对象
var myMixin = {
	created: function () {
		this.startmixin()
	},
	methods: {
		startmixin: function () {
			document.write("欢迎来到混入实例");
		}
	}
};
    
<!-- 声明混入对象 Vue.extend-->    
var Component = Vue.extend({
	mixins: [myMixin]
})
    
var component = new Component();
    
</script>
</body>
</html>

15.1 选项合并

当组件和混入对象含有同名选项时,这些选项将以恰当的方式混合

比如,数据对象在内部会进行浅合并 (一层属性深度),在和组件的数据发生冲突时以组件数据优先

// Vue 实例与混入对象包含了相同的方法
var mixin = {
    created: function () {
        document.write('混入调用' + '<br>')
    }
}
new Vue({
        mixins: [mixin],  设置混入
        created: function () {
        document.write('组件调用' + '<br>')
    }
});
返回结果: 
	混入调用
	组件调用

15.2 Vue实例优先级高

如果 methods 选项中有相同的函数名,则 Vue 实例优先级会较高

// 混入对象
var mixin = {
    methods: {
        hellworld: function () {
            document.write('HelloWorld 方法' + '<br>');
        },
        samemethod: function () {
            document.write('Mixin:相同方法名' + '<br>');
        }
    }
};
var vm = new Vue({
    mixins: [mixin],
    methods: {
        start: function () {
            document.write('start 方法' + '<br>');
        },
        samemethod: function () {
            document.write('Main:相同方法名' + '<br>');
        }
    }
});
vm.hellworld();
vm.start();
vm.samemethod();

输出结果
HelloWorld 方法
start 方法
Main:相同方法名


15.3 全局混入

注意使用! 一旦使用全局混入对象,将会影响到 所有 之后创建的 Vue 实例。

使用恰当时,可以为自定义对象注入处理逻辑。

// 为自定义的选项 'myOption' 注入一个处理器。
Vue.mixin({
  created: function () {
    var myOption = this.$options.myOption
    if (myOption) {
      console.log(myOption)
    }
  }
})
 
new Vue({
  myOption: 'hello!'
})
// => "hello!"

谨慎使用全局混入对象,因为会影响到每个单独创建的 Vue 实例 (包括第三方模板)

17. 数据代理

17.1 定义

通过一个对象代理对另一个对象中属性的操作(读/写):例如可以通过vm对象来代理data对象中所有属性的操作

好处:可以更方便的操作data中的数据

17.2 实现原理

2.1 通过Object.defineProperty()给vm添加与data对象的属性对应的属性描述符

2.2 所有添加的属性都包含getter/setter

2.3 在getter/setter内部去操作data中对应的属性数据

18. 模板解析 compile.js

模板解析的关键对象: compile对象

18.1. 基本流程

1.1 将el的所有子节点取出, 添加到一个新建的文档fragment对象中

1.2 对fragment中的所有层次子节点递归进行编译解析处理

* 对表达式文本节点进行解析
* 对元素节点的指令属性进行解析
* 事件指令解析
* 一般指令解析

18.2 解析表达式文本节点:textNode.textContent = value

  • 根据正则对象得到匹配出的表达式字符串: 子匹配/RegExp.$1
  • 从data中取出表达式对应的属性值
  • 将属性值设置为文本节点的textContent

18.3 事件指令解析: elementNode.addEventListener(事件名, 回调函数.bind(vm))

  • v-on:click="test"
  • 从指令名中取出事件名
  • 根据指令的值(表达式)从methods中得到对应的事件处理函数对象
  • 给当前元素节点绑定指定事件名和回调函数的dom事件监听
  • 指令解析完后, 移除此指令属性

18.4 一般指令解析: elementNode.xxx = value

  • 得到指令名和指令值(表达式)
  • 从data中根据表达式得到对应的值
  • 根据指令名确定需要操作元素节点的什么属性
    • v-text---textContent属性
    • v-html---innerHTML属性
    • v-class--className属性
  • 将得到的表达式的值设置到对应的属性上
  • 移除元素的指令属性

19. 数据绑定

1. 数据绑定 (model==>View):

一旦更新了data中的某个属性数据的值,所有界面上直接或间接使用了此属性的节点都会更新

2. 数据劫持

数据劫持是vue中 用来实现数据绑定 的一种技术
主要实现原理:

通过defineProperty()来监视data中所有属性(任意层次)数据的变化,一旦变化就去更新界面

3. 四个重要对象

3.1 Observer(观察者)

用来对data所有属性数据进行劫持的构造函数

给data中所有属性重新定义属性描述(get/set)

为data中的每个属性创建对应的dep对象

3.2 Dep(Depend)(依赖)

data中的每个属性(所有层次)都对应一个dep对象

创建的时机:

  • 在初始化define data中各个属性时创建对应的dep对象
  • 在data中的某个属性值被设置为新的对象时

对象的结构

{
  id, // 每个dep都有一个唯一的id
  subs //包含n个对应watcher的数组(subscribes的简写)
}

subs属性说明:
当一个watcher被创建时, 内部会将当前watcher对象添加到对应的dep对象的subs中
当此data属性的值发生改变时, 所有subs中的watcher都会收到更新的通知, 从而最终更新对应的界面

3.3 Compile

用来解析模板页面的对象的构造函数(一个实例)

利用compile对象解析模板页面

每解析一个表达式(非事件指令)都会创建一个对应的watcher对象, 并建立watcher与dep的关系

complie与watcher关系: 一对多的关系

3.4 Watcher

模板中每个非事件指令或表达式都对应一个watcher对象

可以监视当前表达式数据的变化

创建的时机: 在初始化编译模板时

对象的组成:

{
  vm,  //vm对象
  exp, //对应指令的表达式
  cb, //当表达式所对应的数据发生改变的回调函数
  value, //表达式当前的值
  depIds //表达式中各级属性所对应的dep对象的集合对象
          //属性名为dep的id, 属性值为dep
}

3.5 总结:dep与watcher的关系: 多对多

一个data中的属性对应对应一个dep,一个dep中可能包含多个watcher

模板中一个非事件表达式对应一个watcher,一个watcher中可能包含多个dep(表达式中包含了几个data属性)

数据绑定的2个核心技术:defineProperty()和消息订阅与发布

4. 双向数据绑定

向数据绑定是建立在单向数据绑定(model==>View)的基础之上的

双向数据绑定的实现流程:

  • 在解析v-model指令时, 给当前元素添加input监听
  • 当input的value发生改变时, 将最新的值赋值给当前表达式所对应的data属性

20. 项目的打包与发布

20.1 打包:会自动打包,并生成dist目录文件

npm run build

20.2 发布方法一:(使用静态服务器工具包)

npm install -g serve
serve dist
访问:http://localhost:8080

20.3 发布方法二:(使用动态web服务器tomcat)

* 修改配置文件 webpack.prod.conf.js
    output{
       publicPath:'XXX'  XXX是打包目录的名称
    } 
* 重新打包 cnpm run build
* 修改dist文件夹为项目名称 XXX
* 将XXX 拷贝到运行的tomcat的webapps目录下面
* 访问:http://localhost:8080/XXX/   
posted @ 2023-10-10 11:04  songxia777  阅读(5)  评论(0编辑  收藏  举报