vue生命周期函数介绍
- 引入场景: 呈现文字透明渐隐的效果
......
<body>
<div id="container">
<!--值必须写成对象式,在js中,若键值对名称一样,则可以简写-->
<h3 :style="{opacity}">欢迎!</h3>
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
opacity:1, // 设置不透明度
},
});
</script>
</body>
- 解决方式一:在vm外部使用计时器
......
<body>
<div id="container">
<h3 :style="{opacity}">欢迎!</h3>
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
opacity:1,
},
});
setInterval(()=>{ // 使用计时器成功解决这个问题
vm.opacity -= 0.01
if(vm.opacity <= 0) vm.opacity = 1 // js简写
},16)
</script>
</body>
- 如果一定要把逻辑写vue里面,该怎么办
以下代码示例是一个死循环,后果就是耗尽CPU
......
<body>
<div id="container" v-cloak>
<h3 :style="{opacity}">欢迎!</h3>
{{change()}} <!--调用这个方法-->
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
opacity:1,
},
methods:{
change(){
setInterval(()=>{ // 把之前定时器的逻辑放里面
console.log('123')
this.opacity -= 0.01
if(this.opacity <= 0) vm.opacity = 1
},16)
}
}
});
</script>
</body>
......
- 效果:页面无限生成计时器,直至崩溃...
- 由于定时器定时修改data的值,而vue一旦检测到data数据的变动,就重新解析模板
- 而数据是一直发生变化的,所以vue就一直解析模板,这个动作无限循环下去...
- 使用vue生命周期函数"mount()"来解决这个问题
当页面加载完毕以后(vue挂载完毕),会自动调用 mount
......
<body>
<div id="container" v-cloak>
<h3 :style="{opacity}">欢迎!</h3>
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
opacity:1,
},
mounted(){ // 页面加载完毕以后,会被调用一次
setInterval(()=>{
this.opacity -= 0.01
if(this.opacity <= 0) vm.opacity = 1
},16)
}
});
</script>
</body>
......
- 由于mount只被调用一次,所以不会无限生成计时器
- 小结
- 生命周期
- 生命周期回调函数,生命周期函数,生命周期钩子
- 作用: vue在关键时刻帮我们调用的一些特殊名称的函数
- 钩子函数的名称不可更改,具体逻辑要自己实现
- 钩子函数的this,指向vue实例/组件对象
Vue生命周期原理解析
- 引入场景
......
<body>
<div id="container" v-cloak>
<h2>当前n的值是:{{n}}</h2>
<button type="button" @click="add">计算</button>
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
n:1
},
methods:{
add(){
this.n++;
}
},
beforeCreate(){
console.log('beforeCreate')
console.log(this)
debugger // 断点打在这边,观察上面的this(vm实例)
}
});
</script>
</body>
......
- 此时,vm是没有 _data属性的
Vue 生命周期小结
- vm实例的生命周期(部分周期未展示)
- 将要创建: beforeCreate()
- 创建完毕: created()
- 将要挂载: beforeMount()
- 挂载完毕: mount() # 最常用,比如页面加载完成以后,立即发送ajax
- 将要更新: beforeUpdate()
- 更新完毕: update()
- 将要销毁: beforeDestroy() # 常用,比如删除计时器,取消订阅消息...
- 销毁完毕: destroy()
- 之前定时器的示例,若要停止计时器,可以这干
......
<body>
<div id="container" v-cloak>
<h3 :style="{opacity}">欢迎!</h3>
<!--新增按钮-->
<button type="button" @click="stop">停止计时器</button>
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
opacity:1
},
methods:{
stop(){ // 点击按钮,停止计时器
clearInterval(this.timer)
}
},
mounted(){ // 页面加载完成以后,就启动计时器
this.timer = setInterval(()=>{ // 临时给vm实例增加一个timer属性,接收计时器的返回值,以便停止它
console.log('计时器开始')
this.opacity -= 0.01
if(this.opacity <= 0) vm.opacity = 1
},16)
}
});
</script>
</body>
- 可以使用vm生命周期函数 destroy()替换上面 stop() 的逻辑
......
methods:{
stop(){
// clearInterval(this.timer)
this.$destroy() // 不仅停止了计时器,整个vm实例也被删除
}
},
......
- 上述写法其实是有缺陷的,为了停止计时器,把整个vm实例删除,有点极端
- 现在,使用 beforeDestroy 停止计时器是最好的选择
......
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
opacity:1
},
methods:{
stop(){
this.$destroy() // 摧毁之前,会调用 beforeDestroy
}
},
mounted(){
this.timer = setInterval(()=>{
console.log('计时器开始')
this.opacity -= 0.01
if(this.opacity <= 0) vm.opacity = 1
},16)
},
beforeDestroy(){
clearInterval(this.timer) // 被调用
}
});
</script>
- vue生命周期最终小结
- 常用的生命周期钩子
- mounted: 发送 ajax请求,启动定时器,绑定自定义事件,订阅消息等(初始化操作)
- beforeDestroy: 清楚定时器,解绑自定义事件,取消订阅消息等(收尾工作)
- 关于摧毁vue实例
- 销毁后借助vue开发者工具看不到任何信息
- 销毁后自定义事件会失效,但原生DOM事件依然有效
- 一般不会在 beforeDestroy 操作数据,因为即便操作了,也不会触发更新流程
VUE created与mounted区别
-
created mounted
-
created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图
-
mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作
-
实质就是模板渲染前/模板渲染后的区别
-
模块
- 定义: 一个个的js文件
组件
-
定义: 实现局部(特定)功能效果的代码集合(html(片段)/css/js/image/mp4......)
-
好处: 别人拿过去直接用,不必再一个个修改css/js......
-
特点: 组件里面可以再包含组件
- html 文档
- header 组件
- middle 组件
- left 组件
- right 组件
- footer 组件
- 单文件组件 和 非单文件组件
- 单文件组件(一个文件里面只有一个组件,实战用得最多): filename.vue
- 非单文件组件(一个文件里面有多个组件)
组件demo演示
骨架如下,没有问题
......
<body>
<div id="container">
<h2>学校名称: {{schoolName}}</h2>
<h2>学校地址: {{address}}</h2>
<hr >
<h2>学生姓名: {{studentName}}</h2>
<h2>学生年龄: {{age}}</h2>
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
schoolName:'第一学校',
address:'海淀区',
studentName:'JimGreen',
age:18
},
});
</script>
</body>
- 局部组件demo演示
......
<body>
<div id="container">
<xuexiao></xuexiao> <!--应用自定义组件标签-->
<hr>
<xuesheng></xuesheng> <!--应用自定义组件标签-->
</div>
<script type="text/javascript">
var school = Vue.extend({ // 注册组件
template:`
<div> // div根元素一定要有,否则报错
<h2>学校名称:{{schoolName}}</h2>
<h2>地址: {{address}}</h2>
</div>
`,
data(){ // 一定要写成函数式
return {
schoolName:'第一学校',
address:'海淀区',
}
}
})
var student = Vue.extend({ // 同理
template:`
<div>
<h2>学生名称:{{studentName}}</h2>
<h2>年龄: {{age}}</h2>
</div>
`,
data(){
return {
studentName:'JimGreen',
age:18
}
}
})
var vm = new Vue({
el:'#container',
components:{ // 注册局部组件标签
xuexiao:school,
xuesheng:student
}
});
</script>
</body>
- 加入点击事件演示
......
<body>
<div id="container">
<xuexiao></xuexiao>
<hr>
<xuesheng></xuesheng>
</div>
<script type="text/javascript">
var school = Vue.extend({
template:`
<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>地址: {{address}}</h2>
<!--加入点击事件-->
<button @click="showSchoolName">提示信息</button>
</div>
`,
......
methods:{
showSchoolName(){
alert(this.schoolName)
}
}
})
var student = Vue.extend({
......
})
var vm = new Vue({
el:'#container',
components:{
xuexiao:school,
xuesheng:student
}
});
</script>
</body>
- 全局组件demo演示
......
<body>
<div id="container">
<xuexiao></xuexiao>
<hr>
<xuesheng></xuesheng>
<hello></hello> <!--应用-->
</div>
<script type="text/javascript">
var hello = Vue.extend({ // 定义全局组件(和局部一样)
template:`
<div>
<h2>你好啊~{{name}}</h2>
</div>
`,
data(){
return {
name:'Kate Green'
}
}
})
Vue.component('hello',hello) // 不一样的地方
var school = Vue.extend({
......
})
var student = Vue.extend({
......
})
var vm = new Vue({
el:'#container',
components:{ // 这里并没有注册 hello组件
xuexiao:school,
xuesheng:student
}
});
</script>
</body>
- 小结
- Vue中使用组件的三大步骤
- 定义组件(创建组件)
- 注册组件
- 使用组件(写组件标签)
- 如何定义一个组件
- 使用 Vue.extend(options)创建
- 如何注册组件
- 局部注册: 靠 new Vue的时候传入 components选项
- 全局注册: 靠 Vue.component('组件名',组件)
- 编写组件标签
- <school></school>
组件的命名
-
一个单词组成
-
首字母小写: school
-
首字母大写: School
-
-
多个单词组成
-
kebab-case命名: my-school
-
camelCase命名: MySchool(需要vue脚手架支持)
-
-
备注
-
组件命名尽可能回避HTML中已有的元素名称,例如: h2/H2 都不行
-
可以使用 name 配置项指定组件在开发者工具中呈现的名字(仅仅定义在开发者工具中的展示名称)
-
-
关于组件标签
-
第一种写法:
-
第二种写法:
- 备注: 不使用脚手架时,
会导致后续组件不能渲染
- 备注: 不使用脚手架时,
-
-
一个简写方式
-
const school = Vue.extend(options)
-
const school = options
-
vue组件的嵌套
- 示例demo: 子组件只能写在父组件的模板中
......
<body>
<div id="container">
<xuexiao></xuexiao> <!--父组件-->
<hr>
</div>
<script type="text/javascript">
// 定义子组件
var student = Vue.extend({
template:`
<div>
<h2>学生名称:{{studentName}}</h2>
<h2>年龄: {{age}}</h2>
</div>
`,
data(){
return {
studentName:'JimGreen',
age:18
}
}
})
// 定义父组件
var school = Vue.extend({
template:`
<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>地址: {{address}}</h2>
<button @click="showSchoolName">提示信息</button>
<hr>
<student></student> // 把子组件写进来
</div>
`,
name:'mySchool',
data(){
return {
schoolName:'第一学校',
address:'海淀区',
}
},
methods:{
showSchoolName(){
alert(this.schoolName)
}
},
components:{ // 注册子组件
student
}
})
var vm = new Vue({
el:'#container',
components:{
xuexiao:school, // 注册父组件
}
});
</script>
</body>
- 开发中经常这么干
- vue实例仅仅只管理一个叫'app'的组件
- 由app组件管理众多子组件
......
<body>
<div id="container">
<app></app> <!--只用app组件标签-->
<hr>
</div>
<script type="text/javascript">
// 定义 hello 组件
var hello = Vue.extend({
template:`
<div>
<h2>你好啊~{{name}}</h2>
</div>
`,
data(){
return {
name:'Kate Green'
}
}
})
// 定义student组件
var student = Vue.extend({
template:`
<div>
<h2>学生名称:{{studentName}}</h2>
<h2>年龄: {{age}}</h2>
</div>
`,
data(){
return {
studentName:'JimGreen',
age:18
}
}
})
// 定义 school组件,并把student组件包含进去
var school = Vue.extend({
template:`
<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>地址: {{address}}</h2>
<button @click="showSchoolName">提示信息</button>
<hr>
<student></student>
</div>
`,
name:'mySchool',
data(){
return {
schoolName:'第一学校',
address:'海淀区',
}
},
methods:{
showSchoolName(){
alert(this.schoolName)
}
},
components:{
student
}
})
// 定义app组件,包含 hello组件和school组件
var app = Vue.extend({
template:`
<div>
<hello></hello>
<school></school>
</div>
`,
components:{
hello,
school
}
})
var vm = new Vue({
el:'#container',
components:{
app, // vue实例只管理app组件
}
});
</script>
</body>
组件注意事项
- 本质是 VueComponent 构造函数,是由 vue.extend() 生成的
- 当我们写组件标签,比如<school/>时,vue会帮助我们创建 组件实例对象
即 new VueComponent(options)
- 特别注意:每次调用 vue.extend() 时,生成都是全新的 组件实例对象
- 好比python创建两个类实例对象,这两个实例对象相等么?否...
class Foo(object):
pass
f1 = Foo()
f2 = Foo()
print(f1 == f2) # False
- this的指向
- 在 VueComponent 配置中,this指向 组件实例对象
- 在 Vue实例中,this指向 vm实例
- 简称
- vm实例
- vc实例(组件)
组件vc和vm的区别
- vc相当于vm的小弟,vm有的,vc99%也有
- 1%是'el'配置项,即vm服务于根元素(容器),而vc没有这个功能
js原型对象 review
function Demo(){
this.a = 1;
this.b = 2;
}
var d = new Demo()
// 二者相等,是一模一样的东东
console.log('Demo',Demo.prototype) // 构造函数具有 显示原型对象Demo.prototype
console.log('d',d.__proto__) // 实例对象具有 隐式原型对象 d.__proto__
Demo.prototype.x = 100;
console.log(d.x) // 100
一个重要的内置关系
- Vuecomponent.prototype.__proto__ === Vue.prototype
- 为什么要有这种关系? 让vc可以访问到Vue原型的属性和方法
只要Vue绑定了新的属性,那么vc也有这个新属性