vue3源码剖析之简单实现方法

目录
  • 前言
  • 🍹准备工作
  • 🍲vue3用法
  • 🍖实现
  • 总结

前言

最近,由于我的第一个vue3 + ts的正式项目,已经进入验收阶段。听你们老说vue3、vue3的,我就想着去看看vue3到底和vue2有啥区别。🤷🏻‍♀️🤷🏻‍♀️🤷🏻‍♀️

文章主要阐述vue3的API用法,以及简单地实现一个vue3。带大家感受一下vue3与之前vue2的区别。以及简单带大家揭秘源码中vue3初始化的一个流程。

🍹准备工作

要想看看vue3内部的源码是咋搞得,首先跟vue2源码剖析一样,先从github上下一份源码到本地。

接着就是安装依赖:

yarn --ignore-scripts

在你执行命令的时候可能会遇到node版本过低的错误:

解决此问题可以升级自己的node版本,或者忽略该engine。

如果选择忽略的话可以设置

yarn config set --ignore-engines true

然后执行依赖安装。

依赖安装完成后,编译打包生成vuejs文件:

yarn dev

需要调试的话,可以在packages\vue\examples文件下建立测试文件。引用打包后的vue文件,可以应用packages\vue\dist\vue.global.js。

🍲vue3用法

vue3的特性我就不多阐述了,就vue3的用法而言,更倾向于函数式编程,通过对外暴露Vue中的createApp()API,以工厂函数的方式创建了一个应用程序实例。相比较vue2的new Vue实例,更加贴切。

在源码文件中,我们新建一个init.html文件。

<script src="../dist/vue.global.js"></script>
 <body>
  <div id="app">{{name}}</div>
  <script>
    const { createApp } = Vue
    const app1 = createApp({
      data() {
        return {
          name: 'clying'
        }
      },
      setup() {
        return {
          name: 'deng'
        }
      }
    }).mount('#app')
  </script>
</body>

根据上例,我们可以看出vue3是即支持Composition API,也支持Options API,两者可以同时使用。

但是,我们可以看到在data和setup中,我同时使用了一个name变量进行赋值。那么页面中会展示哪一个呢?

3!2!1!上答案:

可以明显看出composition-api中setup优先级更高。

当然也可以在源码中的packages\runtime-core\src\componentPublicInstance.ts看到,通过switch先判断setup中的变量是否存在,然后再去判断data中的变量。所以setup中变量的优先级会高于data中的变量。

🍖实现

通过上面的用法,我们可以知道vue3中会对外暴露一个Vue变量,内部存在createApp、reactive等方法。

在此,我们先实现vue3的初始化框架。就createApp而言,它会接收用户传入的参数:data()、setup()等,最后进行实例挂载mount。所以在createApp中会接收一些参数options、内部还会存在mount方法。

const Vue = {
    createApp(options) {
      return {
        mount(selector) { //解析、获取render、挂载
        }
      }
    }
}

在mount中通过selector获取到宿主元素。

接下来就是对模板的编译,由于将template编译AST后,依旧要转成render函数。在此我们简化操作,在编译时直接返回一个render。

mount(selector) { //解析、获取render、挂载
  const parent = document.querySelector(selector)
  console.log(parent);
  if (!options.render) {
    // 编译返回render
    options.render = this.compileToFunction(parent.innerHTML)
  }
},
compileToFunction(template) {
  return function render() {
    const h = document.createElement('div')
    h.textContent = this.name
    return h
  }
}

拿到render之后,执行它,将其添加到宿主元素中,将老的节点删除。

在执行render的时候,我们需要注意它的this指向。如果给它绑定data,那么它展示的就会使data中的name。

mount(selector) { //解析、获取render、挂载
  const parent = document.querySelector(selector)
  console.log(parent);
  if (!options.render) {
    // 编译返回render
    options.render = this.compileToFunction(parent.innerHTML)
  }
  // 执行render
  const el = options.render.call(options.data())
  parent.innerHTML = ''
  parent.appendChild(el)
},

可以看到页面上展示的是clying。反之,如果绑定的是options.setup(),那么页面上出现的就是deng。

对于vue3的用法,我们知道setup的优先级是高于data的。那我们可以使用代理啊,将两者的属性变量,通过代理的方式,糅合到一起,优先考虑setup。当访问相同name时,实际访问的就是setup中的name。

mount(selector) { //解析、获取render、挂载
  const parent = document.querySelector(selector)
  console.log(parent);
  if (!options.render) {
    // 编译返回render
    options.render = this.compileToFunction(parent.innerHTML)
  }
  if (options.setup) {
    this.setupState = options.setup()
  }
  if (options.data) {
    this.data = options.data()
  }
  this.proxy = new Proxy(this, {
    get(target, key) {
      if (key in target.setupState) {
        return target.setupState[key]
      } else if (key in target.data) {
        return target.data[key]
      }// 还可能存在props、watch等其他同名变量
    },
    set(target, key, value, newVal) {
      console.log(target, key, value, newVal);
    }
  })
  // 执行render  this.proxy就是整合setup和data的上下文
  const el = options.render.call(this.proxy)
  console.log(el, options.render);
  parent.innerHTML = ''
  parent.appendChild(el)
},

在proxy的get中,先看setup中是否存在目标属性,如果存在的话返回的就是setup中的属性变量,否则就是data中的。在渲染的时候,直接将整合的变量集传入即可。当然proxy中也会存在set方法,需要先代理,然后在外部获取变量做值得修改才能触发,在此有兴趣的同学可以自行研究哦!

总结

到此这篇关于vue3源码剖析之简单实现的文章就介绍到这了,更多相关vue3源码剖析内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Vue3.x源码调试的实现方法

    几句话说下我看源码的方式 断点调试 根据demo小例子或者api的使用姿势进行调试,每个小例子只关心其走过的逻辑分支. 如何调试vue3.x的ts源码 官网说使用 yarn dev 命令就可以对其进行调试,可是运行该命令后,是生成过后的代码,不能对其编写的ts源码进行调试. 其实再生成对应的sourcemap文件,便可以原汁原味的调试. 先看下几个截图: 如果这是你想要的调试效果,下面请看下如何生成sourcemap文件. 生成sourcemap文件 rollup.js中文文档 // rollu

  • Vue3 源码导读(推荐)

    5号凌晨,尤雨溪公布了 Vue 3 源代码. 话不多说,我们趁热对 Vue 3 源码进行一些简要的分析. 如果你还没有阅读过Composition API RFC,可能无法完全看懂下面的内容. 兼容性 目前打包后的代码是 ES2015+,不支持 IE 11. 对 TypeScript 的使用 目前的代码 98% 以上使用 TypeScript 编写. 如果你还没有学习 TypeScript,请尽快学习,否则可能看不懂源码. 另外有件事情说出来可能会让你非常惊讶,Vue 3 的源代码完全没有使用

  • Vue3.0 响应式系统源码逐行分析讲解

    前言 关于响应式原理想必大家都很清楚了,下面我将会根据响应式API来具体讲解Vue3.0中的实现原理, 另外我只会针对get,set进行深入分析,本文包含以下API实现,推荐大家顺序阅读 effect reactive readonly computed ref 对了,大家一定要先知道怎么用哦~ 引子 先来段代码,大家可以直接复制哦,注意引用的文件 <!DOCTYPE html> <html lang="en"> <head> <meta ch

  • vue3.x源码剖析之数据响应式的深入讲解

    目录 前言 什么是数据响应式 数据响应式的大体流程 vue2.x数据响应式和3.x响应式对比 大致流程图 实现依赖收集 代码仓库 结尾 前言 如果错过了秋枫和冬雪,那么春天的樱花一定会盛开吧.最近一直在准备自己的考试,考完试了,终于可以继续研究源码和写文章了,哈哈哈.学过vue的都知道,数据响应式在vue框架中极其重要,写代码也好,面试也罢,数据响应式都是核心的内容.在vue3的官网文档中,作者说如果想让数据更加响应式的话,可以把数据放在reactive里面,官方文档在讲述这里的时候一笔带过,笔

  • Vue3源码分析reactivity实现方法示例

    目录 深入分析对于map.set.weakMap.weakSet的响应式拦截 (1).mutableInstrumentations (2).shallowInstrumentations (3).readonlyInstrumentations (4).shallowReadonlyInstrumentations ref.computed等方法的实现 (1).ref与shallowRef源码解析 (2).toRefs (4).computed (5)其他api源码 最后总结: 深入分析对于m

  • 谈谈jQuery之Deferred源码剖析

    一.前言 大约在夏季,我们谈过ES6的Promise,其实在ES6前jQuery早就有了Promise,也就是我们所知道的Deferred对象,宗旨当然也和ES6的Promise一样,通过链式调用,避免层层嵌套,如下: //jquery版本大于1.8 function runAsync(){ var def = $.Deferred(); setTimeout(function(){ console.log('I am done'); def.resolve('whatever'); }, 10

  • Flask核心机制之上下文源码剖析

    一.前言 了解过flask的python开发者想必都知道flask中核心机制莫过于上下文管理,当然学习flask如果不了解其中的处理流程,可能在很多问题上不能得到解决,当然我在写本篇文章之前也看到了很多博文有关于对flask上下文管理的剖析都非常到位,当然为了学习flask我也把对flask上下文理解写下来供自己参考,也希望对其他人有所帮助. 二.知识储备 threadlocal 在多线程中,线程间的数据是共享的, 但是每个线程想要有自己的数据该怎么实现? python中的threading.l

  • python源码剖析之PyObject详解

    一.Python中的对象 Python中一切皆是对象. ----Guido van Rossum(1989) 这句话只要你学过python,你就很有可能在你的Python学习之旅的前30分钟就已经见过了,但是这句话具体是什么意思呢? 一句话来说,就是面向对象中的"类"和"对象"在Python中都是对象.类似于int对象的类型对象,实现了"类的概念",对类型对象"实例化"得到的实例对象实现了"对象"这个概念.

  • Spring Bean的实例化之属性注入源码剖析过程

    前言 这一章节我们来讨论创建Bean过程中的属性注入,在Spring的IOC容器启动过程中,会把定义的Bean封装成BeanDefinition注册到一个ConcurrentHashMap中,Bean注册完成后,就会对单利的且lazy-init=false 的Bean进行实例化.创建Bean的代码在 AbstractAutowireCapableBeanFactory#doCreateBean 中,当Bean创建成功之后,会调用AbstractAutowireCapableBeanFactory

  • golang高并发系统限流策略漏桶和令牌桶算法源码剖析

    目录 前言 漏桶算法 样例 源码实现 令牌桶算法 样例 源码剖析 Limit类型 Limiter结构体 Reservation结构体 Limiter消费token limiter归还Token 总结 前言 今天与大家聊一聊高并发系统中的限流技术,限流又称为流量控制,是指限制到达系统的并发请求数,当达到限制条件则可以拒绝请求,可以起到保护下游服务,防止服务过载等作用.常用的限流策略有漏桶算法.令牌桶算法.滑动窗口:下文主要与大家一起分析一下漏桶算法和令牌桶算法,滑动窗口就不在这里这介绍了.好啦,废

  • Vue3 源码解读之副作用函数与依赖收集

    目录 副作用函数 副作用函数的全局变量 targetMap targetMap 为什么使用 WeakMap activeEffect shouldTrack 副作用的实现 effect 函数 ReactiveEffect 类 track 收集依赖 track 函数 trackEffects 函数 trigger 派发更新 trigger 函数 triggerEffects 函数 总结 版本:3.2.31 副作用函数 副作用函数是指会产生副作用的函数,如下面的代码所示: function effe

  • Spring Cloud集成Nacos Config动态刷新源码剖析

    目录 正文 Nacos Config动态刷新机制 Nacos Config 长轮询源码剖析 ClientWorker构造器初始化线程池 长轮询流程方法 正文 从远端服务器获取变更数据的主要模式有两种:推(push)和拉(pull).Push 模式简单来说就是服务端主动将数据变更信息推送给客户端,这种模式优点是时效性好,服务端数据发生变更可以立马通知到客户端,但这种模式需要服务端维持与客户端的心跳连接,会增加服务端实现的复杂度,服务端也需要占用更多的资源来维持与客户端的连接. 而 Pull 模式则

随机推荐