IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> Vue源码之Vue和VueComponent的关系 -> 正文阅读

[JavaScript知识库]Vue源码之Vue和VueComponent的关系

在这个专栏前面的好几篇文章中都有提到过Vue实例对象和组件VueComponent实例对象,说它们两个是非常类似的两个对象,但是并没有进一步探讨它们具体的一些实现。

今天想花点时间把这方面的知识给补充补充。

1. Vue对象的使用例子

我们接触Vue这个类的使用,一般都是在main.js中开始的。我们的第一个,通常也是唯一的一个Vue实例对象就是在这里创建的,而整个Vue应用的生命周期也是从这里开始的。

import Vue from "vue";
import App from "./App.vue";

Vue.config.productionTip = false;
const vm = new Vue({
  render: (h) => h(App),
  components: { App },
  data: {},
}).$mount("#app");

这里new Vue调用的就是Vue构造函数。里面传入的就是我们此前分析过的渲染函数,data选项,应用的组件等选项信息。

2. Vue构建函数

Vue构造函数在源码路径的core/instance/index.js这个文件,要分析它的构造函数,我们这最好是整个文件一起看,因为很多信息是隐藏在构造函数之外的。

function Vue(options) {
  if (process.env.NODE_ENV !== "production" && !(this instanceof Vue)) {
    warn("Vue is a constructor and should be called with the `new` keyword");
  }
  this._init(options);
}

initMixin(Vue);
stateMixin(Vue);
eventsMixin(Vue);
lifecycleMixin(Vue);
renderMixin(Vue);

export default Vue;

构造函数很简单,就是调用了Vue原型的_init方法,并把我们前面传入的选项参数传递进去。

但是,这个_init是在什么时候加入到Vue的prototype原型对象中的呢?其实就是系统在加载这个index.js的文件的时候,而不是特别的某个调用。

3. Vue.prototype原型初始化

一旦我们的项目引入了vue.js,然后就会对自动执行这个js的文件,期间引用到这个文件,所以就会自动的执行initMixin这些函数。

而这一系列的方法调用其实就是设置Vue的prototype上的原型方法,比如上面提到的_init方法:

export function initMixin(Vue: Class<Component>) {
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this;
    // a uid
    vm._uid = uid++;

    ...
    
    vm._self = vm;
    initLifecycle(vm);
    initEvents(vm);
    initRender(vm);
    callHook(vm, "beforeCreate");
    initInjections(vm); // resolve injections before data/props
    initState(vm);
    initProvide(vm); // resolve provide after data/props
    callHook(vm, "created");

    ...

    if (vm.$options.el) {
      // debugger;
      vm.$mount(vm.$options.el);
    }
  };
}

这里就不一一列举和分析prototype上的那一堆方法了,我们今后用到哪个就会分析哪个,这里先从高层建瓴的角度知道prototype上的那些属性和方法是怎么来的就行了。
在这里插入图片描述
也正是有了这一系列函数所实现的Vue原型的初始化,才让我们能够很方便的通过this.$nextTick这些原型方法来做事情。

那么我们看了Vue示例对象的初始化的大概流程,下面我们看下组件的初始化流程大概是怎么样的。

4. Vue组件的自动创建

我们通常写组件的时候,都只是在一个vue单文件组件文件中直接编写。然后再用到的地方将其import进来,然后在components选项中注册该组件,最后在模板中通过组件名称来作为标签进行使用就完了,期间并没有见到有显式的组件创建过程。

事实上,Vue组件的创建过程是框架默默帮我们做了。

我们知道页面的渲染在vue中要经历两个过程

  • 首先是调用render函数生成虚拟DOM
  • 然后调用update方法根据新的虚拟DOM和老的虚拟DOM做diff,将diff的内容上树即更新页面。

详情可参考此前的两篇文章

当然,第一次初始化的时候是没有老的虚拟DOM的,比如前面分析的Vue实例的初始化,在碰到这个标签对应的虚拟DOM的时候,如果发现是一个没有别实例化的组件,就会直接调用VueComponent构造函数来创建。

5. VueComponent和extend方法的关系

其实,VueComponent构造函数同时也是是一个高阶函数,因为它是通过调用Vue.extend方法得到的。

Vue.extend = function (extendOptions: Object): Function {
  extendOptions = extendOptions || {};
  const Super = this;
  ...

  const Sub = function VueComponent(options) {
    this._init(options);
  };
  ...
  return Sub;
};

下面我们先看一个通过extend方法获取到VueComponent构造函数创建一个组件的例子。

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="vue.js"></script>
    <style>
      button {
        font-size: 2rem;
        padding: 1rem;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <hello-world></hello-world>
    </div>

    <script>
      const HelloWorld = Vue.extend({
        template: `
        <div>
          <div>Hello World</div>
        </div>
        `,
        data() {
          return {};
        },
      });
      const vm = new Vue({
        el: "#app",
        components: {
          HelloWorld,
        },
      });
    </script>
  </body>
</html>

例子中我们通过Vue.extend方法的调用,得到的其实就是VueComponent的构造函数。通过下面的调试输出也能说明这一点

在这里插入图片描述

6. VueComponent和Vue的一个重要关系

这里我们再次看下Vue的extend方法

Vue.extend = function (extendOptions: Object): Function {
  extendOptions = extendOptions || {};
  const Super = this;

  ...

  const Sub = function VueComponent(options) {
    debugger;
    this._init(options);
  };
  Sub.prototype = Object.create(Super.prototype);
  Sub.prototype.constructor = Sub;
  
  ...

  return Sub;
};

因为这里extend方法是Vue的成员方法,所以里面的this指向是Vue, 也就是这里的Super是Vue。

跟着就是把Vue的构造函数赋予给Sub。

紧跟着就是我们这里的一个关键点,首先,Object.create(a)的作用是创建一个对象,该对象的__proto__指向参数a。

所以这一行的意义就是,创建一个对象,将其__proto__指向Super.prototype,即Vue.prototype,然后将这个对象赋值给Sub,即VueComponent的prototype。

用程序员的语言来说就是: VueComponent.prototype.proto === Vue.prototype。可别忘了,原型对象毕竟是个对象,也是有隐式原型的。只是平常我们见到的往往会指向Object,而这里,我们将VueComponent的原型对象的隐式原型指向了Vue的原型对象。

最终的结果就是,所有Vue的原型对象prototype上有的属性和方法,在VueComponent中顺着原型链去找都能找到。而我们上面分析的Vue可以看到,Vue本身没有定义多少方法和属性,大部分的方法如$nextTick,$watch等都是定义在原型对象prototype上的。

我是@天地会珠海分舵,「青葱日历」和「三日清单」作者。能力一般,水平有限,觉得我说的还有那么点道理的不妨点个赞关注下!

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 11:46:33  更:2022-10-31 11:46:37 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 20:15:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码