vue created 调用方法_浅浅谈vue.js

1、Vue的生命周期

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

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

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

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

eb6df9ec85d51161985f4c5bb0249e00.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)

29d6c545f79778d804e90e13b3e5de97.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属性访问到。

ffd916025a3b2f47c19e69fe040188e0.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钩子函数。

cc0849d63d3116943a44fb873ae573cf.png

9efd678bcf68c9aea3532d4fb15ffd57.png

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

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

b0af8e6d222a3bfa95c35cc1626c2f78.png

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

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

64eea0ed2e7fbd919270f357bd17f635.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等方法,完成数据和方法的初始化工作。

743ab129c61e84c35b3e5aeb08685420.png

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

a994fcf9e280364efabb74bcdb751323.png

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

a136f9fd5c1c3a30e5c75c5398bf9fec.png

b3140a06720b6823c805a52455a4d387.png

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

76a1f57ed37a82b19b7cde3013167809.png

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

0273c02cba91b65717cdaaff7a1615d1.png

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

6bf9840b36430098525316a254ab1165.png

3、Vue模板挂载与渲染

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

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

c070e0295c9e9f0f102bead064b67109.png

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

const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && query(el)

  /* istanbul ignore if */
  if (el === document.body || el === document.documentElement) {
    process.env.NODE_ENV !== 'production' && warn(
      `Do not mount Vue to <html> or <body> - mount to normal elements instead.`
    )
    return this
  }

  const options = this.$options
  // resolve template/el and convert to render function
  if (!options.render) {
    let template = options.template
    if (template) {
      if (typeof template === 'string') {
        if (template.charAt(0) === '#') {
          template = idToTemplate(template)
          /* istanbul ignore if */
          if (process.env.NODE_ENV !== 'production' && !template) {
            warn(
              `Template element not found or is empty: ${options.template}`,
              this
            )
          }
        }
      } else if (template.nodeType) {
        template = template.innerHTML
      } else {
        if (process.env.NODE_ENV !== 'production') {
          warn('invalid template option:' + template, this)
        }
        return this
      }
    } else if (el) {
      template = getOuterHTML(el)
    }
    if (template) {
      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
        mark('compile')
      }

      const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV !== 'production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      options.render = render
      options.staticRenderFns = staticRenderFns

      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
        mark('compile end')
        measure(`vue ${this._name} compile`, 'compile', 'compile end')
      }
    }
  }
  return mount.call(this, el, hydrating)
}

$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 方法。

1a38afa83f4f747591bd318b087df65d.png

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

export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  vm.$el = el
  if (!vm.$options.render) {
    vm.$options.render = createEmptyVNode
    if (process.env.NODE_ENV !== 'production') {
      /* istanbul ignore if */
      if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
        vm.$options.el || el) {
        warn(
          'You are using the runtime-only build of Vue where the template ' +
          'compiler is not available. Either pre-compile the templates into ' +
          'render functions, or use the compiler-included build.',
          vm
        )
      } else {
        warn(
          'Failed to mount component: template or render function not defined.',
          vm
        )
      }
    }
  }
  callHook(vm, 'beforeMount')

  let updateComponent
  /* istanbul ignore if */
  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    updateComponent = () => {
      const name = vm._name
      const id = vm._uid
      const startTag = `vue-perf-start:${id}`
      const endTag = `vue-perf-end:${id}`

      mark(startTag)
      const vnode = vm._render()
      mark(endTag)
      measure(`vue ${name} render`, startTag, endTag)

      mark(startTag)
      vm._update(vnode, hydrating)
      mark(endTag)
      measure(`vue ${name} patch`, startTag, endTag)
    }
  } else {
    updateComponent = () => {
      vm._update(vm._render(), hydrating)
    }
  }

  // we set this to vm._watcher inside the watcher's constructor
  // since the watcher's initial patch may call $forceUpdate (e.g. inside child
  // component's mounted hook), which relies on vm._watcher being already defined
  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted && !vm._isDestroyed) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true /* isRenderWatcher */)
  hydrating = false

  // manually mounted instance, call mounted on self
  // mounted is called for render-created child components in its inserted hook
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
  }
  return vm
}

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
    评论
### 回答1: vue.js devtools_5.3.3_chrome.cn.zip是一种用于谷歌浏览器的vue.js开发工具包,适用于开发vue.js前端应用程序。该工具包可以协助开发者对vue.js应用程序进行调试和监视。它包含了一些非常有用的功能,例如实时调试、状态管理、路由、组件、事件和动画工具等。 除了基本的功能外,vue.js devtools_5.3.3_chrome.cn.zip还提供了其他一些高级功能,例如缩放、搜索、过滤、追踪等。开发者可以使用这些功能来调试js代码、优化性能、测试响应时间等。 总体来说,vue.js devtools_5.3.3_chrome.cn.zip是一款非常实用的开发工具包,它可以协助开发者快速快速定位和解决vue.js应用程序中的问题,以便更好的提升应用程序的质量和用户体验。 ### 回答2: vue.js devtools_5.3.3_chrome.cn.zip是一个用于开发vue.js应用程序的Chrome浏览器插件,它提供了一系列工具和功能,帮助开发人员更好地调试和优化vue.js应用程序。 该插件允许你检查Vue组件的状态、属性、事件、计算属性和生命周期钩子,以及Vue实例的状态和组件层次结构。还可以追踪Vue活动,包括事件和网络请求,以及检查Vue.js开发者工具现有的应用程序状态。 该插件的另一个有用的功能是实时编辑和重载Vue组件,从而加快开发和测试过程。这个特性可以帮助开发人员实时查看他们所做的更改的结果,而无需手动刷新。 使用vue.js devtools_5.3.3_chrome.cn.zip将大大简化Vue.js应用程序开发和调试的过程,并帮助开发人员更加高效地工作。无论是在开发阶段还是在生产环境中,这个插件都是一个必备的工具。 ### 回答3: vue.js devtools_5.3.3_chrome.cn.zip是一款针对Vue.js开发的Chrome浏览器插件,可用于更方便地透视Vue.js应用程序并进行调试。使用此插件,用户可以轻松查看Vue组件树,进行状态和属性的检查和更改,并进行事件分析和调试。此外,vue.js devtools还具有性能分析功能,可以帮助开发者分析应用程序的性能数据,以及验证和优化应用程序的瓶颈。总之,vue.js devtools是一款实用而强大的工具,可以大大提高开发Vue.js应用程序的效率和质量。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值