js数组获取index_浅浅谈vue.js

1、Vue的生命周期

在Vue官方文档中生命周期函数有如下定义

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做 生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。

简单的说,生命周期就是Vue实例在某个时间点下会自动执行的函数。为了加深对Vue生命周期函数的理解,接下来讲从源码的角度对Vue生命周期进行分析。

Vue实例化的入口为/src/core/instance/index.js,可以看到vue实际上是一个利用ES5语法定义得到类。在new vue时还会执行一系列的初始化函数,他们的主要作用是往vue的原型链上挂载一系列的方法。

9c41fedf940fa2daa09caea18d92801d.png

1、beforeCreate和created(/src/core/instance/state.js)

beforeCreated和created都是在实例化Vue的阶段在_init方法中执行的,由于beforeCreated和created的调用是在initState前后,而initState的作用是初始化props、data、methods、watch和compute等属性的。所以在initState之前调用的beforeCreated是取不到这些属性的,而在initState之后调用的created则可以。(虽然一般用不到这种极限操作,不过实际在initState中这些属性的初始化也有先后顺序的,props->methods->data->computed->watch)

68ae87f7f7eac6932e32e3bf2a2f9b10.png

2、beforeMount和mounted(/src/core/instance/lifecycle.js)

beforeMount和mounted的调用发生在DOM挂载的前后,在执行vm.render函数渲染Vnode之前执行了beforeMount,在执行完vm.update把VNode patch到真实的DOM上之后mounted钩子函数。也就是说在beforeMount之前Vue就会找到对应的template并将其编译成render函数,在mounted执行前将其挂载到DOM上。所以再mounted钩子函数中可以通过DOM API获取DOM节点,同时可以利用$ref属性访问到。

5d8160b2948f96fd51992d4f892c2d6c.png

3、beforeUpdate和updated(/src/core/instance/lifecycle.js和/src/core/observer/scheduler.js)

beforeUpdate和updated的钩子函数执行时机都应该是在数据更新的时候,如下图所示在执行beforeUpdate和updated前会先判断DOM是否挂载,也就是说在第一次数据变化(mounted前)是不会执行befoUpdate和updated函数的。然后在flushSchedulerQueue 函数中对DOM进行重新渲染,在渲染完成后调用updated钩子函数。

7c9af4b16d58824f478d4c5e9ac6539c.png

e3a1161fa4bce5b56c37345ac6960623.png

4、beforeDestory和destoryed(/src/core/instance/lifecycle.js)

beforeDestory和destoryed两者的执行时机都是在组件销毁的阶段。在组件销毁时都会调用$destory方法,beforeDestory的执行时机是$destory最开始的地方,之后执行了一系列的销毁动作,包括删除掉所有的self,_watch,数据引用等,执行完后调用了destoryed。所以beforeDestory是在实例销毁前调用的,在这一步Vue实例还完全可用。

2245349e2322dcc484839e701888693a.png

5、activated和deactivated(/src/core/instance/lifecycle.js)

activated和deactivated是专门为keep-alive组件定制的钩子函数,由于组件一旦被keep-alive缓存,那么再次渲染时就不会执行created和mounted函数,但有时又需要在再次渲染时做一些事,所以就有了这个钩子函数。

10c511854d4104536611ecb79dffa737.png

5、总结各钩子函数的使用场景

1)beforecreate

完成实例初始化,初始化非响应式变量

this指向创建的实例

可以在这加个loading事件

data computed watch methods上的方法和数据均不能访问

2)created

实例创建完成

完成数据(data props computed)的初始化 导入依赖项

可访问data computed watch methods上的方法和数据

未挂载DOM,不能访问$el,$ref为空数组

可在这结束loading,还做一些初始化,实现函数自执行

可以对data数据进行操作,可进行一些请求,请求不易过多,避免白屏时间太长

若在此阶段进行的 DOM 操作一定要放在 Vue.nextTick() 的回调函数中

3)berofeMount

有了el,编译了template|/outerHTML

能找到对应的template,并编译成render函数

4)mounted

完成创建vm.$el,和双向绑定

完成挂载DOM 和渲染;可在mounted钩子对挂载的dom进行操作

即有了DOM 且完成了双向绑定 可访问DOM节点,$ref

可在这发起后端请求,拿回数据,配合路由钩子做一些事情

可对DOM 进行操作

5)beforeUpdate

数据更新之前

可在更新前访问现有的DOM,如手动移除添加的事件监听器

6)updated

完成虚拟DOM的重新渲染和打补丁

组件DOM 已完成更新

可执行依赖的dom 操作

注意:不要在此函数中操作数据,会陷入死循环的

7)activated

在使用vue-router时有时需要使用<keep-alive></keep-alive>来缓存组件状态,这个时候created钩子就不会被重复调用了,

如果我们的子组件需要在每次加载的时候进行某些操作,可以使用activated钩子触发

8)deactivated

keep-alive 组件被移除时使用

9)beforeDestroy

在执行app.$destroy()之前

可做一些删除提示,如:你确认删除XX吗?

可用于销毁定时器,解绑全局时间 销毁插件对象

10)destroyed

当前组件已被删除,监听事件、组件、事件、子实例也被销毁,这时组件已经没有了,无法操作里面的任何东西了。

2、Vue响应式

在Vue官方文档中是这么描述响应式的

Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。这使得状态管理非常简单直接,不过理解其工作原理同样重要,这样你可以避开一些常见的问题。

其实简单点说Vue的响应式就是当数据发生变化后,会重新对页面渲染。但是这又是怎么实现的呢。接下来就会根据vue源码对,响应式做一下简单的分析。

响应式的研究对象是数据,而在前面Vue生命周期的描述中我们知道,数据的初始化是在initState方法中进行的,所以我们再来看看initState方法。在initState方法中会依次调用initProps,initMethods,initData,initComputed,initWatch等方法,完成数据和方法的初始化工作。

fb081afdef76455c237663e08bc42ba9.png

响应式中我们更关心数据的变化,所以先来看看initData方法。在initData方法中首先判断data是否是对象,之后用遍历methods和props,把不同名的data代理到vm上(明白同名为啥会报错了吧)。最后利用observe方法来监听data的变化。

c4de22ef2862087c4f164055cc1316c1.png

来看看observe相关的逻辑,Observer函数将在传入的value对象中添加一个”__ob__”属性,将自己赋给这个属性。如果value是数组调用observeArray函数,来对每个元素单独执行observe。不然对value执行walk,在walk中执行defineReactive函数。

4994318d79214f0b09ca3ae3fc1b332c.png

441fd964359c3fb05bbd2040a87e25ea.png

在definereactivity中会调用defineProperty方法,利用defineProperty中的get和set属性,实现在取值和赋值时添加一些操作。给value设置了getter和setter,在getter中进行依赖收集,在setter中派发更新。收集依赖的目的是为了当这些响应式数据发送变化,触发它们的 setter 的时候,能知道应该通知哪些订阅者去做相应的逻辑处理,我们把这个过程叫派发更新,这是一个非常经典的观察者设计模式的实现。不过这里就不对依赖收集和派发更新的具体实现进行叙述了,不然又是一个很大的篇幅。

d7c307d94ec81db94e2bf6691b35c203.png

通过defineProperty实现响应式,但这其中还是存在一些问题无法检测到对象属性的添加或删除,这是因为 Vue 通过Object.defineProperty来将对象的key转换成getter/setter的形式来追踪变化,但getter/setter只能追踪一个数据是否被修改,无法追踪新增属性和删除属性。如果是删除属性,我们可以用vm.$delete实现,那如果是新增属性,Vue则提供了set (/src/core/observer/index.js)方法供我们使用。

5b715b1d09cae0e39fa1cbb37411d5ba.png

对于其他的一些数组的操作Vue则重写了这些方法,所以并非这些函数碰巧可以在vue中能用,而是vue对此做了一些操作。能够直接使用的数组函数也只有这些了。

3de6737b1fa1fd41add6df9f6dac568d.png

3、Vue模板挂载与渲染

要分析Vue模板的挂载与渲染就要知道模板是什么,其实从本质上来说模板的本质就是字符串(早期没有模板引擎时,对后端返回的数据就是利用字符串拼接的方式来产生HTML标签的,个人觉得这应该就是模板的前身了)。但是模板这种字符串又和我们传统意义上的字符串有些不同,他有for循环有if判断,所以可以说模板是一种有逻辑的字符串。但是字符串就是一种数据类型,怎么会有逻辑呢?所以这里就需要对字符串进行一些转化了,由于前端三剑客中只有JS是图灵完备的语言,所以这里就需要把模板转化成js函数。也就是生成Vue的render函数。

在vue-cli生成的项目中,在main.js函数里有如下代码,其中$mount函数就是生成render函数的关。

cddf6ed2ced3d304b159cac95826173a.png

由于Vue值有很多平台的兼容性代码,所以也定义了很多套的$mount函数,这里我们研究平时较为常用的compiler版本入口为src/platforms/web/entry-runtime-withcompiler.js 。

const 

$mount方法支持传入 2 个参数,第一个是el,它表示挂载的元素,可以是字符串,也可以是 DOM 对象,如果是字符串在浏览器环境下会调用query方法转换成 DOM 对象的。第二个参数是和服务端渲染相关,在浏览器环境下我们不需要传第二个参数。$mount之后又对el做了限制,Vue 不能挂载在body、html这样的根节点上。接下来如果没有定义render方法,则会把el或者template字符串转换成render方法。在 Vue 2.0 中,所有 Vue 的组件的渲染最终都需要render方法,这个过程相当于一个在线编译的过程是利用compileToFunctions实现的。compileToFunctions实现有点复杂,这里就不再多说。

这里的$mount方法实际上是对原型链上的$mount方法进行了复用和改写,原型链上的$mount方法定义在src/platform/web/runtime/index.js 中,可以看到这里的$mount方法应该是一个简化版,最后相当于执行了mountComponent 方法。

f3522cfcf26bbc13b35f43b9ebba4165.png

mountComponent 方法定义在src/core/instance/lifecycle.js中

export 

mountComponent 核心就是先调用 vm._render 方法先生成虚拟 Node,再实例化一个渲染Watcher,在它的回调函数中会调用 updateComponent 方法,最终调用 vm._update 更新 DOM。Watcher 在这里起到两个作用,一个是初始化的时候会执行回调函数,另一个是当 vm 实例中的监测的数据发生变化的时候执行回调函数。函数最后判断为根节点的时候设置vm._isMounted为true, 表示这个实例已经挂载了,同时执行mounted钩子函数。至此模板的挂载和渲染完成。

最后总结一下整个模板挂载和渲染的过程

1、确定模板的挂载点,并确保这个确保这个模板挂载点不为html,body这类跟节点(因为模板会把这个挂载点替换掉,把html或body替换了可能会出现问题)。

2、vue中有两种渲染方式,一种是通过template模板字符串,另一个是通过手写render函数。对于template模板则需要先调用compileToFunctions方法,将模板字符串转化为抽象语法树(AST),最后转化为render渲染函数,参与实例的挂载。而对于手写的render函数则可以绕过编译阶段,直接调用$mount方法。

3、无论是template模板还是手写render函数,最终都将进入mountComponent过程,这个阶段会实例化一个渲染watcher,渲染watcher的回调函数有两个执行时机,一个是在初始化时执行,另一个是当vm实例检测到数据发生变化时会再次执行回调函数。

4、回调函数是执行updateComponent的过程,这个方法有两个阶段,一个是vm._render,另一个是vm._update。 vm._render会执行前面生成的render渲染函数,并生成一个Virtual Dom tree,而vm._update会将这个Virtual Dom tree转化为真实的DOM节点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值