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 相关文章推荐
Javascript客户端将指定区域导出到Word、Excel的代码
Oct 22 Javascript
jquery随意添加移除html的实现代码
Jun 21 Javascript
DIV+CSS+JS不间断横向滚动实现代码
Mar 19 Javascript
JQuery 文本框回车跳到下一个文本框示例代码
Aug 30 Javascript
node.js中的fs.read方法使用说明
Dec 17 Javascript
jQuery判断对象是否存在的方法
Feb 05 Javascript
jquery实现百叶窗效果
Jan 12 Javascript
微信小程序 setData使用方法及常用错误解决办法
May 11 Javascript
全选复选框JavaScript编写小结(附代码)
Aug 16 Javascript
深入理解ES7的async/await的用法
Sep 09 Javascript
JavaScript中的高级函数
Jan 04 Javascript
Element-UI 使用el-row 分栏布局的教程
Oct 26 Javascript
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 注释规范
2012/03/29 PHP
php自定义的格式化时间示例代码
2013/12/05 PHP
彻底删除thinkphp3.1案例blog标签的方法
2014/12/05 PHP
Linux操作系统安装LAMP环境
2015/06/26 PHP
总结PHP中初始化空数组的最佳方法
2019/02/13 PHP
tp5框架前台无限极导航菜单类实现方法分析
2020/03/29 PHP
让人印象深刻的10个jQuery手风琴效果应用
2012/05/08 Javascript
Firefox中使用outerHTML的2种解决方法
2014/06/07 Javascript
jQuery实现带延迟的二级tab切换下拉列表效果
2015/09/01 Javascript
Bootstrap显示与隐藏简单实现代码
2017/03/06 Javascript
浅谈angular4 ng-content 中隐藏的内容
2017/08/18 Javascript
javascript移动端 电子书 翻页效果实现代码
2019/09/07 Javascript
Bootstrap table 实现树形表格联动选中联动取消功能
2019/09/30 Javascript
JS实现秒杀倒计时特效
2020/01/02 Javascript
vue中使用WX-JSSDK的两种方法(推荐)
2020/01/18 Javascript
js最全的数组的降维5种办法(小结)
2020/04/28 Javascript
JavaScript中的this妙用实例分析
2020/05/09 Javascript
Array.filter中如何正确使用Async
2020/11/04 Javascript
对vue生命周期的深入理解
2020/12/03 Vue.js
Node快速切换版本、版本回退(降级)、版本更新(升级)
2021/01/07 Javascript
NestJs使用Mongoose对MongoDB操作的方法
2021/02/22 Javascript
Python多进程机制实例详解
2015/07/02 Python
Python爬虫包BeautifulSoup学习实例(五)
2018/06/17 Python
numpy 计算两个数组重复程度的方法
2018/11/07 Python
pytorch中交叉熵损失(nn.CrossEntropyLoss())的计算过程详解
2020/01/02 Python
Django实现将views.py中的数据传递到前端html页面,并展示
2020/03/16 Python
python statsmodel的使用
2020/12/21 Python
美国知名玩具品牌:Melissa & Doug
2016/08/16 全球购物
美国鲍勃商店:Bob’s Stores
2018/07/22 全球购物
英国领先的高级美容和在线皮肤诊所:Face the Future
2020/06/17 全球购物
售后主管岗位职责
2013/12/08 职场文书
活动总结格式
2014/08/30 职场文书
爱护公物演讲稿
2014/09/09 职场文书
经营目标责任书
2015/05/08 职场文书
股东合作协议书模板2篇
2019/11/05 职场文书
如何解决flex文本溢出问题小结
2022/07/15 HTML / CSS