ReactP4_JSX=>虚拟DOM=>真实DOM_阶段性案例

ReactP4_JSX=>虚拟DOM=>真实DOM_阶段性案例

这边是react学习笔记,期间加入了大量自己的理解,用于加强印象,若有错误之处还请多多指出

关于JSX

jsx 从本质上来说是 React.createElement(component, props, …children) 函数的语法糖

而所有的jsx都会通过类似于babel这样的js编译器被转换成React.createElement的函数调用

关于babel的转换可以在babel的官方网站查找

babel转换

关于babel转换的问题,这里进行一个简单的案例示范:

第一步:进入babel官方网站,点击顶部导航栏中的try it out选项,进入转换页面

第一步
第二步:在左侧进行转换配置

转换配置
第三步:在左侧文本框输入内容即可在右侧完成转换

转换完成
从此处可以正式babel转换后得到的代码都是不同参数下对React.createElement的函数调用

React.createElement源码

React_v16.13.1代码的下载地址https://github.com/facebook/react/archive/v16.13.1.zip

源代码入口文件的存储的位置在解压文件react-16.13.1 => packages => react => src => index.js中

入口文件位置
随后在入口文件index.js中找到export关键字,即导出的函数中找到createElement方法

createElement方法位置
根据结尾导出提示可知,该方法是从src的React文件中被引用的

引用文件位置
在React.js文件中找到creatElement方法

方法代码
此处首先看到的是一个判断方法

验证判断
该处主要对开发环境进行判断,如果是需要开发环境,则在内部会进行大量的数据验证判断(具体内容不介绍),从而尽可能多的给出警告提示,如果不是开发环境,则直接执行createElement函数。

createElement函数:

接下来是重头戏,代码太占位置,所以不贴了,具体内容自行查看

createElement函数
此处可知,createElement需要传递三个参数:

  • 参数一:type——当前ReactElement的类型。如果是标签元素,那么就使用字符串表示 “div”,如果是组件元素,那么就直接使用组件的名称

  • 参数二:config——所有jsx中的属性都在config中以对象的属性和值的形式存储

  • 参数三:children——存放在标签中的内容,以children数组的方式进行存储。

    其中,多个元素React内部有对它们进行处理,处理的源码在下方

多元素处理
对传入的参数利用argument,从第三个元素开始遍历提取,放入一个新的数组,数组中的就是后面所有的参数了。

虚拟DOM

通过 React.createElement 最终创建出来一个 ReactElement对象,原因是React利用ReactElement对象组成了一个JavaScript的对象树,JavaScript的对象树就是虚拟DOM(Virtual DOM)

对于虚拟DOM的介绍,我看的是虚拟DOM解析及其在框架里的应用

采用虚拟DOM的主要原因:

  • 很难跟踪状态发生的改变:原有的开发模式,很难跟踪到状态发生的改变,不方便针对应用程序进行调试
  • 操作真实DOM性能较低:传统的开发模式会进行频繁的DOM操作,而这一的做法性能非常的低

导致真实DOM操作性能低的原因

  • document.createElement本身创建出来的就是一个非常复杂的对象
  • DOM操作会引起浏览器的回流和重绘(很吃性能的两个事件),所以在开发中应该避免频繁的DOM操作

通过ReactDOM.render让 虚拟DOM 和 真实DOM同步起来,这个过程中叫做协调(Reconciliation)

个人观点:如果把渲染网页比作是考场上答题,那么虚拟DOM就是我们在草稿纸上画一个草图,真实DOM就是根据草图上的内容在答题纸上写下最后的答案。直接改答题卡是一件很麻烦的事情,但是在草稿上涂涂改改的是很随意的一件事儿。同样的,对虚拟DOM的操作不论从性能还是效率上来说,都是远大于对真实DOM直接进行操作的。可以说,虚拟DOM的存在,是一种对浏览器渲染方式的优化选择。

查看ReactElement的树结构

render() {
	var elementObj = (
	    <div>
	      <div className="header">
	        <h2 title="标题">标题内容</h2>
	      </div>
	      <div className="content">
	        <h2>页面的内容</h2>
	        <button>提交</button>
	        <a href="http://www.baidu.com">百度一下</a>
	      </div>
	      <div className="footer">
	        <p>尾部内容</p>
	      </div>
	    </div>
	  )
	  console.log(elementObj);
	  return elementObj;
}

我们可以将之前的jsx返回结果进行打印

打印结果
关于第一个子元素header继续展开的结果
header元素
而ReactElement最终形成的树结构就是Virtual DOM(随手画图板画的)

树结构

阶段性案例

完成一个简单的类购物车列表,效果如下图:
类购物车列表
其中:

  • 点击按钮“+”,所选行的购买数量+1;
  • 购买数量为1时,按钮“-”不可被点击;
  • 点击“移除”按钮,当前行被删除;
  • 序列号和总价格能够实时更新;
  • 如果所有项被移除完,则显示“内容为空”字样;

结果代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>

  <style>
    table {
      border: 1px solid #eee;
      border-collapse: collapse;
    }

    th, td {
      border: 1px solid #eee;
      padding: 10px 16px;
      text-align: center;
    }

    th {
      background-color: #ccc;
    }

    .count {
      margin: 0 5px;
    }
  </style>
</head>
<body>
    
  <div id="app"></div>

  <script src="../react/react.development.js"></script>
  <script src="../react/react-dom.development.js"></script>
  <script src="../react/babel.min.js"></script>

  <script src="./format-utils.js"></script>

  <script type="text/babel">
    class App extends React.Component {
      constructor(props) {
        super(props);

        this.state = {
          books: [
            {
              id: 1,
              name: '《算法导论》',
              date: '2006-9',
              price: 85.00,
              count: 1
            },
            {
              id: 2,
              name: '《UNIX编程艺术》',
              date: '2006-2',
              price: 59.00,
              count: 1
            },
            {
              id: 3,
              name: '《编程珠玑》',
              date: '2008-10',
              price: 39.00,
              count: 1
            },
            {
              id: 4,
              name: '《代码大全》',
              date: '2006-3',
              price: 128.00,
              count: 1
            },
          ]
        }
      }

      renderBooks() {
        return  (
          <div>
            <table>
              <thead>
                <tr>
                  <th></th>
                  <th>书籍名称</th>
                  <th>出版日期</th>
                  <th>价格</th>
                  <th>购买数量</th>
                  <th>操作</th>
                </tr>
              </thead>
              <tbody>
                {
                  this.state.books.map((item, index) => {
                    return (
                      <tr>
                        <td>{index+1}</td>
                        <td>{item.name}</td>
                        <td>{item.date}</td>
                        <td>{formatPrice(item.price)}</td>
                        <td>
                          <button disabled={item.count <= 1} onClick={e => this.changeBookCount(index, -1)}>-</button>
                          <span className="count">{item.count}</span>
                          <button onClick={e => this.changeBookCount(index, 1)}>+</button>
                        </td>
                        <td><button onClick={e => this.removeBook(index)}>移除</button></td>
                      </tr>
                    )
                  })
                }
              </tbody>
            </table>
            <h2>总价: {this.getTotalPrice()}</h2>
          </div>
        )
      }

      renderEmptyTip() {
        return <h2>内容为空~</h2>
      }

      render() {
        return this.state.books.length ? this.renderBooks(): this.renderEmptyTip();
      }

      changeBookCount(index, count) {
        const newBooks = [...this.state.books];
        newBooks[index].count += count;
        this.setState({
          books: newBooks
        })
      }

      removeBook(index) {
        // React中设计原则: state中的数据的不可变性;
        this.setState({
          books: this.state.books.filter((item, indey) => index != indey)
        })
      }

      getTotalPrice() {
        const totalPrice = this.state.books.reduce((preValue, item) => {
          return preValue + item.count * item.price;
        }, 0);

        return formatPrice(totalPrice);
      }
    }

    ReactDOM.render(<App/>, document.getElementById("app"));
  </script>

</body>
</html>

感谢coderwhy(王红元老师)的课程

最后,爱生活,爱猪猪

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值