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

标签:vue,$mount,el
0
投稿

猜你喜欢

  • Study jQuery in a Simplified Way

    2010-01-30 12:55:00
  • SqlServer 实用操作小技巧集合第1/2页

    2024-01-28 13:19:33
  • 扩展数据库系统选项实现更高的可扩展性

    2009-01-06 11:14:00
  • MySQL的命令行提示符及其表达的意思

    2008-03-17 13:34:00
  • Python使用LRU缓存策略进行缓存的方法步骤

    2023-09-03 05:00:06
  • 弄清Pytorch显存的分配机制

    2023-05-22 22:12:44
  • Oracle数据库快照的使用

    2010-07-28 13:32:00
  • python实现对服务器脚本敏感信息的加密解密功能

    2022-11-07 08:35:11
  • WAP2.0网页设计中的交互细节

    2010-08-12 20:50:00
  • 10个杀手级应用的Python自动化脚本

    2023-10-06 14:01:53
  • Python基于wordcloud及jieba实现中国地图词云图

    2021-03-31 15:08:53
  • Python使用Socket(Https)Post登录百度的实现代码

    2023-09-19 21:25:34
  • python破解bilibili滑动验证码登录功能

    2023-04-10 06:03:05
  • Vue 2.0 服务端渲染入门介绍

    2023-07-02 16:58:52
  • 解决golang json解析出现值为空的问题

    2024-04-27 15:27:58
  • python处理两种分隔符的数据集方法

    2023-06-17 11:24:54
  • VScode查看python f.write()的文件乱码问题及解决方法

    2023-01-25 19:02:10
  • python实现图片处理和特征提取详解

    2023-10-04 02:28:16
  • Node.js对MongoDB数据库实现模糊查询的方法

    2024-01-23 14:31:02
  • Python 读取WAV音频文件 画频谱的实例

    2021-11-27 02:23:43
  • asp之家 网络编程 m.aspxhome.com