Vue 实例
在刚接触Vue的时候,就知道 实例 在Vue中是一个重要的概念,在学习之后也整理了一篇有关于Vue实例和生命周期的学习笔记。经过一段时间的学习之后,重新再温习了一遍有关于Vue的实例,整理一下,提供给有需要的同学作为参考资料。
Vue 的基本原理
Vue是一个优势的JavaScript框架,其主要是用来处理 视图层(我的理解就是UI层)。当用户操作 View Model (即JavaScript,在Vue中指的是Vue实例,一个观察者)使其依照一定的逻辑取得需要改变的数据(Model),配合在HTML中Vue提供的模板语法来改变配置,重新渲染后使画面产生相应的变化(View)。用一张图来描述这个过程:
比如我们在浏览器中渲染出来的页面有个区域和“删除”按钮,当用户点击“删除”按钮之后,会移除这个区域。用户交互之后反馈给用户的是,界面(视图)中的区域被移除。看上去非常的简单,其实在Vue中,主要经历了以下几个过程:
- (1) 用户在界面中点击“Remove”按钮进行交互
- (2) 通过模板中的绑定触发Vue实例中注册的
remove()
事件 - (3)
remove()
事件通过Ajax
向服务器请求数据 - (4) 服务器取得的数据后重新传给Vue实例
- (5) Vue实例修改绑定的ViewModel
- (6) ViewModel改变后触模板重新渲染
- (7) 改变后的视图反馈给用户(用户看到重新渲染后的视图)
咱们可以用下图来描述这个交互的整个过程:
事实上Vue的原理要比这复杂的多,只不过用上图来阐述对于初学者而言在概念上有一定的辅助作用。但其也遵循一个原则:视图的状态由数据描述,并且通过数据来驱动视图变化!
深入的概念还无法理解的情况之下,我们暂时只需要先记住,在Vue中视图和数据之间的关系是一个非常简单的关系,即双向数据绑定。
Vue采用发布者 —— 订阅者模式实现双向数据绑定,首先Vue将会获得需要监听的对象的所有属性,通过
Object.defineProperty
方法完成对象属性的劫持,将其转化为getter
和setter
,当属性被访问或修改时,立即将变化能知给订阅者,并由订阅者完成相应的逻辑操作。
我们可以用下图来描述双向数据绑定主要流程:
Observer
:主要处理属性监听逻辑,将监听属性转化为get
和set
属性,当属性被访问时,调用dep.depend()
方法,而属性被修改时,则调用了dep.notify()
方法Dep
:担任发布者的角色,维护访问者列表,负责订阅者的添加和通知工作,上面所提到的depend()
和notify()
方法在这里实现Watcher
:担任订阅者角色,即Dep.target
,可以订阅多个Dep
,在每次收到发布者消息通知时触发update()
方法执行更新逻辑
创建Vue实例
接下来,我们来创建一个Vue实例,就是前面提到的**ViewModel
**部分。Vue实例是Vue中很重要的一部分,创建他很简单:
let app = new Vue({
})
创建Vue实例很简单,通过new Vue({})
即可。事实上,Vue实例就是一个JavaScript对象,而且常常将这个对象赋值给一个app
变量。
Vue的第一个参数是options
,它会传给Vue实例这个对象。options
这个参数主要包括:
el
:Vue实例需要挂载的一个DOM元素,一般是index.html
文件中的div#app
元素data
:需要一个输出取得结果的数据methods
:方法- Vue实例生命周期的钩子函数
比如下面这个示例:
let app = new Vue({
el: '#app',
data () {
return {
message: 'W3cplus.com'
}
},
methods: {
getRemoteMessage () {
Promise.resolve('获取远程数据:大漠')
.then((res) => {
this.message = res
})
}
}
})
除了上面这种方式,Vue实例的挂载还有另外一种方式:
// main.js
new Vue({
render: h => h(App),
}).$mount('#app')
而给Vue实例传递参数,可以像下面这样的方式:
<!-- App.vue -->
<script>
export default {
name: 'app',
data () {
return {
message: 'W3cplus.com'
}
},
methods: {
getRemoteMessage () {
Promise.resolve('获取远程数据:大漠')
.then((res) => {
this.message = res
})
}
}
}
</script>
上面的示例主要做了三件事情:
- 将Vue实例挂载在页面中
id
为app
的一个div
元素上 - 在
data
中初始化了一个message
,并且在<template>
中以{{message}}
方式将data
中的message
显示在页面上 - 定义了一个
getRemoteMessage()
方法,该方法会以非同步的方式取得message
值 - 在
<template>
中的一个button
绑定了定义好的getRemoteMessage()
方法 - 当用户点击界面中的按钮,将会获取远程的一个数据,并且更改
<template>
中message
的值
效果如下:
创建一个Vue实例就是这么的简单。
最开始我们也提到了,Vue实例是Vue中重要的部分之一,甚至可以说,任何一个Vue的应用程序都是从Vue实例开始的,大创建实例后,可以通过操作实例中的参数来改变页面的配置。
以上示例代码可以查阅app-vue-instance项目的
step1
分支。
Vue实例的生命周期
Vue实例中还有另一个重要的内容,那就是Vue实例的生命周期,在Vue实例中会在各个生命周期中提供相应的钩子函数(Hooks事件),这些钩子函数让开发者可以在Vue实例阶段做相应的处理。Vue官方为Vue实例的生命周期提供了一张非常详细的图:
上图展示了Vue实例整个生命周期,其中红框白底就是生命周期中具备的钩子函数:
beforeCreate
:Vue实例初始化的时候会立即调用,其发生在未创立Vue实例之前,这个时候在Vue实例中的设置都还未配置完成,比如data
created
:Vue实例创建完成,此时Vue实例中的配置除了$el
外全部配置,而$el
只有在Vue实例挂载到相应的HTML元素时才会生效beforeMount
:在Vue实例挂载到目标元素之前被调用,这时的$el
会是还未被Vue实例中的定义渲染的初始设定的模板mounted
:Vue实例上的设置已经安装上模板,这里的$el
是已经由实例中的定义渲染成真成的页面beforeUpdate
:当Vue实例中的data
发生变化后或是执行vm.$forceUpdate()
调用,这时的页面还未被重新沉浸而改变页面updated
:在重新渲染页面后被调用,这时的页面已经被重新渲染成改变后的页面beforeDestroy
:Vue实例被销毁前调用,这个时候Vue实例还是拥有完整的功能destroyed
:Vue实例被销毁,这个时候Vue实例中的任何定义都已被解除绑定,此时对Vue实例做的任何操作都会失效
接下来看一个小示例,把分支切换到step2
,来看看Vue实例中操作各个钩子函数,然后打印出data
和$el
,看看在各个钩子函数中它们是如何变化的。
<!-- App.vue -->
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<h2>{{ message }}</h2>
<div>
<button @click="getRemoteMessage">获取远程数据</button>
</div>
</div>
</template>
<script>
export default {
name: 'app',
data () {
return {
message: 'W3cplus.com'
}
},
methods: {
getRemoteMessage () {
Promise.resolve('获取远程数据:大漠')
.then((res) => {
this.message = res
})
}
}
}
</script>
根据上图,将Vue实例生命周期中的钩子函数分成四组:
beforeCreate
和created
:创建Vue实例beforeMount
和mounted
:挂载目标元素beforeUpdate
和updated
:改变后重新渲染beforeDestroy
和destroyed
:销毁Vue实例
beforeCreate
和created
先来看beforeCreate
和created
两个钩子函数,在Vue实例中添加这两个钩子函数,打印出相应的data
和$el
:
beforeCreate () {
console.log(`>>> ==== 钩子函数 beforeCreate ==== >>>`)
console.log(`this.message: ${this.message}`)
console.log(`this.$el: ${this.$el}`)
console.log(`>>> ==== End ==== >>>`)
},
created () {
console.log(`>>> ==== 钩子函数 created ==== >>>`)
console.log(`this.message: ${this.message}`)
console.log(`this.$el: ${this.$el}`)
console.log(`>>> ==== End ==== >>>`)
}
结果如下:
由于beforeCreate
阶段,Vue实例还没有创建,所以message
和$el
都是undefined
;而到了created
阶段时,Vue实例已经创建了,所以message
变成了W3cplus.com
,但$el
因为还没有挂载到目标元素,所以依旧是unddefined
。
也就是说,在beforeCreate
钩子中是不能对Vue实例中的任何东西做操作。
beforeMount
和mounted
和前面的方式一样,在Vue实例中添加钩子函数beforeMount
和mounted
:
beforeMount () {
console.log(`>>> |_==_| 钩子函数 beforeMount |_==_| >>>`)
console.log(`this.message: ${this.message}`)
console.log(`this.$el: ${this.$el}`)
console.log(`>> |_==_| this.$el.outerHTML 开始 |_==_| >>`)
console.log(this.$el.outerHTML)
console.log(`>> |_==_| this.$el.outerHTML 结束 |_==_|`)
console.log(`>>> |_==_| End |_==_| >>>`)
},
mounted () {
console.log(`>>> |_==_| 钩子函数 mounted |_==_| >>>`)
console.log(`this.message: ${this.message}`)
console.log(`this.$el: ${this.$el}`)
console.log(`>> |_==_| this.$el.outerHTML 开始 |_==_| >>`)
console.log(this.$el.outerHTML)
console.log(`>> |_==_| this.$el.outerHTML 结束 |_==_| >>`)
console.log(`>>> |_==_| End |_==_| >>>`)
}
结果如下:
在Vue实例的流程图中提到,开始执行beforeMount
钩子函数时,此时$el
还没有生成HTML到页面上,因此在执行this.$el.outerHTML
时会报警,如上图所示。而在执行mounted
钩子函数时,Vue实例已经绑定到元素上,所以这里看到的是渲染后的结果。
也就是说,在beforeMount
前同样不能操作DOM元素。
beforeUpdate
和updated
同样的,把beforeUpdate
和updated
钩子函数加入到Vue实例中:
beforeUpdate () {
console.log(`>>> (^_^) 钩子函数 beforeUpdate (^_^) >>>`)
console.log(`this.message: ${this.message}`)
console.log(`this.$el: ${this.$el}`)
console.log(this.$el.outerHTML)
console.log(`>>> (^_^) End (^_^) >>>`)
},
updated () {
console.log(`>>> (^_^) 钩子函数 updated (^_^) >>>`)
console.log(`this.message: ${this.message}`)
console.log(`this.$el: ${this.$el}`)
console.log(this.$el.outerHTML)
console.log(`>>> (^_^) End (^_^) >>>`)
}
当你点击页面上的按钮时,打印出来的结果如下:
beforeDestroy
和destroyed
beforeDestroy () {
console.log(`(^_^) 钩子函数 beforeDestroy (^_^)`)
console.log(`this.message: ${this.message}`)
console.log(`this.$el:${this.$el}`)
console.log(this.$el.outerHTML)
console.log(`(^_^) End (^_^)`)
},
destroyed () {
console.log(`(^_^) 钩子函数 destroyed (^_^)`)
console.log(`this.message: ${this.message}`)
console.log(`this.$el:${this.$el}`)
console.log(this.$el.outerHTML)
console.log(`(^_^) End (^_^)`)
}
第一次执行app.__vue__.$destroy()
会打印出相应的信息,再执行一次将会报错,如下图所示:
正如上面所示,执行beforeDestroy
后,即将执行销毁Vue实例,如果想要释放一些资源,可以在这里操作;当执行destroyed
时,Vue实例将会销毁。比如上面的示例,执行app.__vue__.$destroy()
后,再点击页面上的按钮,不会有任何的反应,这是因为我们的Vue实例已销毁。
小结
本文从Vue的基本原理着手,学习了Vue的一些基本原理以及相关的概念,然后学习Vue实例的创建以及Vue实例生命周期中的钩子函数,并通过简单的实例,演示了Vue实例中不同钩子函数时实例的状态。这样对于学习Vue实例以及生命周期就不仅仅是停在概念上的,而是知道每个不同周期中对应钩子函数中具体的行为。这样一来,在实际使用Vue的时候,更能清楚的什么事情该在什么样的钩子函数中执行,才能起到相应的效果。特别是对于DOM的操作,掌握这些尤其重要。Air Jordan II High