Vue 实例
每个 Vue 程序都要进行实例化之后使用,实例有两种。
- 一个是 Vue 实例
- 一个是组件实例。
- 对于 Vue 的插件,比如 router 和 resource 也有实例,称其为插件实例。
对于 Vue 的 MVVM 视图的解析
对于 M -V-VM 模型,由 M -V- C 模型衍生。
只不过 M -V- C 中的 C 在 M -V-VM 中变为了 VM。在 M -V-VM 中 VM 就是逻辑操作。通过 VM, 我们去控制视图和数据之间 的变化。
构建一个 Vue 实例逐步解析
// 创建一个 Vue 实例
var vm= new Vue();--> 这个 Vue 实例就是 VM
// 挂载一个 DOM 元素,作为数据展示的容器 --> 这个 el 选项就是 V
var vm =new Vue({el:'#XXX'})
// 设置一个 data 选项,作为一个对象,只保存数据,并且其属性被 Vue 实例代理。--> 这个 data 对象就是 M
var vm =new Vue({
el:'#Name',data:{
key:value,
.....
}
})
构造器
每个 Vue 实例都是通过构造函数 Vue 创建一个 Vue 的 根实例
var vm = new Vue({//options})
在实例化 Vue 时,需要 传入 一个 选项对象 ,它可以包含 数据
(data), 模板
, 挂载元素
(el), 方法
, 生命周期钩子
等选项。
Vue 就是用选项对象中的 data,created… 等选项,来 创建你想要的行为。这些选项可以在 Vue 中的 API 文档查看。
组件构造器
可以 扩展
的 Vue 构造器,从而用 预定义
选项创建 复用
的组件构造器
// 定义一个组件构造器
var myComponent =Vue.exted({// 扩展选项});// 所有的 myComponent 的实例都将以扩展的组件构造器创建
var mycomponentExp=new myComponent(); //mycomponentExp 是一个组件
所以我们现在知道,所有 Vue.js 组件都是被扩展的 Vue 构造器的实例
实例属性(数据代理和响应)
//html 代码
<div id="app">
<p>{{name}}</p>
</div>
//javascript 代码
var vm = new Vue({
el: '#app',
data: {name: 'huang'}
});
通过上面的代码思考,为什么可以在视图中 直接使用name 属性?
每个 Vue 实例都会 代理 其选项对象 中 data 对象的所有属性。
这就关于 Vue 的另一个概念,数据代理
所谓数据代理 (另称数据劫持),指的是 在访问或修改对象的某个属性 时,通过一段代码拦截这个行为,并 进行额外的操作或者返回修改结果 。比较典型的是Object.defineProperty()
和 E2015 的 Proxy
对象。
对于数据劫持,最著名的应用就是双向绑定。
Vue2.x 中使用的就是 Object.defineProperty()
(Vue 3.x 改用Proxy
进行实现)
这里只作大概了解。
详细: 参考末尾链接
所以,Vue 实例通过数据劫持,代理 data 对象的属性。我们可以通过 Vue 实例来访问 data 对象中的属性。
上面的代码完整写法
//html 代码
<div id="app">
<p>{{this.$data.name}}</p> // 通过 vm.$data 访问源数据
<p>{{$data.name}}</p> // 上一种的简写
<p>{{this.name}}</p> // 与上等价,Vue 实例代理 data 属性
<p>{{name}}</p> // 上一种的简写
</div>
//javascript 代码
var vm = new Vue({
el: '#app',
data: {name: 'huang'}
});
// 注 1:this 指向的是挂载在该元素的或是该元素的祖先元素的 Vue 实例。这里指向 vm
// 注 2:data 对象中,以 $ 或_开头的属性不会被 Vue 实例代理,因为它们是 Vue 定义私有属性的写法可能和 Vue 内置的属性、API 方法冲突。可以通过 $data.privateName 来访问这些属性
// 注 3:privateName 就是 $,_ 开头的属性。
注意: 只有这些被 实例代理的属性 是与视图 响应的,代理的属性变化,源数据也会发生变化。同理,源数据变化,代理的属性也会变化
如果是实例被创建后添加新属性给实例,它是不会触发视图更新的。
//html 代码
<div id="app">
<p>{{name}}</p>
</div>
//javascript 代码
var mydata = {
a: 1,
name: 'huang'
}
var vm = new Vue({
el: '#app',
data: mydata
});
// 视图响应数据,发生变化。代理属性改变,mydata 对象 name 属性也改变
vm.name = 'new';
// 创建 Vue 实例后给实例添加属性,视图对其无响应
vm.age = 22;
指令
什么是指令?
指令 (Directives) 是带有 v - 前缀的特殊 属性。指令属性的值预期是Javascript 表达式(这点很重要 !)v-for 例外。
借助尤雨溪的话: 基于指令的数据绑定使得 具体的 DOM 操作都被合理地封装在指令定义中
简单来说,指令的指责就是当其 表达式 的值 改变 时相应地将某些行为应用到 DOM 上。
指令怎么使用?
只需要将指令 作为 html 元素的属性 来使用就可以了,
例如:
<div v-if="expression">
指令只对当前所在的这个元素起作用,它在哪个元素上,就对哪个元素做出对应的 DOM 操作。
指令的值的解释
我更愿意将指令的值叫做指令的 ” 表达式 ”,为什么这么说?
例如:
<div v-if="expression">
expression 在模板中所对应的是 this.expression
眼熟吗?这就是 Vue 实例代理 data 的属性。
既然是表达式,那么就 一定会有返回值
它的值被传给指令,指令根据表达式返回的结果来决定所在的元素进行怎样的操作。
而表达式的 变量
,都来自 Vue 实例代理的 data 选项的属性。
条件判断指令(条件渲染)
v-if:依照起 Value
是否为 Truthy 来决定是否渲染此元素
<div v-if="expression">
{{...}}
</div>
如果 this.expression 值为真,元素则被插入到 Dom 中,this.expression 为假,则当前元素会从 Dom 中移出。
当我们有多个元素需要用同一个判断怎么办?
<div v-if="expression">
.....
</div>
<p v-if="expression">
.....
</p>
这种写法,有些繁琐。我们可以用 <template> 标签,根据元素的 株连性
把这个标签作为父元素,操作这个元素,并且此元素在渲染后不会影响到页面结构。
<template v-if="expression">
<div>
.....
</div>
<p>
.....
</p>
</template>
v-show:依照起 Value
是否为 Truthy 来决定改变其 Css 的 display 属性值。
和 v-if
在页面上的效果相同,都是决定元素是否显示,但是两者的渲染方式不同。
v-if
在初始渲染时条件若为假,则什么也不做,直到第一次条件为真,才会渲染元素并将其插入 DOM 中
v-show
不管初始条件是什么,都会对元素进行一次初始渲染,在接下来的操作中,就是 Css 的切换。
那么,我们知道,移除元素会导致 DOM 树变化,产生一次 回流(reflow)
, 它会重新计算样式和渲染 DOM 树。
而改变 Css 属性这类值改变某个节点的一些样式, 只会引发 重绘(repaint)
, 只会绘制这个元素。
这么一对比,v-if 频繁切换对浏览器性能开销影响更大,所以建议在运行时,如果条件的 Truthy
频繁的切换,则我们应该优先考虑 v -show。而运行时条件很少改变,我更觉得是 条件锁死
,v-if 的 Truthy 为 false 时,则更推荐 v -if,因为它没有初始渲染开销。
v-else:v-if 的 else 语句块,当 v -if 的 expression 的值为 Truthy,则执行 v -else 指令,将元素渲染在页面上,否则,不渲染。
使用注意:必须紧跟在 v -if 后,否则不会有任何作用
循环调用指令(列表渲染)
v-for:指令基于源数据重复渲染该元素或模板块。
数组迭代: 用 v-for
指令根据一组数组的选项列表进行渲染
支持第二个参数为当前项的索引
语法:
v-for="(every, index) in expression"
every
:可以用任意名字,表示迭代的数组元素的别名,可以用这个别名访问数组元素
index
:任意名称, 表示迭代的数组索引的别名,可以用这个别名访问数组索引
expression: 要迭代的源数据数组
实例:
<div v-for="i in arr">
{{i}}
</div>
var vm = new Vue({
el: '#app',
data: {
arr: [{name: 'li'}, {name: 'huanghao'}, {name: 'wang'}]
}
})
对象迭代:v-for
通过一个对象的属性来迭代
还可以有两个额外参数,第二个参数为键名,第三个参数为索引(这个索引更像是 for 循环的 i)
语法:
v-for="(value, key, index) in object"
value:任意名称, 表示迭代的对象属性值的别名,可以用这个别名访问对象属性值
key: 任意名称, 表示迭代的对象的键值的别名,可以用这个别名访问对象键值
index: 任意名称, 表示迭代的对象的索引的别名 ..
object: 要迭代的源数据对象。
实例
<div id="app">
<div v-for="(value,key1,index1) in obj">
{{value}} --{{key1}} ---{{index1}}
</div>
</div>
var vm = new Vue({
el: '#app',
data: {
obj: {
name: 'huang',
age: 22,
sex: 'man'
}
}
})
在 v-for
块中,拥有对父作用域属性的完全访问权限,v-for 块中, 其 内部
的模板语法,可以将 v -for 块中别名作为变量使用
那么模板语法有哪些?
绑定型指令
绑定型指令会有参数, 参数是绑定的属性或事件。
v-bind:
绑定一个属性
语法: v-bind:prop="expression
“
prop: 绑定的属性
expression: 表达式
v-bind 绑定一个属性,如果 expression 的值为真,则 表达式的值
被添加到绑定的属性上,否则不被添加
以一个 <img> 元素的 src 属性举例
<img alt="":src="src"> // <img alt="" :src="1.img">
new Vue({
el: '#app',
data: {Src:'1.img'}
});
v-bind 绑定了一个 src 属性,并且属性值为 this.Src,当表达式的值为真时,src 属性的属性值就是 this.Src
v-bind 绑定 class 和 style 有些特殊,可以使用对象和数组形式绑定
绑定 html 元素的 class
对象语法:
语法: v-bind:class="{className:expression,className2:expression2...}"
计算 expression 是否为 Truthy,如果为真,则 className/className2 将被添加到 class 属性中,否则不会添加.
也可以直接绑定 data 选项中的一个对象
<div id="app" :class="classObject"></div>
var app = new Vue({
el: '#app',
data:{
classObject: {
active: true,
'text-danger': false
}
}
})
或者绑定一个 返回对象
的计算属性
<div id="app" :class="classObject"></div>
var app = new Vue({
el: '#app',
data: {
isActive: true,
error: null
},
computed: {classObject: function () {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal',
}
}
}
})
数组语法:和对象语法有不同,它添加的类名是被实例代理的 data 对象中的属性的属性值,并且此属性值必须的字符串。
例子
<div
v-bind:class="[app,cls]" //<div class="sss"></div>
>
new Vue({
el: '#app',
data: {
cls:"sss",
app:true
}
});
为了更加方便,还可以在数组中使用对象语法,对象语法在模板中照常解析
例子
<div
v-bind:class="[app,cls,expression]" //<div class="sss active error click"></div>
>
new Vue({
el: '#app',
data: {
name: '黄昊',
src:'1.img',
expression:{
active:"class1",
error:'class2',
click:789
},
cls:"sss",
app:true
}
});
关于绑定 style
对象语法: v-bind:style="{style1:expression,style2:expression2}"
style1/style2: 为 style 属性添加的样式名
expression/expression2: 表达式的值必须为字符串,数字例外。表达式的值符合样式属性。
举例
<div
:style="{color:'red'}" //<div style="color: red;"></div>
>
</div>
// 上方等同下方
<div
:style="{color:red}"
>
</div>
new Vue({
el: '#app',
data: {red:'red',}
});
或者为了让模板更加清晰,我们直接给定一个对象表达式
举例
<div
:style="styleObject" ///<div style="color: red; font-size: 16px;"></div>
>
</div>
new Vue({
el: '#app',
data: {
styleObj:{
color:'redd
fontSize:'16px'// 样式按照 Js 的写法,因为我们一切模板语法的操作都是基于挂载的 Dom 对象
}
}
});
数组语法 : 将多个 样式对象 应用到同一个元素上
需要注意的是,数组语法中的元素必须是对象形式。
例子:
<div
:style="[styleObj,styleObj2]"
>
</div>
new Vue({
el: '#app',
data: {
styleObj:{
color:'red',
fontSize:'16px'
},
styleObj2:{
width:'100px',
height:'100px'
}
}
});
在 Vue2.3.0+,style 绑定的属性提供一个包含多个值 的数组,常用于提供多个带前缀的值
例子
<div :style="{display: ['-webkit-box','-ms-flexbox','flex','inline','a'] }"></div>
//<div style="display: inline;"></div>
这样写只会渲染数组中 最后一个被浏览器支持 的值
计算属性
我们通过 computed
选项来添加计算属性
对于任何复杂的逻辑,都应该使用计算属性
当我们在 computed 声明一个计算属性时,我们提供的函数将用作 vm.computed_prop 的 getter 函数 当计算属性被调用时,触发这个函数执行。
怎么使用?vm.computed_prop 简化 —>computed_prop
组件
组件化思维
组件化针对的是页面中的整个完整的功能模块划分(项目的分工)
组件的概念(一个文件)
组件是一个 html、css、js、image 等外链资源,这些部分组成的一个聚合体
优点:代码复用,便于维护
划分组件的原则:复用率高的,独立性强的
组件基础:
vue.js 文件中暴露出一个 Vue 的构造器函数,这个函数的作用,是用来实例化出来一个实例,这个实例也是一个组件,我们称之为 ‘ 根实例 ’
Vue 为了扩展功能,给了一个方法,这个方法叫 extend Vue.extend()
组件的表现形式是标签
使用组件必须遵守 H5 的标准,组件需要合法,必须注册组件。
组件使用前必须注册
组件的注册
全局注册
局部注册
在使用组件时,要注意会不会与原有的标签冲突