for里面调用方法 vue_Vue源码阅读连载之Vue实例

09f8d3084cfc894bd3089c82677bb1fd.png

我们学习Vue都是从下面这个例子开始的

new Vue({render: h => h(App),
}).$mount('#app')

事实上,所有的Vue项目的组成组件都是一个Vue的实例,最后由根部的Vue实例去挂载到DOM上,当然这个"挂载"的操作可以针对不同的平台而有不同的行为,比如挂载到移动设备上就成了weex,挂载到小程序上就成了mpvue。所以首先我们要知道Vue实例里面含有哪些东西。

实际上,这个$mount就等同于React里的ReactDOM.render。

Vue构造函数

我是全局搜索关键词"function Vue"来定位到Vue类的定义的,位于/src/core/instance/index.js里。

function Vue (options) {if (process.env.NODE_ENV !== 'production' &&!(this instanceof Vue)) {warn('Vue is a constructor and should be called with the `new` keyword')}this._init(options)
}initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)export default Vue

代码很简单,就是用function的方式定义了Vue类,之后只要new Vue一下就可以生成Vue的实例了。在构造函数里我们看到调用了_init( )方法,但是上下文中并没有看到_init( )方法的定义,往下看,有一堆的mixin方法,这些方法其实就是在Vue的prototype上增加各种方法,_init( )也就是在这些mixin调用过后添加的。

列举一下Vue里约定俗成前缀

_ 表示私有,这个和一般的约定一致。

$ 表示实例上的属性或方法,比如$mount。

另外还有一些方法是定义在全局的,也就是Vue构造函数上的,比如Vue.component。

initMixin

刚才看到的_init( )方法就是在这里定义出来的,抽掉一些不重要的代码后

Vue.prototype._init = function (options?: Object) {const vm: Component = thisvm._uid = uid++vm._isVue = trueif (options && options._isComponent) {initInternalComponent(vm, options)} else {vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor),options || {},vm)}// expose real selfvm._self = vminitLifecycle(vm)initEvents(vm)initRender(vm)callHook(vm, 'beforeCreate')initInjections(vm) // resolve injections before data/propsinitState(vm)initProvide(vm) // resolve provide after data/propscallHook(vm, 'created')if (vm.$options.el) {vm.$mount(vm.$options.el)}}
}

这一段执行完成后,相当于生命周期的

8b55b2980ffd63d4b6fb76156b094d80.png

大致干了这些事情

对于每一个生成的Vue实例在内部都用_uid来跟踪

合并option对象

initLifecycle方法里定义了许多和生命周期有关的变量,有些是内部的,有些是暴露在实例上的

export function initLifecycle (vm: Component) {const options = vm.$options// locate first non-abstract parentlet parent = options.parentif (parent && !options.abstract) {while (parent.$options.abstract && parent.$parent) {parent = parent.$parent}parent.$children.push(vm)}vm.$parent = parentvm.$root = parent ? parent.$root : vmvm.$children = []vm.$refs = {}vm._watcher = nullvm._inactive = nullvm._directInactive = falsevm._isMounted = falsevm._isDestroyed = falsevm._isBeingDestroyed = false
}

initEvents针对配置里传进来事件,做了一些操作。

initRender里初始化了一些跟虚拟DOM渲染有关的属性和方法,比如后面会登场的赫赫有名的_c方法和实例上的$createElement方法,而把方法连接到实例上其实就是为了在渲染的时候能获得实例的上下文

export function initRender (vm: Component) {vm._vnode = null // the root of the child treevm._staticTrees = null // v-once cached treesconst options = vm.$optionsconst parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent treeconst renderContext = parentVnode && parentVnode.contextvm.$slots = resolveSlots(options._renderChildren, renderContext)vm.$scopedSlots = emptyObjectvm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)}

接着callHook方法第一次登场,从名字就可以看出来callHook就是用来调用钩子函数。

export function callHook (vm: Component, hook: string) {// #7573 disable dep collection when invoking lifecycle hookspushTarget()const handlers = vm.$options[hook]const info = `${hook} hook`if (handlers) {for (let i = 0, j = handlers.length; i < j; i++) {invokeWithErrorHandling(handlers[i], vm, null, vm, info)}}if (vm._hasHookEvent) {vm.$emit('hook:' + hook)}popTarget()
}

它还是通过$emit来调用的,所以在后面的章节要睁大眼睛看一下Vue里事件的实现。

下面的Injection和Provider有点陌生,查了下官网上说

provideinject主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。

而夹在中间的initState( )又是重中之重,初始化了Vue的几大响应式组成部分,计划下一篇文章就是探究这方面的原理的。

最后如果找到配置项里有el,就执行挂载的方法。当然也可以如之前的代码所示,调用Vue实例上的$mount来执行挂载,对应到生命周期图里的这个部分。

f869dd8db654fb497a409cdb26bb8093.png

stateMixin

这里面定义了一个响应式里很重要的方法——$watch,也会放到后文来详述。

eventsMixin

实例上事件的四大方法

  • vm.$on
  • vm.$once
  • vm.$off
  • vm.$emit

先看$on

Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {const vm: Component = thisif (Array.isArray(event)) {for (let i = 0, l = event.length; i < l; i++) {vm.$on(event[i], fn)}} else {(vm._events[event] || (vm._events[event] = [])).push(fn)if (hookRE.test(event)) {vm._hasHookEvent = true}}return vm}

其实就是把传入的方法回调,放在Vue的实例上,而事件的名字就作为键,对应的值是一个数组,表明同一个事件可以调用多个回调方法,传入的event也能是一个数组,不过这种不同的事件上调用同一个回调的事情现实中应该不多吧。

可以做一下实验

4ee3c356e5c380e0ad054c18f4a8ade8.png

有$on就必有$off

Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {const vm: Component = this// allif (!arguments.length) {vm._events = Object.create(null)return vm}// array of eventsif (Array.isArray(event)) {for (let i = 0, l = event.length; i < l; i++) {vm.$off(event[i], fn)}return vm}// specific eventconst cbs = vm._events[event]if (!cbs) {return vm}if (!fn) {vm._events[event] = nullreturn vm}// specific handlerlet cblet i = cbs.lengthwhile (i--) {cb = cbs[i]if (cb === fn || cb.fn === fn) {cbs.splice(i, 1)break}}return vm}

原理也很简单,就是寻找——删除。

而$once本质就是在这个事件回调上做了替换,先调用一下这个传入的回调方法,执行完成后调用一下$off把它删除,达到只调用一次的目的。

Vue.prototype.$once = function (event: string, fn: Function): Component {const vm: Component = thisfunction on () {vm.$off(event, on)fn.apply(vm, arguments)}on.fn = fnvm.$on(event, on)return vm}

$emit的话就是寻找——调用。

Vue.prototype.$emit = function (event: string): Component {const vm: Component = thislet cbs = vm._events[event]if (cbs) {cbs = cbs.length > 1 ? toArray(cbs) : cbsconst args = toArray(arguments, 1)const info = `event handler for "${event}"`for (let i = 0, l = cbs.length; i < l; i++) {invokeWithErrorHandling(cbs[i], vm, args, vm, info)}}return vm}

注意一下这几个方法调用返回的都是Vue实例本身,说明实际中可以写成链式调用的形式。

lifecycleMixin

定义了$forceUpdate,强行在Vue实例上的所有Watcher对象都调用一把更新,后面讲响应式原理的时候会谈到什么是Watcher对象。

Vue.prototype.$forceUpdate = function () {const vm: Component = thisif (vm._watcher) {vm._watcher.update()}}

又定义了$destroy

Vue.prototype.$destroy = function () {const vm: Component = thisif (vm._isBeingDestroyed) {return}callHook(vm, 'beforeDestroy')vm._isBeingDestroyed = true// remove self from parentconst parent = vm.$parentif (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {remove(parent.$children, vm)}// teardown watchersif (vm._watcher) {vm._watcher.teardown()}let i = vm._watchers.lengthwhile (i--) {vm._watchers[i].teardown()}// remove reference from data ob// frozen object may not have observer.if (vm._data.__ob__) {vm._data.__ob__.vmCount--}// call the last hook...vm._isDestroyed = true// invoke destroy hooks on current rendered treevm.__patch__(vm._vnode, null)// fire destroyed hookcallHook(vm, 'destroyed')// turn off all instance listeners.vm.$off()// remove __vue__ referenceif (vm.$el) {vm.$el.__vue__ = null}// release circular reference (#6759)if (vm.$vnode) {vm.$vnode.parent = null}}

对应了生命周期图上的这个部分

0245681f25108b4dc71886a4c23f8d92.png

清理工作还是挺多的

  1. 和父组件的引用关系中拆分出来
  2. 清理所有Watcher对象
  3. 清理所有Observer对象
  4. 移除所有事件
  5. 清理挂载的DOM元素

renderMixin

主要是定义了跟虚拟DOM渲染有关的属性($vnode)和私有方法(_render),在后面讲到虚拟DOM的时候再来讲。

下一篇文章要讲Vue响应式原理了,想想都激动。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499070.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机组装怎么备份系统,手把手教你用GHOST备份还原安装电脑系统详细图文教程...

首先我们可以利用之前安装系统时提到的带启动功能和工具的系统安装盘&#xff0c;比如98启动盘或番茄花园的光盘&#xff0c;如果你用的U盘上带有GHOST工具&#xff0c;那很简单&#xff0c;我们还是要设置由光驱为第一启动项&#xff0c;重新启动电脑让电脑由光驱启动&#xf…

python车牌识别系统开源代码_python利用百度云接口实现车牌识别的示例

一个小需求---实现车牌识别。 目前有两个想法 1. 调云在线的接口或者使用SDK做开发&#xff08;配置环境和编译第三方库很麻烦&#xff0c;当然使用python可以避免这些问题&#xff09; 2. 自己实现车牌识别算法&#xff08;复杂&#xff09; 一开始准备使用百度云文字识别C SD…

c 包含其他文件_C/C++编程笔记:C/C++的编译和链接,计算机专业大学生必备知识...

C/C文件C/C程序文件包括 .h .c .hpp .cpp&#xff0c;其中源文件(.c .cpp)是基本的编译单元&#xff0c;头文件(.h .hpp)不会被编译器编译。C/C项目构建(build)过程&#xff0c;分为以下几个步骤 预处理 → 编译 → 链接。预编译预编译的过程可以理解为编译器(实际上是预处理器…

python函数命名空间_Python中的函数 ​命名空间 作用域和局部函数 匿名(lambda)函数...

函数 是 Python中最重要的代码组织和复用手段函数用def关键字声明&#xff0c;return关键字返回&#xff1a; def function(x, y, z1.5): if z > 1: return z * (x y) else: return z / (x y) ##可以拥有多条return语句&#xff0c;如果到达函数末尾时没有遇到任何一条ret…

二建施工管理思维导图_备考二建不丢分?二建思维导图全程指导,知识点记忆快、不分散...

关键字&#xff1a;二建 一建 建造师考试 建造师证书 建筑 工程 建筑项目 法规 管理 市政 公路目前&#xff0c;距离2020年二级建造师考试的时间越来越近&#xff0c;很多准备参加二建考试的朋友也开始紧张起来了。二建考试分为公共科和专业科&#xff0c;公共科为法规和管理&a…

机器学习线性回归算法实验报告_吴恩达机器学习系列4:线性回归的梯度下降算法...

之前我们已经学过了线性回归、代价函数和梯度下降&#xff0c;但是他们就像一个人的胳膊和腿&#xff0c;只有组合在一起才会成为一个「完整的人」&#xff0c;这个「完整的人」就是一个机器学习算法&#xff0c;让我们一起来学习第一个机器学习算法吧。这个机器学习算法叫做线…

树莓派python3_【树莓派】给ubuntu18安装python3.7

准备工作 安装工具 sudo apt update sudo apt upgrade sudo apt install gcc sudo apt install g sudo apt-get install libffi-dev sudo apt install build-essential checkinstall sudo apt install libreadline-gplv2-dev libncursesw5-dev libssl-dev libsqlite3-dev tk-de…

c++mfc编写实验_零基础学Windows窗口图形界面编程(不用MFC),菜鸟学完变身高手,敢与专业媲美...

又一门新的计算机课上线啦&#xff01;什么课&#xff1f;看下面视频(以下视频是本课第一讲)用什么语言我们声明一下(这个在我们课上也跟同学强调)&#xff1a;不要为用什么语言掐架&#xff01;那是初学者的行为&#xff0c;为高手所不为。第二&#xff0c;我们这门课讲的不是…

超级计算机阿波罗11,Apollo 8000推进超算科学发展

Apollo 8000推进超算科学发展超级计算技术将理论转移到模拟数字环境和计算机分析&#xff0c;一直被应用于加快科学和工程领域的突破。最新的高性能计算技术带来的创新让企业客户也能够访问这些程序和应用、强化研发能力并获得竞争优势。Apollo 8000推进超算科学发展作为散热媒…

找不到r低版本_R的多进程使用与改进

R的多进程使用与改进在R中需要使用多进程时&#xff0c;常见方案是使用foreach和doParallel的组合。foreachforeach包中最重要的是foreach函数&#xff0c;该函数创建一个foreach对象&#xff0c;随后串行或并行的执行表达式。library(foreach)?foreachout:foreach( ..., .c…

html让图片移动到一定位置_百度移动搜索优化指南2.0

百度移动搜索优化指南 2.0前期准备工作【域名】与 PC 网站一样&#xff0c;域名是用户对一个网站的第一印象。一个好的移动域名&#xff0c;不仅容易记忆、易于输入&#xff0c;还能方便用户向其他人推荐。域名应尽量简短易懂&#xff0c;越短的域名记忆成本越低&#xff0c;越…

32位mysql安装包_关于Mysql的安装

在安装之前&#xff0c;为保证能够安装成功&#xff0c;请您尝试以下操作&#xff1a;1、卸载原有mysql;2、搜索C:盘中是否有残余的mysql文件&#xff0c;主要是log文件&#xff0c;全部删除&#xff1b;3、为保证能够正常连接服务器&#xff0c;尽量关闭电脑防火墙&#xff1b…

山东初二计算机会考,2017山东莱芜初二会考科目时间安排:6月14日

考试科目初中学业考试科目、分值和考试时长分别为&#xff1a;语文、数学、英语三科满分均为120分&#xff0c;考试时长均为120分钟&#xff0c;均按原始得分计入总分;思想品德、化学、历史、地理、生物满分均为100分&#xff0c;考试时长均为90分钟&#xff0c;均按考试分数50…

安装 ubuntu18_Ubuntu18.04安装docker及nvidia docker

Ubuntu18.04安装docker及nvidia docker 2之前的时候记得安装完docker之后还需要安装单独的nvidia docker 2&#xff0c;现在的话只需要安装nvidia container toolkit即可1、docker安装官网上又详细的介绍Install Docker Engine on Ubuntu​docs.docker.com或者sudo apt-get upd…

google账号解除游戏绑定_成长守护平台解除实名认证 公众号解绑操作流程

微信成长守护平台是一款培养孩子健康游戏习惯的游戏管控工具&#xff0c;那么微信成长守护平台怎么解除王者荣耀防沉迷呢&#xff1f;接下来小编就给大家带来了解除实名认证方法介绍&#xff0c;一起来看看吧&#xff01;先简单了解一下游戏成长守护平台&#xff0c;它的运作方…

混凝土地坪机器人_地面整平机器人:精准又高效,轻松摆“平”混凝土

随着国内经济不断发展&#xff0c;工业厂房、大型广场、体育场、停车场等大面积场地的建设需求越来越多。这类场地大多使用混凝土现场浇筑地基&#xff0c;然后再覆盖地砖或地坪漆&#xff0c;因此对基础层的平整度提出了很高的要求。传统的混凝土地坪施工方法是人工找平&#…

大学计算机成绩统计表怎么做,wps怎么制作成绩表 wps设计成绩统计表的步骤方法...

当我们使用wps统计数据时&#xff0c;如果我们需要制作一个统计成绩的表格&#xff0c;可是怎么制作成绩表吗?其实方法非常的简单&#xff0c;下面教程之家网为大家分享wps设计成绩统计表的步骤方法&#xff0c;不会制作的朋友可以参考下面的步骤方法自己制作即可。wps怎么制作…

chackbox的值 php获取_最详细最全的PHP面试题(附答案)

这篇文章介绍的内容是关于最全最详细的PHP面试题(带有答案)&#xff0c;有着一定的参考价值&#xff0c;现在分享给大家&#xff0c;有需要的朋友可以参考一下相关推荐&#xff1a;程序猿的生活&#xff1a;面试12家公司&#xff0c;收获9个offer&#xff0c;2020年PHP 面试问题…

python适合安装什么开发环境_python开发环境安装

1、首先安装python-3.4.2.msi&#xff0c;此为python主程序&#xff0c;双击安装&#xff0c;根据自身的情况做选择&#xff0c;也可以使用默认设置&#xff0c;一路next也没什么问题。 2、设置环境变量>编辑Path&#xff0c;在最后加上你的python安装路径。我的安装路径是&…

服务器iis的作用,IIS是什么 IIS服务组件有什么作用

在网上或者一些技术社区我们经常会遇到一些讨论IIS问题文章。那么究竟IIS是什么&#xff1f;有什么用呢&#xff1f;不少电脑爱好者对IIS都充满兴趣&#xff0c;以下脚本之家小编为大家简单介绍下。IIS是什么?IIS是Internet Information Services英文全称的缩写&#xff0c;是…