MVVM、MVC、MVP都是架构模式,这里是这三者的图示。vue中MVVM架构如下:
Vue实例由Vue
函数创建。
var vm = new Vue({
//
})
Vue组件也是Vue实例,组件可以扩展html元素,封装可重用代码。
// 注册
Vue.component('my-component', {
template: 'A custom component!'
})
// 创建根实例
new Vue({
el: '#example'
})
渲染为
<div id="example">
<div>A custom component!div>
div>
参考笔记
// html
<div id="m-dialog">
<child value="str">child>
div>
// js
// 注册子组件
Vue.component("child", {
// 获取value的依赖
props:["value"],
template: '{{ value }}'
});
// 父实例
new Vue({
// 这里的el可视为父组件
el:"#m-dialog"
});
使用v-bind
绑定子组件的data,如果父组件的flag
发生改变,会传递给子组件。
// html
<div id="m-dialog">
<child v-bind:state = 'flag'>child>
div>
// js
Vue.component("child", {
props:["state"],
template: '{{ state }}'
});
new Vue({
el:"#m-dialog",
data: {
flag: true
}
});
渲染结果
true
// html
<div id="m-dialog">
<child v-on:cancle="other">child>
div>
// js
Vue.component("child", {
template: '',
methods: {
cancle: function() {
// 与父组件通信一定要加上这句话
this.$emit('cancle');
}
}
});
var vue1 = new Vue({
// 这里的el可视为父组件
// 所以下面这个片段是放在#m-dialog里面的
el:"#m-dialog",
data: {
flag: true
},
methods: {
other: function() {
alert();
}
}
});
使用 ref
为子组件 / 元素指定一个引用 ID:ref = 'ID'
。通过vm.$refs.ID
获取该组件/元素。
<div id="app">
<child ref = 'mycom'>child> // 子组件
div>
则在父组件中,通过vm.$refs.mycom
获取整个子组件对象。(vm是父组件)
{{content}}
更新文本(部分文本)的textContent
插入的content会被解析为字符串
更新div的innerHtml,内容按普通 HTML 插入 - 不会作为 Vue 模板进行编译。// html
<div v-cloak> {{ content }} div>
// css
[v-cloak] {
display: none;
}
则div中content会在vue实例准备完毕之后才显示。次方法可以避免网速较慢时候,{{ content }}
引起的闪烁。
<div class = "static" v-bind: class = "{ active: isActive, 'text-danger': hasError }">div>
若data如下
data() {
return {
isActive: true,
hasError: false
}
}
结果渲染为
绑定的数据对象不必内联定义在模板中
// 渲染结果同上
<div v-bind:class="classObject"></div>
data: {
classObject: {
active: true,
'text-danger': false
}
}
也可以返回绑定对象的计算属性
class="classObject">div>
data: {
isActive: true,
error: null
},
computed: {
classObject: function () {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal'
}
}
}
- 绑定style
- 绑定元素的属性
例如,绑定一个input标签的name属性
type = 'text' v-bind = "{ value: 'hello ' + name }" />
// data
data() {
name: 'hello world!'
}
v-model
上面input的value值如果发生改变,是不会“同步”到data中的name值,要实现“双向绑定”,需要使用v-model
指令。一般用在表单输入绑定,input、textarea等。
v-for
vue2.0之后,要访问遍历的数组中对象的下标,要把index写在括号中第二位。
<ul>
<li v-for = "(item, index) in items">{{ index }} - {{ item }}li>
ul>
// data
items: ['item1', 'item2', 'item3']
<ul>
<li v-for = "(item, key) in user">
{{ key }}-{{ item }}
li>
ul>
// data
user: {
name: 'userName',
age: 20
}
列表渲染中的key属性
key 的特殊属性主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试修复/再利用相同类型元素的算法。使用 key,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。
假如v-for
出来的元素中有相同的,console会华丽丽打出warning
[Vue warn]: Duplicate keys detected: '重复渲染项'. This may cause an update error.
它也可以用于强制替换元素/组件而不是重复使用它。当你遇到如下场景时它可能会很有用:
- 完整地触发组件的生命周期钩子
- 触发过渡
<transition name = 'fade'>
<span>{{ text }}span>
transition>
如果span
没有绑定key
属性,则在text发生改变的时候,transition
组件会被重复使用,此时text文本被简单替换,不会触发元素过渡效果。
为span元素绑定key属性{{ text }}
,则text发生变化的时候,transition
组件会被替换/重新渲染。
v-if vs v-show
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS display: none;
进行切换。
一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
v-on
用在普通元素上时,只能监听原生 DOM 事件。用在自定义元素组件上时,也可以监听子组件触发的自定义事件。简写@
在监听键盘事件时,我们经常需要检查常见的键值。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符,以下几种写法等效。
<input v-on:keyup.13="submit">
<input v-on:keyup.enter="submit">
<input @keyup.enter="submit">
可以通过全局 config.keyCodes
对象自定义按键修饰符别名:
// 可以使用 v-on:keyup.f1
Vue.config.keyCodes.f1 = 112
watch与计算属性computed
computed
模板内的表达式{{ expression }}
的逻辑若太复杂,不好看也不易维护。
<div id="example">
{{ message.split('').reverse().join('') }}
div>
此时使用计算属性。
<div id="example">
<p>Original message: "{{ message }}"p>
<p>Computed reversed message: "{{ reversedMessage }}"p>
div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
结果:
Original message: “Hello”
Computed reversed message: “olleH”
也可以在methods
中定义一个reversedMessage
方法。效果一样。但是两者也有区别:
[Note] computed
只有在相关依赖发生变化的时候,才会重新求值。例如reversedMessage
属性依赖message
属性,如果message
不改变,reversedMessage
不会更新,此时多次访问reversedMessage
会立即返回缓存的值。
看这个例子:
// html
<p>date: '{{ date }}'p>
<p>date + 1: '{{ dateAdd }}'p>
<p>computed: past: '{{ past }}'p>
<p>function called: now: '{{ now() }}'p>
// javascript
var vm = new Vue({
el:"#app",
data: function(){
return {
message: 'Hello',
date: 1518673470535
}
},
computed: {
// 计算属性dateAdd的依赖是date
dateAdd: function() {
console.log('computed: date + 1')
return this.date + 1
},
// 计算属性past没有依赖date
past: function() {
console.log('computed: past')
return Date.now()
}
},
methods: {
now: function() {
console.log('function called: now')
return Date.now()
}
}
});
vm
实例第一次生成后,计算属性dateAdd
和past
第一次更新。now()
函数调用,所以在控制台可以看到三个消息:
改变date的值
watch
监听数据变化,特别是在有些数据会随着其他数据变化时变化,当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
var vm = new Vue({
el:"#app",
data: function(){
return {
firstName: 'html',
lastName: 'css',
fullName: 'html-css'
}
},
watch: {
// firstName 发生改变的时候,这个函数会立刻被调用
/**
params:
newval:firstName新值
oldval:firstName旧值
**/
firstName: function(newval, oldval) {
// 更新fullName
this.fullName = this.firstName + '-' + this.lastName
}
}
});
watch
属性中firstName
函数,作用就是监听firstName
的值,参数newval
、oldval
都是可选的。
监控路由对象:
'$route': function(newRoute, oldRoute){
// 获取新的路由对象和旧的路由对象
// 对一些特定页面或者特殊操作(如返回),执行某些操作
}
过滤
- 列表渲染
v-for
渲染结果的过滤:在computed属性中使用js内置filter
// html
// ...
for = 'item in filterItems'>
{{ item.id }}
{{ item.text }}
{{ item.ctime }}
// js
computed: {
filterItems: function() {
var self = this
return self.items.filter(function(item){
return item.text.indexOf(self.value) !== -1
})
}
}
// data
value: ''
- 本地过滤器
在vue组件filter
属性中定义私有过滤器。
{{ message | capitalize }}
<div v-bind:id="rawId | formatId">div>
filters: {
capitalize: function(value){
// ...
}
}
value是默认参数,取自过滤器capitalize左边管道符(|)的左边的参数,这里是message。有这几机制,则可以很好理解多级过滤。
{{ message | filterA | filterB }}
过滤器是js的函数,有一个取自管道符(|)左边的默认参数,也可以向过滤器传递参数。
{{ message | filterA('arg1', arg2) }}
以上filterA接受三个参数,第一个参数是默认参数message
,第二个参数是字符串常量'arg1
,第三个参数是表达式arg
。
在创建Vue实例之前,定义全局过滤器。
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
new Vue({
// ...
})
自定义指令
全局注册
Vue.directive('不带 v- 的指令名称', {
钩子函数: function(para){}
}
局部注册
directives: {
'不带v-的指令名称': {
钩子函数: function(para){}
}
}
钩子函数
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
钩子函数的参数el
、binding
、vnode
、oldVnode
。见官网-钩子函数的参数