likes
comments
collection
share

React和ReactDOM是如何工作运行的

作者站长头像
站长
· 阅读数 10

ReactReactDOM是如何工作运行的?

我的技术栈是vue,由于公司的需要,需要学习react。但是在使用react写了一些项目之后,对react的理解还是很浅薄,使用起来虽然可以实现需求,但是内部是如何运行的一无所知,所以在网上找了一篇文章进行翻译加上自己的理解总结成这篇博客,希望能帮助到有需要的人。


当你使用React的时候,你可能会使用JSX进行构建应用程序。JSX是一个基于标签的JavaScript语法,它的语法规则看起来像HTMLReact元素(React element)是在JSX和继续学习React之前需要掌握的最小和最基本的单位。换句话说,在学习JSX和继续深入学习React之前,我们要对React元素有基本的了解。

注意: 为了了解React在浏览器中是如何运行的,我们需要两个库:ReactReactDOMReact库是负责创建视图的,而ReactDOM库是负责浏览器中渲染UI的。

React: https://cdnjs.com/libraries/react
ReactDOM: https://cdnjs.com/libraries/react-dom

引入这两个库在你的主要JavaScript文件之前。在了解React如何运行的同时,我们需要使用ReactReactDOM创建一个小型应用。简单起见,这个应用只包含Index.htmlmain.js两个文件。React库应该使用开发版本,这样在控制台是可以看到警告和错误的。

<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@18.2.0/umd/react-dom.production.min.js"></script></script>
  <script src="./main.js"></script>
</head>
<body>
  
</body>
</html>

React element

先放概念:元素(element)是组成React应用的最小的单位。元素是一个普通的对象,它根据DOM节点描述了我们想要出现在页面上的东西。

HTML只是一组最终会变成DOM元素的命令。假如你需要写一个JS框架或者库的HTML的代码。比如使用vue2使用模板语法来构建DOM树。我们先使用HTML的语法来创建一个HTML层级结构来看下代码和效果:

<section class="js-section">
  <h1>JavaScript Libraries and Frameworks</h1>
  <ul class="list-lib-frameworks">
    <li>React.js</li>
    <li>Angular</li>
    <li>Vue.js</li>
    <li>Node.js</li>
    <li>underscore.js</li>
  </ul>
</section>

上述HTML代码最后会渲染成一个DOM树,根节点是section,它有两个子节点,一个是h1,一个是ulh1的子节点是文本节点。ul的子节点是5个li节点,li的子节点是文本节点。

在过去,网站是由多页面组成的,用户点击链接,浏览器请求一个新的HTML页面并且重新创建DOM。但是在AJAX发明之后,出现了单页面应用,也就是SPA(single page application)。在单页面应用中,浏览器会加载初始的HTML文档,然后用户通过点击链接进行导航的时候,浏览器会发送请求并且浏览器只会更新一部分DOM。当用户点击链接进行导航的时候,从观感上看,是从一个页面跳转到另一个页面了,但实际上是一直保持在一个页面。JavaScript摧毁旧的UI,然后创建新的UI,这就是JavaScript的幕后工作。那么JavaScript在更新UI方面是如何运行的呢?

JavaScript如何更新DOM?

JavaScript使用DOM API更新和操纵DOM节点。DOM APIJavaScript用来操作DOM的对象的集合。操作指的就是CURD,也就是增删改查,如果你想要构建一个HTML页面,也可以使用JavaScript进行构建:

const root = document.querySelector( 'body' );

function createListElement() {
    const libsAndFrameworksNames = ['React.js', 'Angular', 'Vue.js',
    'Node.js', 'Underscore.js'];
    
    const ul = document.createElement( 'ul' );
    ul.classList.add( "list-lib-frameworks" );

    libsAndFrameworksNames.forEach( function appoendToUnorderedList( name ) {
        const listElement = document.createElement( 'li' );
        listElement.innerText = name;
        ul.appendChild( listElement );
    } );

    return ul;
}

function createWebPageWithJavaScript( root ) {
    // PARENT ELEMENT
    const parent = document.createElement( 'section' );
    parent.classList.add( 'js-section' );
    
    // HEADING ELEMENT
    const heading = document.createElement( 'h1' );
    heading.innerText = 'JavaScript Libraries and Frameworks';

    // UNORDERED LIST ELEMENT
    const unorderedListElement = createListElement();

    // APPEND HEADING AND UNORDERED LIST ELEMENT TO PARENT
    parent.appendChild(heading)
    parent.appendChild( unorderedListElement );

    // APPEND PARENT TO ROOT ELEMENT
    root.appendChild( parent );
}

createWebPageWithJavaScript( root );

React是一个被设计用来和DOM交互的框架,从现在来看我们不直接更新DOM,而是告诉React我们要更新DOM.React将通过我们给出的命令为我们负责元素的渲染。

在DOM和React之间有许多相似点,比如他们都由节点组成。DOM是由DOM节点组成,React是由React element 组成的。看起来很相似,实际上很不同。

创建React element

就像前边所说的,React elementReact中最小的单位。React元素是在内存中描述DOM元素的JavaScript对象。我们可以通过使用React的方法createElement**来创建一个React元素。

语法:

React.createElement(type,[props],[...children])

参数:

typetype就是你想要创建元素的类型,如divpli等等。type也可以是其他的React元素。

props:它是一个JavaScript对象,包含构建DOM需要的属性和数据。

childrenorcontentReact元素可能会有children或者content来显示其他的嵌套的元素,是节点的内容。

例子: 如何创建li元素

const element = React.createElement("li",null,'React.js')
console.log(element)

React和ReactDOM是如何工作运行的

在上述例子中,我们给方法里传递了三个参数,如下所示:

li: 它代表着我们创建React元素的类型,我们也能从上述log看出type:"li"

null: 我们没有为元素定义任何的属性,但是我们可以添加像className属性,但是在上边的例子中我们没有那么做。因此我们传递了null值。

React.js: 我们传入了文本作为元素的childrenchildren不仅可以是文本还可以是其他元素。

在渲染期间,也就是render期间,React会将React元素转变成真实DOM

<li>React.js</li>

并且如果你想要给li元素添加一个类,可以像下边这样做:

const element = React.createElement('li',{
    className:"list"},'React.js')
console.log(element)

React和ReactDOM是如何工作运行的

从上图中我可以看到props中多出了一个className属性,证明我们添加成功了。

这里出现一个问题为什么不用class而是使用className?像

<li class="list">React.js</li>

标签上使用的都是class

答案就是react元素他所对标的DOM元素,而不是HTML,在DOM API中要为创建的DOM元素添加类的话也是使用className,这样二者就更加接近。

const arr = document.createElement("div")
arr.className = "hello" 

ReactDOM

当你尝试创建React元素之后,然后你想要在浏览器上看到它。但是浏览器不理解你创建的React元素。浏览器认为React元素只是一个普通的JS对象。ReactDOM就是将React元素渲染在浏览器上的一个“桥梁”。ReactDOM有许多有用的方法,但是我们最感兴趣的就是render方法。它需要两个参数来描述你需要渲染什么元素和元素需要渲染在哪里

例子:在DOM中渲染上文所述的li元素

const element = React.createElement('li',{
	class:"list"},'React.js')
const root = document.getElementById("root")
ReactDOM.render(element,root)

方法第二个参数是我们想要渲染li元素的位置,或者说我们想把li元素渲染成哪个其他元素的子节点,我们也可以使用body元素,但是这种行为是不推荐的。因为一般document.body元素经常被第三方库操作,我们直接操作document.body可能会出现一些不可预知的问题。

接下来我们使用ReactReactDOM进行构建之前的例子,然后渲染在浏览器页面上:

//main.js
const mainElement = React.createElement('section',{
	className:'js-section'
},
React.createElement("h1",null, "JavaScript Libraries and Frameworks" ),
React.createElement("ul",{
	className:'list-lib-frameworks'
},
React.createElement("li",null,"React.js"),
React.createElement("li",null,"Vue.js"),
React.createElement("li",null,"Angular.js"),
React.createElement("li",null,"Node.js")
)
)
ReactDOM.render(mainElement,document.getElementById("root"))

从上边的例子可以看出如果给render方法提供超过三个参数,那么第三个以及第三个以后的都会被作为children。因此React可以创建一个children的数组,这些新创建的children元素被放进了children数组中。因此第三个参数我们可以使用数组,并且用数组可以简化我们的代码。

const listOfLibAndFrameworks = ['React.js', 'Angular', 'Vue.js',
'Node.js', 'underscore.js'];
const mainElement = React.createElement('section',{
	className:'js-section'
},
React.createElement("h1",null, "JavaScript Libraries and Frameworks" ),
React.createElement("ul",{
	className:'list-lib-frameworks'
},
listOfLibAndFrameworks.map((item,index) => {
	return React.createElement('li',null,item)
})
)
)
ReactDOM.render(mainElement,document.getElementById("root"))

使用数组遍历节省了很多的代码量,但是在浏览器控制台中会发现如下警告:

React和ReactDOM是如何工作运行的

翻译过来就是每个数组子元素都应该有一个独一无二的key属性。那么我们在创建li元素的时候,通过给React.createElement方法第二个参数传递key属性就可以了:

listOfLibAndFrameworks.map((element, index) => React.createElement(``'li'``,
    ``{ key: index }, element))

React中,我们使用数组的时候,如果不加上key属性,就会在控制台发出警告。想要消除这个警告,我们就要加上上述的key属性。它用于React来识别唯一的li以便于重新渲染它,这样会让我们的代码更加高效,因此很推荐加上key值。

总结React在内存中创建React element(JS对象),然后ReactDOMReact创建好的视图元素渲染在浏览器页面上。