此学习教程是对官方教程的解析,
本章节主要涉及到的官方教程地址:
本章节介绍实例创建阶段的生命周期钩子:beforeCreate和created
beforeCreate和created的区别
生命周期钩子 | 可获取的对象 | 常见应用场景 |
---|---|---|
beforeCreate | this.$root、this.$parent、this.$options | 需要在虚拟DOM渲染前设置的options选项 |
created | this.$root、this.$parent、this.$options data、computed、watch、methods中的数据和方法 | 添加关联其它方法的方法 用异步获取数据初始化响应式属性 |
beforeCreate应用
组件循环引用的例子
官方教程组件之间的循环引用里,有一个组件循环引用的例子,构建一个文件目录树,有两个组件,每个组件内部引用另一个组件。我使用局部注册的方式实现的代码如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app1">
<tree-folder :folder="folder"></tree-folder>
</div>
<script>
var treeFolderOptions = {
props: ['folder'],
template: '<p>\
<span>{{ folder.name }}</span>\
<tree-folder-contents :children="folder.children"/>\
</p>'
,
components:{
'tree-folder-contents':treeFolderContentsOptions
}
}
var treeFolderContentsOptions = {
props: ['children'],
template: '<ul>\
<li v-for="child in children">\
<tree-folder v-if="child.children" :folder="child"/>\
<span v-else>{{ child.name }}</span>\
</li>\
</ul>'
,
components:{
'tree-folder':treeFolderOptions
}
}
//局部注册
var vm = new Vue({
el: "#app1",
data:{
folder : {
name : "A",
children: [
{
name : "AA",
children: [
{name : "AAA"}
]
}
]
}
},
components:{
'tree-folder':treeFolderOptions
}
})
</script>
</body>
</html>
运行出错及原因
这个例子运行会出错:
出错的原因就是在渲染tree-folder组件时,遇到了<tree-folder-contents>
,但却说是“不认识的自定义元素”,询问你“是否正确注册了这个组件”。
怎么会这样呢,不是明明在代码中局部注册了tree-folder-contents组件了吗?
components:{
'tree-folder-contents':treeFolderContentsOptions
}
很可惜,这时候treeFolderContentsOptions的值是undefined,因为treeFolderContentsOptions写在treeFolderOptions后面,treeFolderOptions无法识别
treeFolderContentsOptions。
那把treeFolderContentsOptions写在treeFolderOptions前面不就得了?不行, treeFolderContentsOptions又依赖于treeFolderOptions,这样写又会识别不了<tree-folder>
了。
这可真是个悖论!你中有我,我中有你。怎么办呢?
解决方案
<tree-folder-contents>
组件是在<tree-folder>
组件渲染时才发现没有注册的, 那只要在渲染前注册组件就行了!在哪里注册呢?
从vue官方教程实战(七)—— 生命周期概述可以知道,组件渲染是在mounted之前发生的,所以在mounted之前的beforeCreate、created、beforeMount钩子里都可以!而且注册组件的components选项是在vm.$options里,在上面三个钩子里也都可以访问到。
因为只是使用到vm.$options,不涉及数据、方法和render函数,所以放在beforeCreate更合适、代码组织更合理。
更改上面的例子:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app1">
<tree-folder :folder="folder"></tree-folder>
</div>
<script>
var treeFolderOptions = {
props: ['folder'],
template: '<p>\
<span>{{ folder.name }}</span>\
<tree-folder-contents :children="folder.children"/>\
</p>'
,
beforeCreate: function () {
this.$options.components['tree-folder-contents'] = treeFolderContentsOptions
}
}
var treeFolderContentsOptions = {
props: ['children'],
template: '<ul>\
<li v-for="child in children">\
<tree-folder v-if="child.children" :folder="child"/>\
<span v-else>{{ child.name }}</span>\
</li>\
</ul>'
,
components:{
'tree-folder':treeFolderOptions
}
}
//局部注册
var vm = new Vue({
el: "#app1",
data:{
folder : {
name : "A",
children: [
{
name : "AA",
children: [
{name : "AAA"}
]
}
]
}
},
components:{
'tree-folder':treeFolderOptions
}
})
</script>
</body>
</html>
浏览器运行以上代码,成功了!
created应用
在官方教程侦听器中,有一个使用created的例子:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
</head>
<body>
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<script>
var watchExampleVM = new Vue({
el: '#watch-example',
data: {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
// 如果 `question` 发生改变,这个函数就会运行
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},
created: function () {
// `_.debounce` 是一个通过 Lodash 限制操作频率的函数。
// 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
// AJAX 请求直到用户输入完毕才会发出。想要了解更多关于
// `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,
// 请参考:https://lodash.com/docs#debounce
this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
},
methods: {
getAnswer: function () {
if (this.question.indexOf('?') === -1) {
this.answer = 'Questions usually contain a question mark. ;-)'
return
}
this.answer = 'Thinking...'
var vm = this
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = 'Error! Could not reach the API. ' + error
})
}
}
})
</script>
</body>
</html>
这个例子是把debouncedGetAnswer方法的定义放在了created钩子中。因为debouncedGetAnswer方法是和getAnswer方法是紧密相联的,在created钩子里可以访问到methods中的方法,所以放在这里定义是最好不过了。
那为什么不能放在methods定义呢?那是因为methods只是一个对象,把debouncedGetAnswer放在methods中定义会访问不到getAnswer方法。而且这时候methods中的方法还没有注入到实例中,所以使用this.getAnswer的方式调用也是不行的。
methods: {
getAnswer: function () {
...
},
//报错,无法访问getAnswer方法
//debouncedGetAnswer : _.debounce(getAnswer,500)
//报错,无法访问this.getAnswer方法
//debouncedGetAnswer : _.debounce(this.getAnswer,500)
}
本章节教程结束。
全部教程地址:Vue入门实战教程 | 寒于水学习网
vue生命周期实战系列: