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个最主要的模块
2.2 特点
2.2.1 数据驱动视图
在使用了 vue 的页面中,vue 会监听数据的变化,那么所有绑定该数据的 DOM 都会自动跟着改变重新渲染页面
当页面数据发生变化时,页面会自动重现渲染
数据驱动视图是单向数据绑定
2.2.2 双向数据绑定
在填写表单时,双向数据绑定可以辅助开发者在不操作 DOM 的前提下,自动把用户填写的内容同步到数据源 中;同时,当数据源发生变化时,也会将数据自动渲染到页面中
2.3 MVVM原理
ViewModel 作为 MVVM 的核心,是它把当前页面的数据源(Model)和页面的结构(View)连接在了一起
当数据源发生变化时,会被 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解决表达式闪现问题
使用表达式 {{}} 的方式,容易出现闪现的问题
-
方法一:使用
v-text
,但是这种方式比较麻烦 -
方法二:使用
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-if
和v-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的生命周期可以大致分为:初始化显示、更新显示、销毁死亡
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: 指令绑定的前一个值,仅在
update
和componentUpdated
钩子中可用。无论值是否改变都可用。 - 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: 上一个虚拟节点,仅在 update
和 componentUpdated
钩子中可用
<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/