vue $mount 和 el的区别说明


Posted in Javascript onSeptember 11, 2020

两者在使用效果上没有任何区别,都是为了将实例化后的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() 方法的实现

以上这篇vue $mount 和 el的区别说明就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
为Yahoo! UI Extensions Grid增加内置的可编辑器
Mar 10 Javascript
javascript 广告后加载,加载完页面再加载广告
Nov 25 Javascript
jQuery 插件仿百度搜索框智能提示(带Value值)
Jan 22 Javascript
Javascript Object 对象学习笔记
Dec 17 Javascript
javascript图片预加载实例分析
Jul 16 Javascript
JavaScript中使用sencha gridpanel 编辑单元格、改变单元格颜色
Nov 26 Javascript
JavaScript数据操作_浅谈原始值和引用值的操作本质
Aug 23 Javascript
js时间控件只显示年月
Jan 08 Javascript
JavaScript无阻塞加载和defer、async详解
Feb 26 Javascript
基于复选框demo(分享)
Sep 27 Javascript
基于原生js运动方式关键点的总结(推荐)
Oct 01 Javascript
Vue +WebSocket + WaveSurferJS 实现H5聊天对话交互的实例
Nov 18 Vue.js
JavaScript 判断数据类型的4种方法
Sep 11 #Javascript
jQuery实现日历效果
Sep 11 #jQuery
JS实现密码框效果
Sep 10 #Javascript
JavaScript常用工具函数库汇总
Sep 17 #Javascript
el-form 多层级表单的实现示例
Sep 10 #Javascript
详解React的回调渲染模式
Sep 10 #Javascript
vue-cli3项目配置eslint代码规范的完整步骤
Sep 10 #Javascript
You might like
自定义PHP分页函数
2006/10/09 PHP
基于PHP异步执行的常用方式详解
2013/06/03 PHP
PHP中文编码小技巧
2014/12/25 PHP
php写入、删除与复制文件的方法
2015/06/20 PHP
javascript中的location用法简单介绍
2007/03/07 Javascript
Javascript 读书笔记索引贴
2010/01/11 Javascript
javascript实现的基于金山词霸网络翻译的代码
2010/01/15 Javascript
使用jQuery模板来展现json数据的代码
2010/10/22 Javascript
使用js正则控制input标签只允许输入的值
2013/07/29 Javascript
JavaScript中的Function函数
2015/08/27 Javascript
Bootstrap table的使用方法
2016/11/02 Javascript
利用jQuery解析获取JSON数据
2017/04/08 jQuery
详解webpack性能优化——DLL
2017/10/20 Javascript
node.js中fs文件系统目录操作与文件信息操作
2018/02/24 Javascript
为什么说JavaScript预解释是一种毫无节操的机制详析
2018/11/18 Javascript
如何在vue中使用百度地图添加自定义覆盖物(水波纹)
2020/11/03 Javascript
elementUI同一页面展示多个Dialog的实现
2020/11/19 Javascript
Python模拟三级菜单效果
2017/09/11 Python
python增加矩阵维度的实例讲解
2018/04/04 Python
python 除法保留两位小数点的方法
2018/07/16 Python
Django数据库连接丢失问题的解决方法
2018/12/29 Python
详解pytorch 0.4.0迁移指南
2019/06/16 Python
python3实现斐波那契数列(4种方法)
2019/07/15 Python
pytorch打印网络结构的实例
2019/08/19 Python
Python3.8.2安装包及安装教程图文详解(附安装包)
2020/11/28 Python
html5指南-5.使用web storage存储键值对的数据
2013/01/07 HTML / CSS
洲际酒店集团英国官网:IHG英国
2019/07/10 全球购物
美德好少年事迹材料
2014/01/19 职场文书
献爱心大型公益活动策划方案
2014/09/15 职场文书
2014党员四风对照检查材料思想汇报
2014/09/17 职场文书
大学生见习报告范文
2014/11/03 职场文书
2014年小学数学教师工作总结
2014/12/03 职场文书
小学安全工作总结2015
2015/05/18 职场文书
教师法制教育培训学习心得体会
2016/01/14 职场文书
如何在C++中调用Python
2021/05/21 Python
Win10 和 Win11可以共存吗? win10/11产品生命周期/服务更新介绍
2021/11/21 数码科技