ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

JSX 和虚拟 DOM

2021-03-17 16:03:45  阅读:226  来源: 互联网

标签:DOM App element React 虚拟 vnode JSX


 

新蜂商城开源仓库:github.com/newbee-ltd(内涵 Vue 2.x 和 Vue 3.x 的 H5 商城开源代码)

Vue 3.x + Vant 3.x 高仿微信记账本开源地址:github.com/Nick930826/…

写在前面

这篇文章我构思了很久,想用比较白话的形式阐述关于 JSX 和 VDOM 的知识点。翻阅了不少相关内容,多数文章都是以源码为基础,讲的内容不能说不好,但是至少我觉得对于刚入门的前端同学,内容篇硬。本篇文章以 React 作为切入点,分析理解 JSX 和虚拟 DOM ,当然 Vue 技术栈的同学也可以看,毕竟这两个框架都是互相学习互相借鉴的,知识都是互通的。

还是那句话,这篇文章篇理解,对新手较友好,大佬够自信的话,就此作罢。看完的同学觉得有帮助的话,可以点个赞,让我有继续写下去的动力。前几篇文章评论区有几位同学想了解别的知识,我都记着,等我过年回老家再码吧。

我学习一个知识点,习惯带着问题去找答案,所以本篇文章也不例外,我们带着下面几个问题看文章:

  • JSX 是什么?
  • 用不用 JSX 对开发有什么影响?
  • 虚拟 DOM 长啥样,怎样渲染成真实 DOM ?
  • 虚拟 DOM 存在的意义是什么?

把问题整明白了才是真的实力,别整天想着吊打面试官,面试官做错了什么。(逃)

JSX 是什么

它是 JS 的一个语法扩展。官方是这么定义它的:

JSX 是一个 JavaScript 的语法扩展,但它具有 JavaScript 的全部功能。

在 React 项目中我们是这样去书写 JSX ,如下:

const App = <div>
  test
</div>
复制代码

不是说 React 是通过虚拟 DOM 来渲染页面的吗?此时,好像看不出虚拟 DOM 的样子。 别急,首先 babel 会为我们将 JSX 语法变异成 React.createElement() 的形式,具体可以通过 babel 官网 查看编译后的样子,如下所示:

image.png

我们来验证一下,直接写成编译后的 React.createElement 函数,页面会不会正常渲染,我们通过 create-react-app 构建一个 React 基础项目,修改 index.js 如下:

import React from 'react'
import ReactDOM from 'react-dom'

const App = () => {
  return React.createElement(
    "div",
    {
      className: "app"
    },
    "father",
    React.createElement(
      "div",
      null,
      "child"
    )
  )
}

ReactDOM.render(<App />, document.getElementById('root'))
复制代码

浏览器展示如下:

image.png

我们不妨在 index.js 中打印一下 App 和 App() ,看看有什么不同,如下所示:

console.log('App:', App)
console.log('App():', App())
复制代码

打印结果如下:

image.png

这里你可以看到,在不执行 App 的时候,它就是一个普通的函数,所以我们应该称它为函数组件 — Componnet ,而执行完后的返回结果,正是我们想要的虚拟 DOM ,这里我们可以称它为 React 元素 — ReactElement 。

这个 ReactElement 对象实例,本质上是以 JavaScript 对象形式存在的对 DOM 的描述,也就是虚拟 DOM。

上图中的虚拟 DOM 我们可以反推出真实 DOM 是长这样的:

<!--最外层的div-->
<div>
  <!--第一个子节点-->
	father
  <!--第二个子节点是被 div 包裹的,内容是child-->
  <div>child</div>
</div>
复制代码

所以这时我们就能很自信的说,不用 JSX 开发项目,也是可以的。只要你已经无敌,全都用 React.createElement 去写标签以及标签内的方法、样式、自定义属性等等等等。 反正我肯定没有这么无敌,大傻子才这么"淦"吧。

虚拟 DOM 咋渲染成真实 DOM

我们继续沿用上面通过 create-react-app 构建好的 demo 项目,修改 index.js 如下:

import React from 'react'

// JSX 编写 React 组件
const App = () => <div>
  <div>十三哥:你是什么星座的?</div>
  <div>尼克陈:我是为你量身定座。</div>
</div>

// 自定义虚拟 DOM 转真实 DOM 函数 MyRender。
// vnode:虚拟DOM节点;root:插入的父节点(注意,这里不一定就是 index.html 里的 app 节点)。
const MyRender = (vnode, root) => {
  // 如果没有没有传入 root 节点,则不执行。
  if (!root) {
    return
  }
  let element // 声明一个空变量,用于下面存放节点信息。
  if (vnode.constructor !== Object) {
    // 如果 vnode 的类型为非 Object,则是没有标签包裹的普通字符,直接赋值 element。
    element = document.createTextNode(vnode);
  } else {
    // 否则,则是有标签包裹的类型,通过 createElement 事件创建新的标签,标签名就是 type 属性值。
    element = document.createElement(vnode.type);
  }
  // 塞进父节点 root。
  root.appendChild(element)
  
  // 如果 vnode 有 children 属性,则要进行递归操作。
  if (vnode.props && vnode.props.children) {
    const childrenVNode = vnode.props.children
    // 判断是不是数组,如果是,则进入 forEach 循环执行 MyRender
    if (Array.isArray(childrenVNode)) {
      childrenVNode.forEach((child) => {
        MyRender(child, element)
      })
    } else {
      // 否则直接执行 MyRender
      MyRender(childrenVNode, element)
    }
  }
}

// 初始化执行 MyRender 函数,注意第一个参数需要传入 ReactElement,也就是虚拟 DOM。
MyRender(App(), document.getElementById('root'))
复制代码

代码解析已经都写在上述代码的注视中,每一行都有解释,认真看完,并不难理解。一顿操作,其实就是想方设法将虚拟 DOM ,通过 JS 方法,渲染成真实 DOM ,然后插入到根节点。 我们通过 npm run start 运行项目,看看浏览器是否能渲染出真实 DOM :

image.png

嚯喔!~~(羞涩)。 甚至你还可以在给“十三哥”来点“绿”,点击“尼克陈”来点方法,代码如下:

const App = () => <div>
  <div className='shisan' style={{ color: 'green' }}>十三哥:你是什么星座的?</div>
  <div onClick={() => console.log('别闹啊')}>尼克陈:我是为你量身定座。</div>
</div>

...
let element
if (vnode.constructor !== Object) {
  element = document.createTextNode(vnode)
} else {
  element = document.createElement(vnode.type)
  // 添加点击事件
  if (vnode.props.onClick) {
    element.addEventListener('click', () => {
      vnode.props.onClick()
    })
  }
  // 添加样式
  if (vnode.props.style) {
    Object.keys(vnode.props.style).forEach(key => {
      element.style[key] = vnode.props.style[key]
    })
  }
  // 添加类名
  if (vnode.props.className) {
    element.className = vnode.props.className
  }
}
...
复制代码

浏览器展示如下:

Kapture 2021-01-31 at 12.36.56.gif

这里申明, ReactDOM.render 的内容并没有我上述写的那么简单,涉及到的源码也相当庞大,这里只是我简单的将虚拟 DOM 转化成真实 DOM 的一个小用例。包括 React 的事件机制,也是自身单独实现了一份,不是上述描述的这么简单。

标签:DOM,App,element,React,虚拟,vnode,JSX
来源: https://www.cnblogs.com/suntongxue/p/14549883.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有