生命周期钩子函数
在 beforCreate 钩子函数调用的时候,是获取不到props或者data中的数据的,因为这些数据的初始化都在initState中。
然后执行created钩子函数,在这个钩子函数中就可以访问props和datas了,但是这时组件还没有挂载。
然后执行beforeMount钩子函数,开始创建VDOM,最后执行mounted钩子,并把VDON渲染成真实DOM并渲染数据。组建中如果有子组件的话,会递归挂载子组件,只有当所有子组件全部挂载完毕,才会还行根组件的挂载钩子。
数据更新时会调用的钩子函数beforeUpdate和Updated,这两个钩子函数分别在数据更新前和更新后会调用。
keep-alive独有的生命周期,分别为actived和deactivated。用keep-alive包裹的组件在切换时不会进行销毁,而是缓存在内存中并执行deavtived钩子函数,命中缓存渲染后会执行actived钩子函数。
最后是销毁组件的钩子函数beforeDestroy 和destroyed 。在beforeDestroy 中适合移除事件、定时器等等。否则可能引起内存泄露。然后进行一系列的销毁操作,如果有子组件,也会递归销毁子组件,所有子组件销毁完毕后会执行根组件的destroyed钩子函数。
组件通信
组件通信一般分为一下几种情况:
父子组件通信
父组件通过props传递数据给子组件,子组件通过emit发送事件传递数据给父组件。
这种通信方式是单向数据流,父组件通过props传递数据,子组件不能直接修改props,而是必须通过发送事件的方式告知父组件修改数据。
还可以通过$parent或者$children来访问组件实例中的方法和数据。
兄弟组件通信
可以通过查找父组件中的子组件实现,也就是this.$parent.$children ,在$children中可以通过组件 name 查询到需要的组件实例,然后通信。
任意组件通信
可以通过Vuex和Event Bus解决。
mixin和mixins区别
mixin 用于全局混入,会影响到每个组件实例。
Vue.mixin({ beforeCreat(){ // 这种方式会影响每个组件的beforeCreate钩子函数 } })
mixins是最常用的扩展组件的方式。如果多个组件中有相同的业务逻辑,就可以将这些逻辑剥离出来,通过mixins混入代码。
mixins混入的钩子函数会先于组件内的钩子函数执行,并且遇到同名选项的时候会有选择性的进行合并。
computed和watch区别
computed是计算属性,依赖其他属性计算值,并且computed的值有缓存,只有当计算只变化才会返回内容。
watch监听到值的变化就会执行回调,在回调中可以进行一些逻辑操作。
一般来说需要依赖别的属性来动态获得值的时候可以使用computed,对于监听到值的变化需要做一些复杂业务逻辑的情况可以使用watch。
组件复用时所有组件实例都会共享data,如果data是对象的话,就会造成一个组件修改data以后影响到其他所有组件,所以需要将data写成函数,每次用到就调用一次函数获得新的数据。
当使用new Vue()方式的时候,无论我们将data设置为对象还是函数都可以,因为new Vue()方式生成一个根组件,该组件不会复用,也就不存在共享data的情况了。
vue内部使用了Object.defineProperty()来实现数据响应式,通过这个函数可以监听到set和get的事件。
var data = {name : 'yck'} observe(data); let name = data.name; // get value data.name = 'yyy' // set value function observe(obj){ // 判断类型 if(!obj || typeof obj !== 'object'){ return; } Object.keys(obj).forEach(key=>{ defineReactive(obj, key, obj[key]) }) } function defineReactive(obj, key, val){ observe(val) Object.defineProperty(obj, key, { enumerable: true, // 可枚举 configurable: true, // 可配置 get: function reactiveGetter(){ return val }, set : funtcion reacticeSetter(newVal){ console.log("change value") val = newVal } }) }
只有执行了依赖收集,才能在属性更新的时候派发更新。
<div>{{name}}</div>
在解析如上模板代码中,会遇到{{name}}就会进行依赖收集
接下来我们实现一个Dep类,用来解耦属性的依赖收集和派发更新操作。
class Dep{ constructor(){ this.subs =[] } addSub(sub){ // 添加依赖 this.subs.push(sub) } notify(){ //更新 this.subs.forEach(sub=>{ sub.update() }) } } Dep.target = null // 全局属性,通过该属性配置Watcher
当需要依赖收集的时候调用addSub,当需要派发更新的时候调用notify。
Vue组件挂载时添加响应式的过程:
触发依赖收集:
class Watcher{ constructor(obj, key, cb){ Dep.target = this; this.cb = cb this.obj = obj this.key = key this.value = obj[key] Dep.target = null } update(){ this.value = this.obj[this.key] this.cb(this.value) } }
此时需要对defineReactive函数进行改造
function defineReactive(obj, key, val){ observe(val) Object.defineProperty(obj, key, { enumerable: true, // 可枚举 configurable: true, // 可配置 get: function reactiveGetter(){ if(Dep.target){ //将Watcher添加到订阅 dp.addSub(Dep.target) } return val }, set : funtcion reacticeSetter(newVal){ console.log("change value") val = newVal dp.notify() // 执行watcher的update方法 } }) }
下面测试一下代码:
var data ={name: 'yck'} observe(data) function update(value){ document.querySelector('div').innerText = value; } new Watcher(data,'name',update); data.name = 'xxx'
Vue会通过编译器将模板通过几个阶段最终编译成render函数,然后通过执行render函数生成Virtual Dom 最终映射为真是的DOM。
编译过程可分为三个阶段:
第一阶段,最主要的事情还是通过各种各样的正则表达式匹配模板中的内容,然后将内容提取出来做各种逻辑操作,接下来生成一个最基本的AST对象
{ type :1, // 类型 tag, // 标签属性 attrsList: attrs, //属性列表 attrsMap : makeAttrsMap(attrs), //属性映射 parent, //父节点 children:[] //子节点 }
在这一阶段,还有一起其他的判断逻辑,比如对比前后开闭标签是否一致……
第二阶段,优化AST阶段,对阶段进行了静态内容提取,即将永远不会变动的节点提取出来,实现复用Virtual DOM。
最后阶段,主要是遍历整个AST,根据不同的条件生成不同的代码。
每张故作坚强的笑脸背后,是怎样风雨漂泊的一生---Lin
本文分享自微信公众号 - 女程序员的日常(gh_df41d619fb70),作者:凛
原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。
原始发表时间:2020-02-22
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。
我来说两句