前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Vue 2.0源码分析-实例挂载的实现

Vue 2.0源码分析-实例挂载的实现

作者头像
越陌度阡
发布于 2023-11-24 02:20:39
发布于 2023-11-24 02:20:39
30700
代码可运行
举报
运行总次数:0
代码可运行

Vue 中我们是通过 mount 实例方法去挂载 vm 的,mount 方法在多个文件中都有定义,如 src/platform/web/entry-runtime-with-compiler.js、src/platform/web/runtime/index.js、src/platform/weex/runtime/index.js。因为 mount 这个方法的实现是和平台、构建方式都相关的。接下来我们重点分析带 compiler 版本的 mount 实现,因为抛开 webpack 的 vue-loader,我们在纯前端浏览器环境分析 Vue 的工作原理,有助于我们对原理理解的深入。

compiler 版本的 $mount 实现非常有意思,先来看一下 src/platform/web/entry-runtime-with-compiler.js 文件中定义:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
    el?: string | Element,
    hydrating?: boolean
): Component {
    el = el && query(el)

    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
    if (!options.render) {
        let template = options.template
        if (template) {
            if (typeof template === 'string') {
                if (template.charAt(0) === '#') {
                    template = idToTemplate(template)
                    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) {

            if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
                mark('compile')
            }

            const { render, staticRenderFns } = compileToFunctions(template, {
                shouldDecodeNewlines,
                shouldDecodeNewlinesForHref,
                delimiters: options.delimiters,
                comments: options.comments
            }, this)
            options.render = render
            options.staticRenderFns = staticRenderFns

            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 方法,再重新定义该方法,我们先来分析这段代码。首先,它对 el 做了限制,Vue 不能挂载在 body、html 这样的根节点上。接下来的是很关键的逻辑 , 如果没有定义 render 方法,则会把 el 或者 template 字符串转换成 render 方法。这里我们要牢记,在 Vue 2.0 版本中,所有 Vue 的组件的渲染最终都需要 render 方法,无论我们是用单文件 .vue 方式开发组件,还是写了 el 或者 template 属性,最终都会转换成 render 方法,那么这个过程是 Vue 的一个“在线编译”的过程,它是调用 compileToFunctions 方法实现的,编译过程我们之后会介绍。最后,调用原先原型上的 mount 方法挂载。

原先原型上的 $mount 方法在 src/platform/web/runtime/index.js 中定义,之所以这么设计完全是为了复用,因为它是可以被 runtime only 版本的 Vue 直接使用的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Vue.prototype.$mount = function (
	el?: string | Element,
	hydrating?: boolean
): Component {
	el = el && inBrowser ? query(el) : undefined
	return mountComponent(this, el, hydrating)
}

$mount 方法支持传入 2 个参数,第一个是 el,它表示挂载的元素,可以是字符串,也可以是 DOM 对象,如果是字符串在浏览器环境下会调用 query 方法转换成 DOM 对象的。第二个参数是和服务端渲染相关,在浏览器环境下我们不需要传第二个参数。

$mount 方法实际上会去调用 mountComponent 方法,这个方法定义在 src/core/instance/lifecycle.js 文件中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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') {


            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


    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)
        }
    }


    new Watcher(vm, updateComponent, noop, {
        before() {
            if (vm._isMounted) {
                callHook(vm, 'beforeUpdate')
            }
        }
    }, true )

    hydrating = false


    if (vm.$vnode == null) {
        vm._isMounted = true
        callHook(vm, 'mounted')
    }
    return vm
}

从上面的代码可以看到,mountComponent 核心就是先实例化一个渲染Watcher,在它的回调函数中会调用 updateComponent 方法,在此方法中调用 vm._render 方法先生成虚拟 Node,最终调用 vm._update 更新 DOM。

Watcher 在这里起到两个作用,一个是初始化的时候会执行回调函数,另一个是当 vm 实例中的监测的数据发生变化的时候执行回调函数,这块儿我们会在之后的章节中介绍。

函数最后判断为根节点的时候设置 vm._isMounted 为 true, 表示这个实例已经挂载了,同时执行 mounted 钩子函数。 这里注意 vm.$vnode 表示 Vue 实例的父虚拟 Node,所以它为 Null 则表示当前是根 Vue 的实例。

总结

mountComponent 方法的逻辑也是非常清晰的,它会完成整个渲染工作,接下来我们要重点分析其中的细节,也就是最核心的 2 个方法:vm._render 和 vm._update。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-11-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Nginx启用具有一般的TCP代理和负载均衡的stream模块
一直以来,nginx并不支持TCP协议,一些基于TCP的业务必须依靠别的高可用负载软件来实现。但在nginx 1.9.0以后,意识到了此问题,终于得到了重大的重写。 2015-04-28 nginx更新说明  nginx-1.9.0 mainline version has been released, with the stream module for generic TCP proxying and load balancing. nginx-1.9.0主线版本已经发布,增加了具有一般的TCP
天策
2018/06/22
1.3K0
Nginx发布1.9.0版本,新增支持TCP代理和负载均衡的stream模块
昨天在公司微信群,CTO 分享了这个消息,对运维来说以后基于 TCP 协议的后端业务的高可用又多了一个新的选择,实在是棒极了! 一直以来,Nginx 并不支持 tcp 协议,所以后台的一些基于 TCP
张戈
2018/03/23
1.5K0
Nginx发布1.9.0版本,新增支持TCP代理和负载均衡的stream模块
Nginx使用反向代理转发SSH服务
官方文档指出,在1.9.0版本后可以使用stream模块,但此模块不是默认编译进去的,所以需要我们在编译安装nginx的时候加上它--with-stream。
行 者
2023/10/20
2.7K0
第六章·Nginx四层负载均衡
-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。
DriverZeng
2022/09/26
9840
第六章·Nginx四层负载均衡
使用Nginx的stream模块实现MySQL反向代理与RabbitMQ负载均衡
一只牛博
2025/05/31
1000
Nginx禁用https证书实现转发
最近有一个需求,如何禁用ssl证书,只用于Nginx 443端口转发流量?https服务器A和B 有 https 服务并提供两个 IP 以实现高可用。 例如https服务器 A[ip1:443] 和 B[ip2:443] 被路由到Nginx代理服务器上。 Nginx代理服务器没有 ssl_certificate 和 ssl_certificate_key使用 Nginx 代理模块将请求代理到实际的https服务器上。如何在没有SSL验证的情况下将443端口流量简单转发到后端。
王先森sec
2023/10/17
9140
Nginx禁用https证书实现转发
CentOS7 nginx安装并负载mysql
#1.安装依赖项目:PCRE 链接: https://pan.baidu.com/s/1JA-Tifch8ftM32znQO1csQ 提取码: svgw ./configure make && make install #2.安装依赖项目:libtool 链接: https://pan.baidu.com/s/18UohgCRggfhlwcAoVOlkvw 提取码: dnd6 ./configure make && make install #3.安装nginx wget http://nginx.o
肖哥哥
2020/08/06
5850
Nginx基于TCP/UDP端口的四层负载均衡(stream模块)配置梳理
通过我们会用Nginx的upstream做基于http/https端口的7层负载均衡,由于Nginx老版本不支持tcp协议,所以基于tcp/udp端口的四层负载均衡一般用LVS或Haproxy来做。至于4层负载均衡和7层负载均衡的区别,可以参考:http://www.cnblogs.com/kevingrace/p/6137881.html。然而Nginx从1.9.0版本开始,新增加了一个stream模块,用来实现四层协议的转发、代理或者负载均衡等,鉴于Nginx在7层负载均衡和web service上的成功,和Nginx良好的框架,stream模块前景一片光明。官方文档:http://nginx.org/en/docs/stream/ngx_stream_core_module.html
洗尽了浮华
2018/09/28
9.2K1
Nginx基于TCP/UDP端口的四层负载均衡(stream模块)配置梳理
nginx平滑添加stream模块
1、操作背景 操作系统版本:CentOS Linux release 7.4.1708 (Core) nginx版本:1.13.4 nginx从1.9.0版本开始,新增了ngx_stream_core_module模块,使nginx支持四层负载均衡。默认编译的时候该模块并未编译进去,需要编译的时候添加--with-stream,使其支持stream代理。 2、nginx编译添加stream模块 2.1、查看原nginx编译参数 [root@test-server sbin]# nginx -V nginx
随心助手
2019/10/15
11K0
CentOS7下使用nginx实现TCP和UDP代理
nginx从1.9.0版本开始,新增了ngx_stream_core_module模块,使nginx支持四层负载均衡,实现TCP和UDP代理。默认编译的时候该模块并未编译进去,需要编译的时候添加--with-stream,使其支持stream代理
yuanfan2012
2020/05/18
10.1K0
CentOS7下使用nginx实现TCP和UDP代理
【Nginx】如何使用Nginx实现MySQL数据库的负载均衡?看完我懂了!!
作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。自开源半年多以来,已成功为十几家中小型企业提供了精准定时调度方案,经受住了生产环境的考验。为使更多童鞋受益,现给出开源框架地址:
冰河
2020/10/29
4.7K1
Nginx代理sftp配置
在192.168.10.185客户端服务器通过nginx代理服务器登陆sftp_server服务器:
范一刀
2021/08/10
6.2K0
Nginx系列之nginx四层反向代理
经过以上简单的配置,nginx -s reload后,nginx即可作为四层反向代理服务器。这段配置的关键在于server配置端,指定监听的端口,proxy_pass来指定上游服务器或上游服务器组。与七层代理的配置区别主要在于http-->stream,没有localctation配置,直接监听端口,将端口的流量直接进行转发。
yxxhero
2022/05/31
8.5K0
05 . Nginx的反向代理与负载均衡
Nginx负载均衡 客户端的访问都被代理到后端的一台服务器上,最终会出现性能瓶颈,从而导致效率降低,前端用户的访问速度急速下降,要解决这个问题就需要添加多台httpd,同时承受大量并发连接,每台服务器接收的并发连接降低了,效率就高了,nginx作为代理,在收到客户端的请求时,会分发到不同的后端服务器,但是nginx收到请求之后会分配到那个服务器之上,有什么规律和需求,就需要用调度算法来分配. upstream语法 # upstream的定义必须在server{..}外定义 upstream <组名
iginkgo18
2020/09/27
8980
Nginx-正反向代理及负载均衡
目录 正/反向代理 代理的方式 Nginx代理服务支持的协议 代理实战 部署web01 部署Lb01 Nginx代理常用参数 添加发往后端服务器的请求头信息 代理到后端的TCP连接、响应、返回等超时时间 proxy_buffer代理缓冲区 配置代理优化文件 负载均衡 负载均衡的架构 负载均衡的实现 连接池 实现步骤 负载均衡的比例 轮询 权重 ip_hash 负载均衡后端状态 down backup max_fails、fail_timeout proxy_next_upstream监控的错误类型 负载均衡
HammerZe
2022/03/25
5110
Nginx-正反向代理及负载均衡
nginx升级1.9,支持tcp代理的stream模块
回忆:坑的来源 外网服务器nginx一直用的好好的,主要用于http代理和反代理,忽然有一天,客户想要外网访问内网的kafka,这样就必须 要支持tcp转发了,好吧,开始操作
零式的天空
2022/03/25
1.6K0
C++搭建集群聊天室(十七):ngnix简介及tcp负载均衡配置
单台服务器受限于硬件资源,其性能是有上限的,当单台服务器不能满足应用场景的并发需求量时,就需要考虑部署多个服务器共同处理客户端的并发请求,此时就需要一台负载均衡器,通过预设的负载算法,指导客户端连接服务器。
看、未来
2021/09/18
4670
Nginx基于TCP/UDP端口的四层负载均衡(stream模块)配置梳理
通过我们会用Nginx的upstream做基于http/https端口的7层负载均衡,由于Nginx老版本不支持tcp协议,所以基于tcp/udp端口的四层负载均衡一般用LVS或Haproxy来做。至于4层负载均衡和7层负载均衡的区别,可以参考:http://www.cnblogs.com/kevingrace/p/6137881.html。然而Nginx从1.9.0版本开始,新增加了一个stream模块,用来实现四层协议的转发、代理或者负载均衡等,鉴于Nginx在7层负载均衡和web service上的成
洗尽了浮华
2018/01/23
29.6K0
kubeadm 搭建K8S 1.18集群--安装kubeadm和api-server
我这里的环境是虚拟机,如果是云环境可以直接用slb的方式,这一步可以跳过。 虚拟机这里使用nginx local proxy
陈不成i
2021/07/01
7170
nginx搭建 负载均衡
配置/usr/local/nginx/conf/nginx.conf的http,配置vhost虚拟主机目录,然后配置vhost下的比如LB.conf文件。
用户1499526
2019/07/15
5070
推荐阅读
相关推荐
Nginx启用具有一般的TCP代理和负载均衡的stream模块
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档