vue3剖析之简版实现

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 文章主要阐述vue3的API用法,以及简单地实现一个vue3。带大家感受一下vue3与之前vue2的区别。以及简单带大家揭秘源码中vue3初始化的一个流程。

网络异常,图片无法展示
|


hello 大家好,🙎🏻‍♀️🙋🏻‍♀️🙆🏻‍♀️

我是一个热爱知识传递,正在学习写作的作者,ClyingDeng 凳凳!


最近,由于我的第一个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变量,内部存在createAppreactive等方法。


在此,我们先实现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的简单实现到此就结束拉。感兴趣的同学可以关注 Vue源码初识专栏或者关注我哦!会持续输出vue相关知识哦(●'◡'●)。 如果不足,请多指教。

目录
相关文章
|
2月前
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
148 64
|
2月前
|
JavaScript 前端开发 API
Vue 3 中 v-model 与 Vue 2 中 v-model 的区别是什么?
总的来说,Vue 3 中的 `v-model` 在灵活性、与组合式 API 的结合、对自定义组件的支持等方面都有了明显的提升和改进,使其更适应现代前端开发的需求和趋势。但需要注意的是,在迁移过程中可能需要对一些代码进行调整和适配。
118 60
|
13天前
|
JavaScript API 数据处理
vue3使用pinia中的actions,需要调用接口的话
通过上述步骤,您可以在Vue 3中使用Pinia和actions来管理状态并调用API接口。Pinia的简洁设计使得状态管理和异步操作更加直观和易于维护。无论是安装配置、创建Store还是在组件中使用Store,都能轻松实现高效的状态管理和数据处理。
47 3
|
2月前
|
前端开发 JavaScript 测试技术
Vue3中v-model在处理自定义组件双向数据绑定时,如何避免循环引用?
Web 组件化是一种有效的开发方法,可以提高项目的质量、效率和可维护性。在实际项目中,要结合项目的具体情况,合理应用 Web 组件化的理念和技术,实现项目的成功实施和交付。通过不断地探索和实践,将 Web 组件化的优势充分发挥出来,为前端开发领域的发展做出贡献。
41 8
|
2月前
|
存储 JavaScript 数据管理
除了provide/inject,Vue3中还有哪些方式可以避免v-model的循环引用?
需要注意的是,在实际开发中,应根据具体的项目需求和组件结构来选择合适的方式来避免`v-model`的循环引用。同时,要综合考虑代码的可读性、可维护性和性能等因素,以确保系统的稳定和高效运行。
35 1
|
2月前
|
JavaScript
Vue3中使用provide/inject来避免v-model的循环引用
`provide`和`inject`是 Vue 3 中非常有用的特性,在处理一些复杂的组件间通信问题时,可以提供一种灵活的解决方案。通过合理使用它们,可以帮助我们更好地避免`v-model`的循环引用问题,提高代码的质量和可维护性。
45 1
|
2月前
|
JavaScript
在 Vue 3 中,如何使用 v-model 来处理自定义组件的双向数据绑定?
需要注意的是,在实际开发中,根据具体的业务需求和组件设计,可能需要对上述步骤进行适当的调整和优化,以确保双向数据绑定的正确性和稳定性。同时,深入理解 Vue 3 的响应式机制和组件通信原理,将有助于更好地运用 `v-model` 实现自定义组件的双向数据绑定。
|
2月前
|
存储 JavaScript 前端开发
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
【10月更文挑战第21天】 vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
|
2月前
|
JavaScript 索引
Vue 3.x 版本中双向数据绑定的底层实现有哪些变化
从Vue 2.x的`Object.defineProperty`到Vue 3.x的`Proxy`,实现了更高效的数据劫持与响应式处理。`Proxy`不仅能够代理整个对象,动态响应属性的增删,还优化了嵌套对象的处理和依赖追踪,减少了不必要的视图更新,提升了性能。同时,Vue 3.x对数组的响应式处理也更加灵活,简化了开发流程。
|
2月前
|
JavaScript 前端开发 开发者
Vue 3中的Proxy
【10月更文挑战第23天】Vue 3中的`Proxy`为响应式系统带来了更强大、更灵活的功能,解决了Vue 2中响应式系统的一些局限性,同时在性能方面也有一定的提升,为开发者提供了更好的开发体验和性能保障。
82 7

热门文章

最新文章