第1期 从底层了解Vue实例的创建

简介: 从底层了解Vue实例的创建

一、概述


我们平时在搭建Vue项目时都是用官网提供的脚手架进行搭建,但对于底层逻辑的东西很少了解,为了帮助大家更深入了解Vue底层原理【快智岛】专门开设了【从源码开始学Vue专栏】和大家一起开启学Vue之旅。

二、渐进式框架


Vue.js 设计的初衷就包括可以被渐进式地采用,下面就渐进式框架结合实际项目给大家加以说明:

将 Vue.js 添加到项目中主要有四种方式

  1. 在页面上以 CDN 包的形式导入。
  2. 下载 JavaScript 文件并自行托管
  3. 使用 npm 安装
  4. 使用官方的 CLI 来构建一个项目,它为现代前端工作流程提供了功能齐备的构建设置

三、了解Vue的引入原理


我们在了解Vue的时候,首先要了解Vue是从哪里来,我们在搭建好的脚手架里面,通过main.js可以找见引入import Vue from 'vue',从从node_modules中找见vue文件夹,进入package.json文件,这个文件的作用是描述整个项目的。在这个文件中存在两个配置字段,它们都是程序的主入口文件。

{
  ...
  "main": "dist/vue.runtime.common.js",
  "module": "dist/vue.runtime.esm.js",
  "unpkg": "dist/vue.js",
  "jsdelivr": "dist/vue.js",
  "typings": "types/index.d.ts",
  "files": [
    "src",
    "dist/*.js",
    "types/*.d.ts"
  ],
  ...

在module的优先级大于main的优先级。在module不存在时,main对应的配置项就是主入口文件。可以看到dist/vue.runtime.esm.js才是主入口文件。在脚手架main.js中,我们可以找见创建Vue实例的代码如下所示:

import Vue from 'vue'
import App from './App.vue'
import router from './router/'
import store from './store/'
import i18n from './locales/'
const vm = new Vue({
  router,
  store,
  i18n,
  created: bootstrap,
  render: h => h(App)
}).$mount('#app')
export default vm

dist/vue.runtime.esm.js文件中搜索Vue的定义函数如下:

function Vue (options) {
  //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);
}

四、了解Vue的初始化函数


通过上述描述,初始画函数包含在 this._init(options);中,这里的this指的是Vue实例

Vue.prototype._init = function (options) {
    var vm = this;
    // a uid
    vm._uid = uid$3++;
    var startTag, endTag;
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      startTag = "vue-perf-start:" + (vm._uid);
      endTag = "vue-perf-end:" + (vm._uid);
      mark(startTag);
    }
    // a flag to avoid this being observed
    vm._isVue = true;
    // merge options
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options);
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      );
    }
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm);
    } else {
      vm._renderProxy = vm;
    }
    // expose real self
    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');
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      vm._name = formatComponentName(vm, false);
      mark(endTag);
      measure(("vue " + (vm._name) + " init"), startTag, endTag);
    }
    if (vm.$options.el) {
      vm.$mount(vm.$options.el);
    }
  };

五、render函数


render: h => h(App)是ES6的写法,其实就是如下内容的简写:

# ES5写法
render: function (createElement) {
     return createElement(App);
}
# ES6写法
render: createElement => createElement(App)
# h代替createElement
render: h => h(App)

官方文档中是这样的,createElement 是 Vue.js 里面的 函数,这个函数的作用就是生成一个 VNode节点,render 函数得到这个 VNode 节点之后,返回给 Vue.js 的 mount 函数,渲染成真实 DOM 节点,并挂载到根节点上。

render: function (createElement) {
    return createElement(
      'h' + this.level,   // tag name 标签名称
      this.$slots.default // 子组件中的阵列
    )
  }

为什么会使用h代替createElement

It comes from the term "hyperscript", which is commonly used in many virtual-dom implementations. "Hyperscript" itself stands for "script that generates HTML structures" because HTML is the acronym for "hyper-text markup language".

它来自单词 hyperscript,这个单词通常用在 virtual-dom 的实现中。Hyperscript 本身是指

生成HTML 结构的 script 脚本,因为 HTML 是 hyper-text markup language 的缩写(超文本标记语言) 也就是说,createElement 函数是用来生成 HTML DOM 元素的,而上文中的 Hyperscript也是用来创建HTML结构的脚本,这样作者才把 createElement 简写成 h。

而 createElement(也就是h)是vuejs里的一个函数。这个函数的作用就是生成一个 VNode节点,render 函数得到这个 VNode 节点之后,返回给 Vue.js 的 mount 函数,渲染成真实 DOM 节点,并挂载到根节点上。

其实在vue 1.x 中,这样的写法也就是如下的含义:

new Vue({
  el: '#app',
  template:'</App>'
  componets: {App}
})

然后页面中使用:

<div id='app'>
  <app></app>
</div>

其实在vue 2.x 中,这样的写法也就是如下的含义:

const vm = new Vue({
  render: h => h(App)
}).$mount('#app')

然后页面中使用:

<div id='app'>
</div>



目录
打赏
0
0
0
0
4
分享
相关文章
Vue实现动态数据透视表(交叉表)
Vue实现动态数据透视表(交叉表)
63 13
极致的灵活度满足工程美学:用Vue Flow绘制一个完美流程图
本文介绍了使用 Vue Flow 绘制流程图的方法与技巧。Vue Flow 是一个灵活强大的工具,适合自定义复杂的流程图。文章从环境要求(Node.js v20+ 和 Vue 3.3+)、基础入门案例、自定义功能(节点与连线的定制、事件处理)到实际案例全面解析其用法。重点强调了 Vue Flow 的高度灵活性,虽然预定义内容较少,但提供了丰富的 API 支持深度定制。同时,文中还分享了关于句柄(handles)的使用方法,以及如何解决官网复杂案例无法运行的问题。最后通过对比 mermaid,总结 Vue Flow 更适合需要高度自定义和复杂需求的场景,并附带多个相关技术博客链接供进一步学习。
属性描述符初探——Vue实现数据劫持的基础
属性描述符还有很多内容可以挖掘,比如defineProperty与Proxy的区别,比如vue2与vue3实现数据劫持的方式有什么不同,实现效果有哪些差异等,这篇博文只是入门,以后有时间再深挖。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
Vue Router 核心原理
Vue Router 是 Vue.js 的官方路由管理器,用于实现单页面应用(SPA)的路由功能。其核心原理包括路由配置、监听浏览器事件和组件渲染等。通过定义路径与组件的映射关系,Vue Router 将用户访问的路径与对应的组件关联,支持哈希和历史模式监听 URL 变化,确保页面导航时正确渲染组件。
ry-vue-flowable-xg:震撼来袭!这款基于 Vue 和 Flowable 的企业级工程项目管理项目,你绝不能错过
基于 Vue 和 Flowable 的企业级工程项目管理平台,免费开源且高度定制化。它覆盖投标管理、进度控制、财务核算等全流程需求,提供流程设计、部署、监控和任务管理等功能,适用于企业办公、生产制造、金融服务等多个场景,助力企业提升效率与竞争力。
128 12
Vue中的class和style绑定
在 Vue 中,class 和 style 绑定是基于数据驱动视图的强大功能。通过 class 绑定,可以动态更新元素的 class 属性,支持对象和数组语法,适用于普通元素和组件。style 绑定则允许以对象或数组形式动态设置内联样式,Vue 会根据数据变化自动更新 DOM。
Vue Router 简介
Vue Router 是 Vue.js 官方的路由管理库,用于构建单页面应用(SPA)。它将不同页面映射到对应组件,支持嵌套路由、路由参数和导航守卫等功能,简化复杂前端应用的开发。主要特性包括路由映射、嵌套路由、路由参数、导航守卫和路由懒加载,提升性能和开发效率。安装命令:`npm install vue-router`。
iframe嵌入页面实现免登录思路(以vue为例)
通过上述步骤,可以在Vue.js项目中通过 `iframe`实现不同应用间的免登录功能。利用Token传递和消息传递机制,可以确保安全、高效地在主应用和子应用间共享登录状态。这种方法在实际项目中具有广泛的应用前景,能够显著提升用户体验。
383 8
Vue 组件化开发:构建高质量应用的核心
本文深入探讨了 Vue.js 组件化开发的核心概念与最佳实践。
117 1