目录
1、生命周期
8个常用的钩子:beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed
在new Vue后,首先会进行初始化,初始化生命周期,事件等,然后触发beforeCreate这个钩子,此时的vm无法访问data里面的数据和methods里面的方法;
接下来进行数据代理和数据监测,然后触发created这个钩子,此时vm可以访问到data里面的数据和metheds里面的方法,但是页面上没有;
然后判断vm实例有没有el绑定dom,如果没有el就等待vm.$mount(el)执行以后在继续向下,如果有el就判断实例中有没有template,没有的话就把el的outerhtml作为template,这个过程是解析模版,生成了虚拟DOM,然后存在内存中,
接着触发beforeMount这个钩子,此时的页面还是没有经过Vue编译的页面,对DOM的所有操作都最终(后面的转成真实DOM会给覆盖掉)都不生效,但是对vm 操作会生效;
接下来会生成一个vm.$el并且会用前面的el替换它,这是吧虚拟DOM变成了真实DOM,并且插入在页面上,然后会触发mount这个钩子,此时的页面是经过vue编译的页面,到这里初始化页面的过程结束,后面会开启定时器、发送网络请求,订阅消息,绑定自定义事件(注意当页面销毁时,只会解绑自定义事件,原本的click等事件还是生效的)等操作;
接下来vue会检测数据的变化,当检测到变化以后,会触发beforeUpdate这个钩子,然后数据会变成最新的,但是页面还是之前的(面试:那个生命周期的时候数据和页面不同步?),接下来会根据新的数据生成新的虚拟DOM,然后diff算法进行新旧页面的对比,最后完成页面的更新,这个过程完成了MVVM的从Model到View的更新,完成后会触发updated这个钩子,这时的数据和页面是同步的。
接下来当调用了vm.$destroy()的时候,会触发beforeDestroy这个钩子,接下来我们一般在这个钩子这里关闭定时器,取消订阅消息,解绑自定义事件(注意当页面销毁时,只会解绑自定义事件,原本的click等事件还是生效的)等,当接触完了监视和子组件的事件监听后会触发destroyed这个钩子。
代码实验:
<body> <div id="block" style="border: 1px solid;"> <div>{{n}}</div> <!-- 注意这里的click不属于自定义事件,所以不会被销毁,还可以使用,但是不能操作vm了 --> <button @click="num">点我n++并且查询在不在</button> </div> </body> <script type="text/javascript"> Vue.config.productionTip = false let vm = new Vue({ /*这里有template后,原文档里面el会被整体替换成template里面的东西, 注意观察style还在不在,还有注意的点,template里面的文档需要有 一个包含在一个div中,不能暴露在根下 */ // template:`<div><div>template的{{n}}</div><div>{{n+1}}</div></div>`, el:'#block', data:{ n:1, timer:null, }, methods:{ num(){ console.log('我还在') this.n++; } }, beforeCreate(){ console.log('1',this); //这里我们debugger一下后发现vm里面没有做数据代理,看不见n和那些函数 // debugger }, created(){ console.log('2',this); //这里我们debugger一下后发现,这里的vm里面已经做了数据代理了,但是页面还没有加载出来 // debugger }, beforeMount(){ //这里对DOM操作会最终失效,但是对vm的操作会有效,因为created哪里已经做好了数据代理了 this.n = 100 console.log('3',this); //这里我们debugger一下后发现,这里页面还没有加载出来,因为生成的是虚拟DOM,还没有加载在页面上,是存在内存中 // debugger }, mounted(){ console.log(this,1); this.$nextTick(function () { // 仅在整个视图都被渲染之后才会运行的代码 console.log(this,3) this.timer = setInterval(()=>{ console.log('hello') },100) }) console.log(this,2) }, beforeUpdate(){ console.log('5',this); //这里我们debugger一下后发现,vm里面的内容已经变了,但是页面没变,生成的是虚拟DOM接下来要新旧DOM对比。。。 // debugger }, updated(){ console.log('6',this); this.$nextTick(function () { // 仅在整个视图都被重新渲染之后才会运行的代码 }) }, beforeDestroy(){ //这里对n的操作不会引起页面的变化,在这里我们一般进行关闭定时器,取消订阅消息,解绑自定义事件等后续工作 this.n = 90; console.log('7',this); //这里如果我们不处理定时器的话他会继续进行 clearInterval(this.timer); }, destroyed(){ //这里我们一般不会去操作vm,因为操作也没用 console.log('8',this); } }) </script>
2、组件
2.1、非单文本组件的使用
组件:实现应用中局部代码和资源的集合
三个步骤:1、定义组件(定义的时候不要挂载el,data要写成函数形式)
2、注册组件(全局或者局部,注册时如果和原本名字一样可以简写)
3、使用组件(在DOM里面使用注册的名字)
<body>
<div id="block">
<school2></school2>
<student></student>
<people></people>
</div>
<div id="block2">
<people></people>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const school = Vue.extend({
// el:'block',注意,组件里面不能写el
//注意data要写成又返回值的形式,如果写成对象形式,如果一个组件的这个数据变了,其他地方的这个组件的数据也会变
//只有非单文件组件才会用template
template:`
<div>
<div>{{name}}</div>
<div>{{age}}</div>
</div>
`,//注意这里不能暴露在根下,要用div包裹
data(){
return{
name:"POTKISS",
age:90
}
}
})
const student = Vue.extend({
template:`
<div>
<div>{{name}}</div>
<div>{{age}}</div>
</div>
`,
data(){
return{
name:"potkiss",
age:20
}
}
})
const people = Vue.extend({
template:`
<div>
<div>{{name}}</div>
<div>{{age}}</div>
</div>
`,
data(){
return{
name:"hello",
age:20
}
}
})
Vue.component('people',people);//全局的组件
new Vue({
el:'#block',
data:{
n:1,
},
components:{
//局部的组件
school2:school,
student
}
})
new Vue({
el:'#block2',
})
</script>
2.2、组件使用的注意事项
注册的几个注意事项:
1、我们给组件起名字的时候要避开原有的那些标签名字,多个单词的话建议写成短”-“连接的形式(abc-def),Vue开发者工具里面的名字会把组件名字的首字母大写(AbcDef),如果名字有多个单词,我门也可以把名字写成首字母大写的(AbcDef),但是在引入vue.js的文件里面会报错,但是在vue脚手架里面不会。(总而言之,建议用大驼峰和首字母大写起名)
2、我们在使用组件的时候也可以使用<abc-def/>的形式,但是要注意如果页面引用了多个相同的组件全是自闭的这种形式的话,在引入vue.js的文件这个情况下会只出现一个,但是在脚手架情况下不会。
3、我们在定义组件的时候也可以采用简写的形式,省去extend直接写成对象形式,里面写上配置项,vue如果我们没有extend的话vue会帮我们加上(实际上我们传入的是配置项,在我们extend以后,他会帮我吗创建一个实例对象)。
4、有个name配置项,是修改vue开发者工具里面的组件名称。
<body>
<div id="block">
<school1>
</school1>
<SchoolApple/>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const school1 = Vue.extend({
name:'HEllo',
template:`
<div>
<div>{{name}}</div>
<div>{{age}}</div>
</div>
`,//注意这里不能暴露在根下,要用div包裹
data(){
return{
name:"POTKISS",
age:90
}
}
})
// 简写形式
const school = {
template:`
<div>
<div>{{name}}</div>
<div>{{age}}</div>
</div>
`,//注意这里不能暴露在根下,要用div包裹
data(){
return{
name:"POTKISS",
age:90
}
}
}
new Vue({
el:'#block',
data:{
n:1,
},
components:{
//局部的组件
"schoolapple":school,
school1
}
})
</script>
2.3、组件的嵌套
我们在开发中一般会把所有组件放在一个app组件里面
<body>
<div id="block">
<app></app>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
// 简写形式
const student = {
template:`
<div>
<div>xs{{name}}</div>
<div>xs{{age}}</div>
</div>
`,
data(){
return{
name:"POTKISS",
age:90
}
}
}
const school = Vue.extend({
name:'HEllo',
template:`
<div>
<div>sc{{name}}</div>
<div>sc{{age}}</div>
<student></student>
</div>
`,
data(){
return{
name:"POTKISS",
age:90
}
},
components:{
student
}
})
//我们在项目中一般会用一个app组件管理所有的组件
const app = {
template:`
<div>
<school></school>
</div>`,
components:{
school
}
}
new Vue({
el:'#block',
components:{
//局部的组件
app
}
})
2.4、VenComponent构造函数
组件的本质是构造函数,是由Vue.extend帮我门创建的,所以每次调用之后,返回的都是一个新的Vue.component。
组件的配置中,他里面的this指向的是是VueCompont实例对象,而不是Vue实例对象。
VueCompont实例对象简称vc,Vue实例对象简称为vm。
vm里面有个children里面是组件vc。
2.5、一个关于VueComponent的重要的内置关系
VueCompontent.prototype.__proto__ === Vue.prototype;
这样可以让实例对象vc访问到Vue原型对象的属性和方法;
<body> <div id="block"> <student></student> </div> </body> <script type="text/javascript"> Vue.config.productionTip = false Vue.prototype.x = 90; //给Vue的原型对象上添加一个属性x /* 留个疑问?(下方已解决):为啥给VueComponent的原型对象上添加了属性y,但是她的实例对象student却访问不了y这个属性 答:这个Vue.component不是VueComponent */ Vue.component.prototype.y = 100; const student = { template:` <div> <div>{{name}}</div> <div>{{age}}</div> <button @click="show">show</button> </div> `, data(){ return{ name:"POTKISS", age:90 } }, methods:{ show(){ console.log("这里为true,证实了我们的想法,VueCompontent.prototype.__proto__ === Vue.prototype",this.__proto__.__proto__ === Vue.prototype,this.__proto__.__proto__ === vm.__proto__) console.log("解答疑问:Vue.component != 这里的VueCompotent实例对象",this.__proto__.__proto__ === Vue.component.__proto__) console.log("-------------------------"); console.log("VueCompotent实例对象",this) console.log("VueCompotent实例对象的隐式原型对象",this.__proto__) console.log("VueCompotent实例对象的隐式原型对象指向了Vue的原型对象",this.__proto__.__proto__) console.log("Object原型对象",this.__proto__.__proto__.__proto__) } } } const vm = new Vue({ el:'#block', components:{ student } }) </script>