initState 状态初始化

在配置标准化合并以及声明周期初始化完成之后,会调用 callHook('beforeCreate') 来表示组件已进入正式实例化阶段。

这个时候会对数据、方法、监听器等配置项进行对应的处理,并且在开发环境还会进行一系列校验,抛出校验异常信息。整个数据的初始化过程是 initInjection => initState => initProvide,但是 injection/provide 一般是一起使用,所以这里也替换一下顺序,将这两者放到后面一起分析。

首先是 initState 的函数定义:

 export function initState(vm: Component) {
   const opts = vm.$options
   if (opts.props) initProps(vm, opts.props)

   // Composition API
   initSetup(vm)

   if (opts.methods) initMethods(vm, opts.methods)
   if (opts.data) {
     initData(vm)
   } else {
     const ob = observe((vm._data = {}))
     ob && ob.vmCount  
   }
   if (opts.computed) initComputed(vm, opts.computed)
   if (opts.watch && opts.watch !== nativeWatch) {
     initWatch(vm, opts.watch)
   }
 }

整个过程其实十分清晰:

  • initProps:初始化 props 组件参数配置
  • initSetup:解析 setup 配置,处理 setup 的返回值(这里主要是 2.7 版本之后为了适配 v3 语法新增的内容)
  • initMethods:初始化组件方法
  • initData:初始化组件内变量
  • initComputed:初始化组件计算属性
  • initWatch:初始化组件内部监听器

1. initProps

该函数定义如下:

 function initProps(vm: Component, propsOptions: Object) {
   const propsData = vm.$options.propsData || {}
   const props = (vm._props = shallowReactive({}))
   const keys: string[] = (vm.$options._propKeys = [])
   const isRoot = !vm.$parent
   
   if (!isRoot) {
     toggleObserving(false)
   }
   for (const key in propsOptions) {
     keys.push(key)
     const value = validateProp(key, propsOptions, propsData, vm)
     if (__DEV__) {
       const hyphenatedKey = hyphenate(key)
       if (isReservedAttribute(hyphenatedKey) || config.isReservedAttr(hyphenatedKey)) {
         warn('')
       }
       defineReactive(props, key, value, () => {
         if (!isRoot && !isUpdatingChildComponent) {
           warn('')
         }
       })
     } else {
       defineReactive(props, key, value)
     }
     if (!(key in vm)) {
       proxy(vm, `_props`, key)
     }
   }
   toggleObserving(true)
 }

这个过程会遍历整个组件的 props 配置项(mergeOptions 之后,已包含继承和混入),并将每个 prop key 从驼峰形式转换为 - 短横线连接的形式。

这也是为什么官方推荐 props 配置使用 小写驼峰,而组件使用时绑定参数使用 - 短横线 的原因。

之后,则是校验每个 prop 的 key 是否符合规范(即不是 Vue 的内置关键字 ref,key 等,也是不是配置里面 isReservedAttr(key) 禁止的属性名);最后,通过 defineReactive 来对 prop 进行响应式处理,并挂载到 vm._props 中。

当然,上面的响应式处理 只针对根组件,如果不是根组件的话,是会在函数前面部分调用 toggleObserving(false) 来关闭响应式处理

2. initSetup

这个部分是为了适配 v3 语法新增的一部分,这里就不放源码,只简单介绍一下。

该方法位于 src/v3/apiSetup.ts 文件内,在执行过程中,主要有以下几步:

  • 通过 createSetupContext(vm) 创建一个 setup 函数执行过程中的上下文对象,该对象包括 attrs,listeners,slots,emit 几个属性,以及一个 expose 方法;然后将这个上下文对象绑定到组件的 _setupContext 属性上,最后调用 setCurrentInstance 将当前上下文实例指定为当前组件实例 vm
  • 调用 pushTarget() 阻止过程中的依赖收集,并调用 invokeWithErrorHandling 来获取 setup 函数的返回值
  • 然后删除 setup 中的当前实例上下文,调用 popTarget 恢复依赖收集
  • 判断 setup 函数的执行返回值,如果是函数,则说明返回的是 render,将该返回值赋值给 options 用于后面执行 mount 渲染;如果是对象,则会将返回值赋值给 vm._setupState,然后遍历返回值对象,进行响应式处理

3. initMethods

函数定义如下:

 function initMethods(vm: Component, methods: Object) {
   const props = vm.$options.props
   for (const key in methods) {
     if (__DEV__) {
       if (typeof methods[key] !== 'function') {
         warn('')
       }
       if (props && hasOwn(props, key)) {
         warn('')
       }
       if (key in vm && isReserved(key)) {
         warn('')
       }
     }
     vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
   }
 }

这部分就十分十分简单了,就是通过 Function.bind 函数来更改组件配置 options 中的每一个方法的 this 指向,最后重新绑定到当前组件上。

这里的校验其实就是校验名字是否会和 js 的内置方法名冲突,或者与 props 中存在同名方法。

4. initData

在执行 initData 之前,会校验 options 中有没有 data 配置,没有则会初始化为一个空对象。

其函数定义如下:

 function initData(vm: Component) {
   let data: any = vm.$options.data
   data = vm._data = isFunction(data) ? getData(data, vm) : data || {}
   if (!isPlainObject(data)) {
     data = {}
     __DEV__ && warn('')
   }
   const keys = Object.keys(data)
   const props = vm.$options.props
   const methods = vm.$options.methods
   let i = keys.length
   while (i--) {
     const key = keys[i]
     if (__DEV__) {
       if (methods && hasOwn(methods, key)) {
         warn('')
       }
     }
     if (props && hasOwn(props, key)) {
       __DEV__ && warn()
     } else if (!isReserved(key)) {
       proxy(vm, `_data`, key)
     }
   }
   const ob = observe(data)
   ob && ob.vmCount  
 }

这里其实也比较简单,就是校验是否有与 props 或者 methods 同名的数据,并将其代理到 vm._data 上,最后通过 observe 方法对数据进行响应式处理。

5. initComputed 与 initWatch

这两部分主要是配置对 data 与 props 的变量的 变化侦测(监听) ,因为涉及到 Vue 的响应式系统中的 Watcher 观察者定义 与 依赖收集系统,整体的内容比较多,所以后面整体讲。

简单分析两者的基本逻辑:

initComputed:

  • 获取 options.computed 里定义的每个计算属性的 get 方法作为 getter(如果就是一个函数,则这个函数直接作为 getter)
  • 如果该计算属性的 key 不能在当前的实例上找到,则直接通过 defineComputed 定义一个计算属性
  • 如果能找到,则判断是否是在 data,methods,props 中,并报出对应错误

initWatch:

这个过程则更加简单,因为不用校验 key 的重复性,所以会直接遍历 options.watch,如果某个属性的监听器有两个 handler 方法,还会将方法提出来,最后调用 createWatcher 来创建监听器。

到此这篇关于详解Vue 2中的  initState 状态初始化的文章就介绍到这了,更多相关Vue initState 内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

详解Vue 2中的  initState 状态初始化的更多相关文章

  1. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  2. three.js模拟实现太阳系行星体系功能

    这篇文章主要介绍了three.js模拟实现太阳系行星体系功能,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下

  3. HTML5页面无缝闪开的问题及解决方案

    这篇文章主要介绍了HTML5页面无缝闪开方案,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  4. ios – 为什么,将nil作为参数从Objc C发送到swift类初始化器,用新对象替换nil参数

    除非属性本身被声明为nonnull:

  5. ios – 在Swift中对MKCircle进行子类化

    我想通过添加另一个String属性来继承MKCircle,我们称之为“代码”.这个属性不是可选的和常量的,所以我必须从初始化器设置它,对吧?有没有办法定义一个单一的便利初始化器,在这种情况下需要3个参数?本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  6. ios – AVAudioPlayer不再使用Swift 2.0/Xcode 7 beta

    对于我的iPhone应用程序中的vartestAudio声明,我在这里收到错误“调用可以抛出,但错误不能从属性初始化程序中抛出”当我转到Xcode7测试版时,就发生了这种情况.如何在Swift2.0中使用此音频剪辑?

  7. ios – 斯威夫特.在初始化所有存储的属性之前在方法调用中使用’self’

    解决方法在初始化所有非可选实例变量之前,您无法在self上调用方法.有几种方法可以解决这个问题.>将属性更改为选项或隐式解包选项(不建议)>使buildCircle()方法静态或只是一个在文件中运行并为所有圆圈调用addSubview()在所有属性初始化并且您调用之后super.init()等等.你必须避免在自己之前打电话给自己class已初始化.

  8. ios – Objective-C警告未找到超类“-init”的指定的初始化程序的方法覆盖

    我在一个应用程序中清理警告,我收到了两次这个警告对于这行代码和这一行我相当新的Objective-C和谷歌这个警告,只是不明白的解决方案我的问题是如何摆脱这些警告?

  9. ios – UICollectionView不能使用UISearchController?

    在WWDC2014年的“AInsideInsidePresentationControllers”中,演示者展示了如何在UITableView中设置UISearchController.他们通过设置searchController的searchBar框架,然后将其设置为tableView的tableHeaderView来实现.不幸的是,UICollectionView没有相当于tableHeade

  10. ios7 – 如何使用默认的IOS映像

    嗨,我是IOS开发的新手.我知道如何在IOS应用程序中使用图像.但是我不知道如何使用默认图像,如开发者站点中提到的共享或书签图标.我想用它们我必须下载这些图像集或那些可用在xcode?

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部