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;分为以下几个步骤 预处理 → 编译 → 链接。预编译预编译的过程可以理解为编译器(实际上是预处理器…

中科大计算机学院博士导师,中科大计算机学院招生导师

这是我2011年参加中科大研究生推免时中科大给发的导师名册自然计算理论及方法教师陈小平 (教授) 陈恩红 (教授) 岳丽华 (教授)联系方式电 话&#xff1a;3606724(O)Email:xpchenhttp://doc.xuehai.net 电 话&#xff1a;3601558(O)Email:chenehhttp://doc.xuehai.net研究方向Ag…

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…

js三元判断html,JS 三元条件运算符

(condition ? ifTrue : ifFalse)条件运算符根据条件的逻辑值返回两个值之一。功能条件(三元)运算符是 JavaScript 仅有的使用三个操作数的运算符。本运算符经常作为 if 语句的简短形式来使用。演示function getFee(isMember) {return (isMember ? "$2.00" : "…

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

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

android默认exported_android:exported 属性详解-阿里云开发者社区

昨天在用360扫描应用漏洞时&#xff0c;扫描结果&#xff0c;出来一个Android:exported属性&#xff0c;其实之前根本不知道这个属性&#xff0c;更不知道这个属性用来干嘛的&#xff0c;详情见下图&#xff1a;因此&#xff0c;查了官方API&#xff0c;学习了一下这个属性!and…

计算机科技新闻,新浪网_科技时代_计算机_新闻报道

分析&#xff1a;你的PC强壮吗&#xff1f;http://www.sina.com.cn 1999年6月29日 16:00综合自从计算机得到广泛应用之后&#xff0c;计算机的速度和功能一直是人们执着的追求对象&#xff0c;二者实际上也决定了一台计算机的价值。然而随着网络时代的来临&#xff0c;病毒开始…

银行界加强计算机病毒管理,银行计算机管理系统维护现状与对策研究(7.12).doc...

银行计算机管理系统维护现状与对策研究(7.12)银行计算机管理系统维护现状与对策研究[摘要]&#xff1a;随着计算机在银行应用范围的不断扩大和应用深度的不断拓展&#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推进超算科学发展作为散热媒…

python数据库教程_在Python中编写数据库模块的教程

在一个Web App中&#xff0c;所有数据&#xff0c;包括用户信息、发布的日志、评论等&#xff0c;都存储在数据库中。在awesome-python-app中&#xff0c;我们选择MySQL作为数据库。 Web App里面有很多地方都要访问数据库。访问数据库需要创建数据库连接、游标对象&#xff0c;…

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

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

药品研发 计算机系统验证,基于验证的药品研发项目申报系统的设计

摘要&#xff1a;目前国家大力推进信息化与工业化深度融合,提倡"互联网"的概念,信息化已经是现今的一个热名词.计算机系统在各行各业中扮演越来越重要的角色,计算机信息化已经不仅用于信息的管理,更融入了我们日常生活的每一个角落,比如购物,支付等.对于处于制造行业…

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

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

ddr5内存上市时间_DDR5内存即将开始量产!DDR4内存史低价重现

本月中旬&#xff0c;JEDEC协会正式公布了DDR5标准&#xff0c;起步4800Mbps&#xff0c;未来可以达到6400Mbps&#xff0c;是DDR4内存的两倍多&#xff0c;最高有望达到DDR5-8400的水平。01、DDR5内存要来了DDR5标准公布之后&#xff0c;全球三大DRAM工厂——三星、SK海力士及…

电大法学本科计算机考试题,2016年电大-电大法学本科计算机网考答案.doc

2016年电大-电大法学本科计算机网考答案"更改默认主页"是在Internet Explorer浏览器的选项卡中进行设置&#xff0c;这个选项卡是____。D、常规"美国信息交换标准代码"的缩写是______。 B、ASCII1994年4月20日我国被国际上正式承认为接入Internet的国家,所…