React和ReactDOM是如何工作运行的
React
和ReactDOM
是如何工作运行的?
我的技术栈是vue,由于公司的需要,需要学习react。但是在使用react写了一些项目之后,对react的理解还是很浅薄,使用起来虽然可以实现需求,但是内部是如何运行的一无所知,所以在网上找了一篇文章进行翻译加上自己的理解总结成这篇博客,希望能帮助到有需要的人。
当你使用React的时候,你可能会使用JSX
进行构建应用程序。JSX
是一个基于标签的JavaScript
语法,它的语法规则看起来像HTML
。React
元素(React element
)是在JSX
和继续学习React
之前需要掌握的最小和最基本的单位。换句话说,在学习JSX
和继续深入学习React
之前,我们要对React
元素有基本的了解。
注意: 为了了解React
在浏览器中是如何运行的,我们需要两个库:React
和ReactDOM
。React
库是负责创建视图的,而ReactDOM
库是负责浏览器中渲染UI的。
React: https://cdnjs.com/libraries/react ReactDOM: https://cdnjs.com/libraries/react-dom
引入这两个库在你的主要JavaScript
文件之前。在了解React
如何运行的同时,我们需要使用React
和ReactDOM
创建一个小型应用。简单起见,这个应用只包含Index.html和main.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
,一个是ul
,h1
的子节点是文本节点。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 API
是JavaScript
用来操作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
element
是React
中最小的单位。React
元素是在内存中描述DOM
元素的JavaScript
对象。我们可以通过使用React
的方法createElement
**来创建一个React
元素。
语法:
React.createElement(type,[props],[...children])
参数:
type
:type
就是你想要创建元素的类型,如div
,p
,li
等等。type
也可以是其他的React
元素。
props
:它是一个JavaScript
对象,包含构建DOM需要的属性和数据。
children
orcontent
:React
元素可能会有children
或者content
来显示其他的嵌套的元素,是节点的内容。
例子: 如何创建li
元素
const element = React.createElement("li",null,'React.js')
console.log(element)
在上述例子中,我们给方法里传递了三个参数,如下所示:
li: 它代表着我们创建React
元素的类型,我们也能从上述log看出type:"li"
null: 我们没有为元素定义任何的属性,但是我们可以添加像className
属性,但是在上边的例子中我们没有那么做。因此我们传递了null值。
React.js: 我们传入了文本作为元素的children
,children
不仅可以是文本还可以是其他元素。
在渲染期间,也就是render
期间,React
会将React
元素转变成真实DOM
。
<li>React.js</li>
并且如果你想要给li
元素添加一个类,可以像下边这样做:
const element = React.createElement('li',{
className:"list"},'React.js')
console.log(element)
从上图中我可以看到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
可能会出现一些不可预知的问题。
接下来我们使用React
和ReactDOM
进行构建之前的例子,然后渲染在浏览器页面上:
//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"))
使用数组遍历节省了很多的代码量,但是在浏览器控制台中会发现如下警告:
翻译过来就是每个数组子元素都应该有一个独一无二的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对象),然后ReactDOM
将React
创建好的视图元素渲染在浏览器页面上。
转载自:https://juejin.cn/post/7263016769250312253