17、生命周期
17-1、引入生命周期
生命周期:
1.又名:生命周期回调函数、生命周期函数、生命周期钩子。
2.是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。
3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
4.生命周期函数中的this指向是vm 或 组件实例对象。
5.第一次放入页面叫挂载,后面的叫更新
什么是挂载(mounted)?
Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted
案例如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>引出生命周期</title> <script type="text/javascript" src="Vue/js/vue.js"></script> </head> <body> <div id="app"> <h2 v-if="a">你好啊</h2> <h2 :style="{opacity}">欢迎学习Vue</h2> </div> </body> <script type="text/javascript"> const vm = new Vue({ el:'#app', data:{ a:false, opacity:1 }, methods: { }, //Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted mounted(){ console.log('mounted',this) setInterval(() => { this.opacity -= 0.01 if(this.opacity <= 0) this.opacity = 1 },16) }, }) //通过外部的定时器实现(不推荐) /* setInterval(() => { vm.opacity -= 0.01 if(vm.opacity <= 0) vm.opacity = 1 },16) */ </script> </html>
17-2、分析生命周期(挂载、更新、与销毁)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>分析生命周期</title> <script type="text/javascript" src="Vue/js/vue.js"></script> </head> <body> <div id="app" :x="n"> <h2 v-text="n"></h2> <h2>当前的n值是:{{n}}</h2> <button @click="add">点我n+1</button> <button @click="bye">点我销毁vm</button> </div> </body> <script type="text/javascript"> new Vue({ el:'#app', //template是母版,使用时用render函数渲染 // template:` // <div> //不能使用template做完组件代替根元素 ,但是可以做为非根元素使用 // <h2>当前的n值是:{{n}}</h2> // <button @click="add">点我n+1</button> // </div> // `, data:{ n:1 }, methods: { add(){ console.log('add') this.n++ }, bye(){ console.log('bye') this.$destroy() //当vm.$destroy被调用时,完全销毁一个实例,清理它与其他实例的连接,解绑了(自定义)事件监听器。 } }, watch:{ n(){ console.log('n变了') } }, beforeCreate() { console.log('beforeCreate') //此处没有vm中的_data、methods中的配置方法。 }, created() { console.log('created') //此处开始有vm和_data、methods中的配置方法以及数据监测和数据代理。 }, beforeMount() { console.log('beforeMount') //此处呈现的是未经Vue编译的DOM结构,所有对DOM的操作都是不奏效的 }, mounted() { console.log('mounted') //页面中呈现的是经Vue编译的DOM,至此初始化结束 //在此处可以进行:开启定时器、发送网络请求、订阅消息、绑定自定义事件、等初始化操作。 }, beforeUpdate() { console.log('beforeUpdate') //此处页面是新的,但是页面是旧的 }, updated() { console.log('updated') }, beforeDestroy() { console.log('beforeDestroy') // 此时vm中的data、methods、指令等均可用,此时方法修改不会触发更新。 //在此阶段,一般进行关闭定时器、取消订阅消息、解绑自定义事件等。 }, destroyed() { console.log('destroyed') 此时已经移除监听、子组件、(自定义)事件监听器。 }, }) </script> </html>
17-3、总结生命周期
vm的一生(vm的生命周期):
将要创建==>调用beforeCreate函数
创建完毕==>调用created函数
(重要)将要挂载==>调用beforeMount函数
挂载完毕==>调用mounted函数=====>【重要的钩子】
将要更新==>调用beforeUpdate函数
更新完毕==>调用updated函数
(重要)将要销毁==>调用beforeDestroy函数=>重要的钩子
销毁完毕==>调用destroyed函数
常用的生命周期钩子:
1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
关于销毁Vue实例
1.销毁后借助Vue开发者工具看不到任何信息。
2.销毁后自定义事件会失效,但原生DOM事件依然有效。
3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>引出生命周期</title> <script type="text/javascript" src="Vue/js/vue.js"></script> </head> <body> <div id="app"> <h2 :style="{opacity}">欢迎学习Vue</h2> <button @click="opacity = 1">透明度设置为1</button> <button @click="stop">点我停止变换</button> </div> </body> <script type="text/javascript"> new Vue({ el:'#app', data:{ opacity:1 }, methods: { stop(){ this.$destroy() } }, //Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted mounted(){ console.log('mounted',this) this.timer = setInterval(() => { console.log('setInterval') this.opacity -= 0.01 if(this.opacity <= 0) this.opacity = 1 },16) }, beforeDestroy() { clearInterval(this.timer) console.log('vm即将驾鹤西游了') }, }) </script> </html>
第二部分、Vue组件化编程
1、模块与组件、模块化与组件化
1-1、模块:
1. 理解: 向外提供特定功能的 js 程序, 一般就是一个 js 文件
2. 为什么: js 文件很多很复杂
3. 作用: 复用 js, 简化 js 的编写, 提高 js 运行效率
1-2、组件:
1. 理解: 用来实现局部(特定)功能效果的代码集合(html/css/js/image…..)
2. 为什么: 一个界面的功能很复杂
3. 作用: 复用编码, 简化项目编码, 提高运行效率。
1-3、模块化:
当应用中的 js 都以模块来编写的, 那这个应用就是一个模块化的应用。
1-4、组件化:
当应用中的功能都是多组件的方式来编写的, 那这个应用就是一个组件化的应用。
2、非单文件组件
2-1、基本使用
一个文件包含有n个组件。
Vue中使用组件的三大步骤:
一、定义组件(创建组件)
二、注册组件
三、使用组件(写组件标签)
一、如何定义一个组件?
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别;
区别如下:
1.el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。
2.data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系。
备注:使用template可以配置组件结构。
二、如何注册组件?
1.局部注册:靠new Vue的时候传入components选项
2.全局注册:靠Vue.component('组件名',组件)
三、编写组件标签:
<school></school>
案例如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>基本使用</title> <script type="text/javascript" src="Vue/js/vue.js"></script> </head> <body> <div id="app"> <hello></hello> <hr> <h1>{{msg}}</h1> <hr> <!-- 第三步:编写组件标签 --> <school></school> <hr> <!-- 第三步:编写组件标签 --> <student></student> </div> <div id="root"> <hello></hello> </div> </body> <script type="text/javascript"> Vue.config.productionTip = false //第一步:创建school组件 const school = Vue.extend({ template:` <div class="demo"> <h2>学校名称:{{schoolName}}</h2> <h2>学校地址:{{address}}</h2> <button @click="showName">点我提示学校名</button> </div> `, // el:'#app', //组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器。 data(){ return { schoolName:'111', address:'002' } }, methods: { showName(){ alert(this.schoolName) } }, }) //第一步:创建student组件 const student = Vue.extend({ template:` <div> <h2>学生姓名:{{studentName}}</h2> <h2>学生年龄:{{age}}</h2> </div> `, data(){ return { studentName:'张三', age:18 } } }) //第一步:创建hello组件 const hello = Vue.extend({ template:` <div> <h2>你好啊!{{name}}</h2> </div> `, data(){ return { name:'Tom' } } }) //第二步:全局注册组件 Vue.component('hello',hello) //创建vm new Vue({ el:'#app', data:{ msg:'你好啊!' }, //第二步:注册组件(局部注册) components:{ school, student } }) new Vue({ el:'#root', }) </script> <!-- 使用对象是引用数据,你用我用大家用,函数返回值,是复印了一份新的给你,你随便改,影响不了原本 --> </html>
2-2、几个注意点
1.关于组件名:
一个单词组成:
第一种写法(首字母小写):school
第二种写法(首字母大写):School
多个单词组成:
第一种写法(kebab-case命名):my-school
第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)
备注:
(1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。
(2).可以使用name配置项指定组件在开发者工具中呈现的名字。
2.关于组件标签:
第一种写法:
第二种写法:
备注:不用使用脚手架时,会导致后续组件不能渲染。
3.一个简写方式:
const school = Vue.extend(options) 可简写为:const school = options
案例如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>几个注意点</title> <script type="text/javascript" src="Vue/js/vue.js"></script> </head> <body> <div id="app"> <h1>{{msg}}</h1> <school></school> </div> </body> <script type="text/javascript"> //定义组件 const s = Vue.extend({ name:'222', template:` <div> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> </div> `, data(){ return { name:'111', address:'002' } } }) new Vue({ el:'#app', data:{ msg:'欢迎学习Vue!' }, components:{ school:s } }) </script> </html>
2-3、组件的嵌套
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>组件的嵌套</title> <script type="text/javascript" src="Vue/js/vue.js"></script> </head> <body> <div id="root"></div> </body> <script type="text/javascript"> //定义student组件 const student = Vue.extend({ name:'student', template:` <div> <h2>学生姓名:{{name}}</h2> <h2>学生年龄:{{age}}</h2> </div> `, data(){ return { name:'222', age:18 } } }) //定义school组件 const school = Vue.extend({ name:'school', template:` <div> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> <student></student> </div> `, data(){ return { name:'111', address:'111' } }, //注册组件(局部) components:{ student } }) //定义hello组件 const hello = Vue.extend({ template:`<h1>{{msg}}</h1>`, data(){ return { msg:'欢迎来学习!' } } }) //定义app组件 const app = Vue.extend({ template:` <div> <hello></hello> <school></school> </div> `, components:{ school, hello } }) //创建vm new Vue({ template:'<app></app>', el:'#root', //注册组件(局部) components:{app} }) </script> </html>
2-4、VueComponent
关于VueComponent:
1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。
2.我们只需要写或,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)。
3.特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!
4.关于this指向:
(1).组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。
(2).new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。
5.VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。Vue的实例对象,以后简称vm。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>VueComponent</title> <script type="text/javascript" src="Vue/js/vue.js"></script> </head> <body> <div id="root"> <school></school> <hello></hello> </div> </body> <script type="text/javascript"> //定义school组件 const school = Vue.extend({ name:'school', template:` <div> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> <button @click="showName">点我提示学校名</button> </div> `, data(){ return { name:'111', address:'001' } }, methods: { showName(){ console.log('showName',this) } }, }) const test = Vue.extend({ template:`<span>111</span>` }) //定义hello组件 const hello = Vue.extend({ template:` <div> <h2>{{msg}}</h2> <test></test> </div> `, data(){ return { msg:'你好啊!' } }, components:{test} }) // console.log('@',school) // console.log('#',hello) //创建vm const vm = new Vue({ el:'#root', components:{school,hello} }) </script> </html>
2-5、一个重要的内置关系
Demo.prototype 显示原型属性
Demo.__proto- - 隐式原型属性
实例的隐式原型属性,永远指向自己缔造者的原型对象
通过隐式原型链获取原型的属性,从自身沿着原型链一直找直到window的原型为空
通过显示原型链给原型(或者原型的原型)添加属性
1.一个重要的内置关系:
VueComponent.prototype.__proto__ === Vue.prototype
2.为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>一个重要的内置关系</title> <script type="text/javascript" src="Vue/js/vue.js"></script> </head> <body> <div id="root"> <school></school> </div> </body> <script type="text/javascript"> Vue.prototype.x = 99 //定义school组件 const school = Vue.extend({ name:'school', template:` <div> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> <button @click="showX">点我输出x</button> </div> `, data(){ return { name:'111', address:'001' } }, methods: { showX(){ console.log(this.x) } }, }) //创建一个vm const vm = new Vue({ el:'#root', data:{ msg:'你好' }, components:{school} }) //定义一个构造函数 /* function Demo(){ this.a = 1 this.b = 2 } //创建一个Demo的实例对象 const d = new Demo() console.log(Demo.prototype) //显示原型属性 console.log(d.__proto__) //隐式原型属性 console.log(Demo.prototype === d.__proto__) //程序员通过显示原型属性操作原型对象,追加一个x属性,值为99 Demo.prototype.x = 99 console.log('@',d) */ </script> </html>
3、ES6模块暴露与模块引入
3-1、ES6一共有三种模块暴露方法
(1)统一暴露
模块 module1:module1.js
function fun1() { console.log('fun1() module1') } function fun2() { console.log('fun2() module1') } // 统一暴露 export {foo,bar}
(2)分别暴露
模块 module2:module2.js
//多行暴露 export function foo() { console,console.log('foo() module2'); } export function bar() { console.log('bar() module2') }
以上两种向外暴露方式在主文件引入时必须使用对象的解构赋值引用(不能使用变量接收的方式来映入)
主模块:main.js
import {foo,bar} from '.js/src/module2.js' import {fun1,fun2} from './js/src/module1.js'
(3)默认暴露
export default { foo() { console.log('默认暴露方式') }, bar() { console.log('默认暴露') } }
默认暴露的方式只允许有一个: export default {}
且在主模块引入时可以使用定义变量来接收的方式!
// 引入模块3 import module3 from '.js/src/module3.js' // 使用模块 module3.foo() module3.bar()
3-2、总结
- 对外暴露出的都是一个对象,但是不同的暴露方法实际暴露的对象有所不同
- 默认并暴露的实质就是使用 export ➕ 去除import关键字后的引入写法
- 默认暴露在进行引入并暴露时不能进行简写
- import引入的文件会自动收集在文件的最上方,并按照引入的顺序执行
4、单文件组件
一个文件只包含有1个组件。
School.vue组件
<template> <!-- 组件的结构 --> <div class="demo"> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> <button @click="showName">点我提示学校名</button> </div> </template> <script> // 组件交互相关的代码(数据、方法等等) // export school = Vue.extend({...})分别暴露 export default { name:'School', data(){ return { name:'111', address:'001' } }, methods: { showName(){ alert(this.name) } }, } // export {school} 统一暴露 </script> <style> /* 组件的样式 */ .demo{ background-color: orange; } </style> Student.vue组件 <template> <div> <h2>学生姓名:{{name}}</h2> <h2>学生年龄:{{age}}</h2> </div> </template> <script> export default { name:'Student', data(){ return { name:'张三', age:18 } } } </script> App.vue组件 <template> <div> <School></School> <Student></Student> </div> </template> <script> //引入组件 import School from './School.vue' import Student from './Student.vue' export default { name:'App', components:{ School, Student } } </script> index.js (这个是引入vm,) import App from './App.vue' new Vue({ el:'#root', template:`<App></App>`, components:{App}, }) index.html <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>练习一下单文件组件的语法</title> </head> <body> <!-- 准备一个容器 --> <div id="root"></div> <!-- <script type="text/javascript" src="../js/vue.js"></script> --> <!-- <script type="text/javascript" src="./index.js"></script> --> </body> </html>