Vue3 源码导读(推荐)

下面就详细讲解一下“Vue3 源码导读(推荐)”的完整攻略。

概述

在Vue.js开发过程中,我们都知道Vue.js是一个非常好用的MVVM框架,而Vue.js3的发布也备受关注。Vue.js 3.0采用完全重写的方式,核心代码相比2.x版本变化较大,提高了性能。

导读

为了能够更好地学习Vue.js 3.0,我们需要先了解Vue.js 3.0的源码结构和架构。Vue.js 3.0代码采用了TypeScript编写,源码的目录结构也有所变动,因此阅读源码时需要先掌握TypeScript。以下是一个寻找Vue.js 3.0源码的示例。

git clone https://github.com/vuejs/vue-next.git
cd vue-next
yarn

目录结构

Vue.js 3.0的源码目录结构如下:

├── benchmark                          性能测试代码
├── dist                               打包后的代码
│   ├── runtime-core      运行时核心
│   ├── runtime-dom       运行时DOM
│   ├── compiler-core     编译器核心
│   ├── compiler-dom      编译器DOM
│   ├── server-renderer   服务端渲染
│   ├── size-plugin       代码包大小检测插件
│   └── vue.global.d.ts   Vue.js 的类型定义
├── packages                           包
│   ├── compiler-core                Vue.js编译器核心
│   ├── compiler-dom                 Vue.js编译器DOM
│   ├── reactivity                   Vue.js响应式系统
│   ├── runtime-core                 Vue.js运行时核心
│   ├── runtime-dom                  Vue.js运行时DOM
│   ├── server-renderer              Vue.js服务端渲染
│   ├── shared                       组件定义相关的工具方法
│   ├── size-check                   代码包大小检测插件
│   ├── template-explorer            用于学习 Vue.js 模板的交互式工具
│   ├── vue                          Vue.js 库的入口文件
│   └── vue-router-next              用于 Vue.js 3 的路由库
├── scripts                            脚本
│   ├── benchmarks                   相应的性能测试相关脚本
│   ├── build                        构建配置文件
│   ├── ci                           持续集成脚本
│   ├── release                      发布相关脚本
│   └── size-check                   代码包大小检测相关脚本
├── tests                              测试
│   ├── e2e                          e2e 测试
│   ├── test                         单元测试
│   └── test-utils                   测试工具
├── types                              TypeScript类型定义
└── packages.json                      包管理文件

其中,runtime-core 表示运行时核心,runtime-dom 表示运行时DOM。compiler-core 表示编译器核心,compiler-dom 表示编译器DOM。server-renderer 表示服务端渲染。

示例

以下是一个 runtime-core 的示例:

// 创建VNode
export function createVNode(type: VNodeTypes, props: (Data & VNodeProps) | null = null, children: unknown = null, patchFlag: number = 0, dynamicProps: string[] | null = null, isBlockNode = false): VNode {
  if (!type || type === NULL_DYNAMIC_COMPONENT) {
    if (__DEV__ && !type) {
      warn(`Invalid vnode type when creating vnode: ${type}.`)
    }
    type = Comment // 当类型为空或者是一个空动态组件时,设置类型为Comment组件
  }

  if (isVNode(type)) {
    // clone vnode
    return cloneVNode(type, props, children)
  }

  // class component normalization.
  if (isFunction(type) && '__vccOpts' in type) {
    type = type.__vccOpts
  }

  // class & style normalization.
  if (props) {
    // for reactive or proxy objects, we need to clone it to enable mutation.
    if (isProxy(props) || InternalObjectKey in props) {
      props = extend({}, props)
    }
    let { class: klass, style } = props
    if (klass && !isString(klass)) {
      props.class = normalizeClass(klass)
    }
    if (isObject(style)) {
      // reactive or proxy objects need to be cloned since they are likely to be
      // mutated
      if (isProxy(style) && !isArray(style)) {
        style = extend({}, style)
      }
      props.style = normalizeStyle(style)
    }
  }

  // encode content if any.
  if (isString(children)) {
    children = escapeHtml(children)
  }

  let shapeFlag = isString(type)
    ? ShapeFlags.ELEMENT
    : isSuspense(type)
      ? ShapeFlags.SUSPENSE
      : isTeleport(type)
        ? ShapeFlags.TELEPORT
        : isObject(type)
          ? ShapeFlags.STATEFUL_COMPONENT
          : 0

  // 设置是否为block节点
  if (isBlockNode) {
    shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE
  }

  // 设置 children 数组
  if (isArray(children)) {
    shapeFlag |= ShapeFlags.ARRAY_CHILDREN
  } else {
    // only allow array children or no children
    children = normalizeChildren(children)
  }

  return {
    __v_isVNode: true,
    __v_skip: true,
    type,
    props,
    key: (props && normalizeKey(props)) || null,
    ref: (props && normalizeRef(props)) || null,
    children,
    component: null,
    suspense: null,
    dirs: null,
    // 设计的优化: fast-path flags
    shapeFlag,
    // 性能优化所用,避免不必要的children数组,如果没有动态props,则静态的走cache,动态的不走
    patchFlag,
    dynamicProps,
    // 针对组件节点上下文,initial为false代表是异步节点
    isBlock: isBlockNode,
    // 是否为cloned节点,即是否为克隆节点,只有teleport的时候才会设置为克隆节点
    // 详情请看 createTeleport 相关源码实现
    isCloned: false,
    // 是否链接其他vnode,详情请看 createEditorBlock 相关源码实现
    isReplaced: false,
    // el 用来记录对应节点 mounted 后对应的真实 DOM,详情请看mount 源码实现
    el: null,
    // anchor 用来记录组件节点的插入位置, 用于需要在该组件前插入DOM时
    anchor: null
  }
}

以上代码是 runtime-core 中创建 VNode 的部分源码,主要分为以下几个部分:

  1. 对类型 type 校验,当类型为空或者是一个空动态组件时,设置类型为 Comment 组件。
  2. 对 children 进行处理,如果为普通字符串则进行编码。
  3. 设置组件的 ShapeFlags,其中 isBlockNode 为是否为 block 节点。
  4. 判断 children 类型,分为数组和非数组两个情况。
  5. 返回 VNode 对象。

以上是 runtime-core 源码的一个示例。

另一个示例是在 reactivity 模块中实现双向绑定的部分。

// reactive/index.ts

export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive<T extends object>(target: T): T {
  if (target && (target as Target)[ReactiveFlags.REACTIVE]) { // 如果target已经是一个Proxy中
    // target is already a Proxy, return it.
    return target
  }

  // target 是不可扩展的对象,则不进行代理
  if (!isObject(target)) {
    if (__DEV__) {
      console.warn(`value cannot be made reactive: ${String(target)}`)
    }
    return target
  }

  const observed = createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers
  )
  return observed
}

该示例展示了从函数调用开始到返回的过程,主要实现了以下几个部分:

  1. 判断 target 是否已经是响应式对象,如果是,则直接返回 target。
  2. 判断 target 是否可扩展,不可扩展则直接返回 target。
  3. 调用 createReactiveObject 生成代理对象 observed 并返回。

以上是 reactivity 模块源码的一个示例。

总结

Vue.js 3.0 的源码是一个非常庞大的生态系统,了解其源码架构和目录结构并进行源码阅读对于学习 Vue.js 3.0 有很大的帮助。同时,Vue.js 3.0 的 TypeScript 实现也给学习 TypeScript 的同学提供了一个学习的机会,加深 TypeScript 的理解。学习 Vue.js 3.0 的源码需要一定的 TypeScript 基础和耐心,相信通过阅读 Vue.js 3.0 的源码,一定会对 Vue.js 3.0 框架有更深入的理解和认识。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Vue3 源码导读(推荐) - Python技术站

(0)
上一篇 2023年5月27日
下一篇 2023年5月27日

相关文章

  • vue3 axios 实现自动化api配置详解

    Vue3 Axios 实现自动化 API 配置详解 背景 在Vue项目中,我们经常需要向后端API接口发起请求来获取数据或者提交表单。Axios是一种流行的基于Promise的HTTP库,它可以帮助我们在Vue中发起请求。 当项目较大时,我们需要管理大量的API请求,每个API都有不同的地址、请求方式、请求参数等。因此,我们需要对Axios进行封装,以便于在…

    Vue 2023年5月28日
    00
  • vue本地打开build后生成的dist文件夹index.html问题

    针对“vue本地打开build后生成的dist文件夹index.html问题”,这里提供一份完整攻略,以下是具体步骤: 1. 构建vue项目 首先需要以vue-cli工具构建一个基本的vue项目。打开终端,输入以下命令: vue create my-project 其中my-project是你项目的名称,你也可以用其他的名称。 完成之后,进入项目文件夹: c…

    Vue 2023年5月28日
    00
  • 解决vue 格式化银行卡(信用卡)每4位一个符号隔断的问题

    要解决vue格式化银行卡(信用卡)每4位一个符号隔断的问题,可以遵循以下的攻略: 1. 导入格式化库 可以使用一些格式化库去帮助你实现格式化功能。在Vue.js中,我们可以使用vue-numeric-input这一库,来实现银行卡格式化功能。 导入vue-numeric-input库: npm install vue-numeric-input –save…

    Vue 2023年5月27日
    00
  • Vue源码学习记录之手写vm.$mount方法

    下面我详细讲解一下 “Vue源码学习记录之手写vm.$mount方法” 的完整攻略,包括如下几个步骤: 1. 确定学习目标 在学习Vue源码的过程中,我们需要了解Vue内部的一些实现细节。这个过程并不简单,我们需要先确定学习目标,才能有系统地学习。在这里,我们的学习目标就是手写 Vue 中的 vm.$mount() 方法。 2. 阅读官方文档 Vue 官网提…

    Vue 2023年5月29日
    00
  • VUE 实现动态给对象增加属性,并触发视图更新操作示例

    VUE 实现动态给对象增加属性,并触发视图更新操作可以通过以下两个示例进行说明。 示例一 <template> <div> <button @click="addAttr">添加属性</button> <span>添加属性之前:</span><span>{…

    Vue 2023年5月28日
    00
  • vue3无法使用jsx的问题及解决

    让我们来详细讲解一下“Vue3无法使用JSX的问题及解决”的攻略。 问题描述 在Vue2中,由于Vue默认使用的模板语言是HTML-Based的,因此不支持JSX,这意味着在Vue2中我们无法直接使用JSX编写组件。 在Vue3中,Vue团队引入了一个新的API——createRenderer()。这个API以渲染器为基础,为Vue提供了更灵活的渲染方式。 …

    Vue 2023年5月28日
    00
  • Vue导出页面为PDF格式的实现思路

    下面我将为您详细讲解Vue导出页面为PDF格式的实现思路。实现思路主要基于PDF.js和html2canvas两个工具组合使用。 实现思路 引入PDF.js和html2canvas 在public文件夹下创建一个pdfjs文件夹,将下载好的PDF.js的代码放到该文件夹下。 在public文件夹中的index.html文件中引入pdfjs文件夹中的pdf.j…

    Vue 2023年5月29日
    00
  • vue-cli配置文件——config篇

    下面是关于“vue-cli配置文件——config篇”的完整攻略: 1. 概述 在使用Vue-cli构建Vue项目时,除了基础配置文件(如 package.json、index.html、main.js等),还有一些高级配置文件。其中,config目录下的配置文件是一些开发、打包、运行时的配置信息集合。 2. 细节 2.1 index.js index.js…

    Vue 2023年5月28日
    00
合作推广
合作推广
分享本页
返回顶部