vue $mount 和 el的区别说明
作者:Daniel丶云焕 发布时间:2024-04-28 09:20:24
两者在使用效果上没有任何区别,都是为了将实例化后的vue挂载到指定的dom元素中。
如果在实例化vue的时候指定el,则该vue将会渲染在此el对应的dom中,反之,若没有指定el,则vue实例会处于一种“未挂载”的状态,此时可以通过$mount来手动执行挂载。
注:如果$mount没有提供参数,模板将被渲染为文档之外的的元素,并且你必须使用原生DOM API把它插入文档中。
例如:
var MyComponent = Vue.extend({
template: '<div>Hello!</div>'
})
// 创建并挂载到 #app (会替换 #app)
new MyComponent().$mount('#app')
// 同上
new MyComponent({ el: '#app' })
// 或者,在文档之外渲染并且随后挂载
var component = new MyComponent().$mount()
document.getElementById('app').appendChild(component.$el)
补充知识:Vue 实例挂载方法($mount)的实现
在 Vue 的 _init 方法中已经回调了beforeCreate 和created这两个生命周期钩子,在此之后就进行了实例的挂载
if (vm.$options.el) { // 挂载实例
vm.$mount(vm.$options.el);
}
在挂载函数中,将要进行 beforeMount 和 mounted 的回调。
在不同的平台下对于 $mount 函数的实现是有差异的,下面考虑 web 平台的 runtime-with-compiler 版本 , 其在web平台下的定义如下(src/platforms/web/runtime/index.js)
import { mountComponent } from 'core/instance/lifecycle';
Vue.prototype.$mount = function(
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined;
return mountComponent(this, el, hydrating);
};
在$mount函数的参数中,第一个为我们属性的el, 第二个参数为服务端渲染有关,在patch函数中用到,这里可以忽略。
但是在调用这个$mount函数的时候,首先调用的是不同版本下的$mount函数,然后在该函数中再调用相应平台的$mount函数,如下在 runtime-with-compiler 版本中$mount函数如下(src/platforms/web/entry-runtime-with-compiler.js)
import Vue from './runtime/index';
const mount = Vue.prototype.$mount; // 缓存 上面的 $mount 方法
Vue.prototype.$mount = function(
el?: string | Element,
hydrating?: boolean
): Component {
el = el && query(el);
// 不能挂载到 body 和 html 上
if (el === document.body || el === document.documentElement) {
return this;
}
const options = this.$options;
if (!options.render) { // 如果没有 render 函数
// ... 将 render 函数添加到 options 上
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;
// ...
}
return mount.call(this, el, hydrating);
};
可知该函数主要干了三件事
1、由于挂载之后会替换被挂载的对象,所以限制不能挂载到 body 和 html 上
2、如果当前Vue实例没有 render() 函数(写template等),则通过编译等手段,将render函数添加到 options 上
3、调用在代码开头我们先缓存的$mount方法,该方法就是web平台下的方法。
在web平台下的$mount方法里面主要就是调用了mountComponent() 方法,接下来我们的核心就是该方法了
在'core/instance/lifecycle.js 文件中我们找到了该方法的定义,删掉一些非重点代码后如下
export function mountComponent(
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
vm.$el = el;
if (!vm.$options.render) {
// 不是重点,该处主要是用来对没有 render 函数下的一些错误提示
}
callHook(vm, 'beforeMount'); // 回调 beforeMount , 开始准备挂载实例
// 声明 更新组件 的函数 (源代码中有关performance配置不是重点,故省略)
const updateComponent = updateComponent = () => {
vm._update(vm._render(), hydrating);
};
// new 一个 Watcher [isRenderWatcher]
new Watcher(vm, updateComponent, noop, {
before() {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate');
}
},
}, true /* isRenderWatcher */);
hydrating = false;
// Vue 的根实例的 mounted 回调在这里执行
if (vm.$vnode == null) {
vm._isMounted = true;
callHook(vm, 'mounted');
}
return vm;
}
上面的代码中主要干了如下三件事
1、回调 beforeMount
2、生成 updateComponent 方法,该方法将 vnode 渲染为真实的DOM
3、new 一个 Watcher ,并在该 Watcher在调用updateComponent方法
4、回调 mounted
对于 updateComponent方法较为复杂,其内部主要调用_update()将 vnode渲染为浏览器上显示的真实DOM
我们考虑如下两个问题
1. Watcher 中如何调用 updateComponent方法
Watcher 函数的构造函数接受如下的参数
constructor(
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
)
在上面的代码中,updateComponent()方法作为第二个参数传递过来,即构造函数中的expOrFn
往下看会看到
if (typeof expOrFn === 'function') {
this.getter = expOrFn;
}
也就是说updateComponent()方法被设置为了getter()方法
看到构造函数的最后
this.value = this.lazy
? undefined
: this.get();
其中 lazy 属性的值在前面被设置为了 false
this.lazy = !!options.lazy; // 我们 options 中没有 lazy 属性
这也就是说,咋i构造函数的末尾会调用this.get(),而在this.get()中
const vm = this.vm;
try {
value = this.getter.call(vm, vm);
}
我们看到调用了getter()方法,也就是调用了updateComponent()方法。
2. 为什么根实例的$vnode为空
在initRender()函数中有如下代码
const parentVnode = vm.$vnode = options._parentVnode;
也就是说 当前实际的 $vnode 值为其父节点的vnode值
而根实例没有父节点,故其$vnode值就为空了,所以会执行
if (vm.$vnode == null) {
vm._isMounted = true;
callHook(vm, 'mounted');
}
那么子节点的mounted回调是在那里执行的呢?
在 path()(core/vdom/patch.js) 函数中有如下代码
function invokeInsertHook(vnode, queue, initial) {
if (isTrue(initial) && isDef(vnode.parent)) {
vnode.parent.data.pendingInsert = queue;
}
else {
for (let i = 0; i < queue.length; ++i) {
queue[i].data.hook.insert(queue[i]); // 这里
}
}
}
在循环queue的时候,调用了 insert()方法,该方法为 VNodeHooks,其在componentVNodeHooks(core/vdom/create-component.js)中声明,代码如下
const componentVNodeHooks = {
insert(vnode: MountedComponentVNode) {
const { context, componentInstance } = vnode;
if (!componentInstance._isMounted) {
componentInstance._isMounted = true;
callHook(componentInstance, 'mounted'); // 这里
}
if (vnode.data.keepAlive) {
if (context._isMounted) {
queueActivatedComponent(componentInstance);
}
else {
activateChildComponent(componentInstance, true /* direct */);
}
}
},
}
由于 path() 方法在 _update()函数中调用,这部不再重点说明。
下节我们将来说说render() 和 _update() 方法的实现
来源:https://blog.csdn.net/c2311156c/article/details/80415633


猜你喜欢
- 需要安装pywin32模块,pip install pywin32##pip install pywin32import win32api,
- Json简介JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它基于JavaScript(Sta
- 如何导入SQL数据库如何将现成的数据库导入到MySQL中?有两种方式:通过终端命令行语句导入:mysql> source SQL文件的
- 1. import_table介绍上期技术分享我们介绍了MySQL Load Data的4种常用的方法将文本数据导入到MySQL,这一期我们
- selenium 介绍selenium 是一个 web 的自动化测试工具,不少学习功能自动化的同学开始首选 selenium ,因为它相比
- 由于电脑上安装了多个版本的pip,以及不同的pip对应不同的python,因此有时候使用pip install安装某个包时,可能会没有安装在
- JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换
- 本文用 Python 实现 PS 滤镜中的 USM 锐化效果import matplotlib.pyplot as pltfrom skim
- 演示技术栈js实战我们也写过很多了,其中每次几乎都用到画布,大家知道它的重要性了吧。今天依旧用到它了。不过我们讲过它的用法就不多说了。 这次
- c3p0是什么c3p0的出现,是为了大大提高应用程序和数据库之间访问效率的。它的特性:编码的简单易用连接的复用连接的管理说到c3p0,不得不
- 如何实现刷新当前页面呢?借助js你将无所不能。1,reload 方法,该方法强迫浏览器刷新当前页面。语法:location.reload([
- 概要贝叶斯网络是一种基于概率的图模型,可用于建立变量之间的条件概率关系。在拼写检查器中,贝叶斯网络可以通过建立一个隐含状态、错误观察值和正确
- 本文实例讲述了JS实现字符串转驼峰格式的方法。分享给大家供大家参考,具体如下:实现效果如:border-bottom-color =>
- Matlab绘图介绍强大的绘图功能是Matlab的特点之一,Matlab提供了一系列的绘图函数,用户不需要过多的考虑绘图的细节,只需要给出一
- 项目(nodejs)中需要一次性插入多笔数据到数据库,数据库是mysql的,由于循环插入的性能太差,就像使用批量插入的方法提高数据的插入性能
- PHP count() 函数实例计算 car 节点的子节点个数:<?php $xml=<<<XML<cars&
- Django auth 应用模块在设计开发任何一个站点的时候都需要有“用户”的概念,从用户的注册、登
- JupyterLab 是 Jupyter 主打的最新数据科学生产工具,某种意义上,它的出现是为了取代Jupyter Notebook。它作为
- 百度作业帮提问:python if not in 多条件 判断怎么写s = ['1','2'] 判断条件st
- 我们平日办公时用得最多的软件是Execl、Word或WPS Office等,你的计算机中一定储存着大量的XLS、DOC、WPS文件吧!网页制