React框架的基本运行原理与组件定义方式
React框架的基本运行原理
React的本质是内部维护了一套虚拟DOM树,这个虚拟DOM树就是一棵js对象树,它和真实DOM树是一致的,一一对应的。
当某一个组件的state发生修改时,就会生成一个新的虚拟DOM,让它和旧的虚拟DOM通过Diff算法进行对比,生成一组差异对象。
然后遍历差异对象,将修改更新到真实的DOM树上。
React的三大特性:JSX语法糖,虚拟DOM, Diff算法
它们之间的关系可以简单理解为:
1.JSX语法糖转换:在webpack进行编译时,JSX语法糖 被转换成 React.createElement的React的API调用,React.createElement的React的API调用会得到一个js对象。
2.生成抽象语法树:通过将这棵JSX树中的每个元素 转换成相应的 React.createElement 的API调用,最终得到一棵js对象树, 这棵js对象树就是虚拟DOM树。
3.使用Diff算法对这棵树进行比较运算,得出要更新的虚拟DOM差异对象,然后遍历这些差异对象,将修改更新到真实的DOM中去。
Diff算法对这棵js对象树的diff分三层:
tree diff :树层级的对比
component diff :组件层级的对比
element diff: 元素层级的对比
key: key属性,把页面上的DOM节点与虚拟DOM节点做一层关联关系。

DOM与虚拟DOM的概念
原来的DOM是浏览器中的JS提供的功能,所以我们只能使用浏览器提供的API,如:getElementById进行操作DOM对象。
虚拟DOM是程序员手动模拟的,类似与浏览器中的DOM。
虚拟DOM出现的原因
是为了提升浏览器的渲染性能,避免因为某一部分DOM元素的更新,刷新整棵DOM树。所以使用js创建一个js对象来模拟一个DOM对象。
一个js对象中包含多个子对象,就构成了一棵虚拟的DOM树。
通过前后比较2棵虚拟DOM树的差异,将得到的差异对象进行遍历批量更新,从而真实的DOM得到了页面更新。
React内部已经实现了虚拟DOM, 所以通过React框架开发的web页面,默认就有了这个能力。
JSX语法糖
在React中,不能像Vue中那样直接写html元素,要通过react的API创建元素React.createElelement()
React.createElement有三个参数,并返回一个dom对象,也就是js对象
参数一:标签名字符串
参数二:属性对象
参数三及其更多:子元素
JSX的原理是什么?
JSX是符合XML规范的JS语法
JSX只是一个语法糖,它内部运行的时候是把类似于HTML这样的标签代码转换为React.createElement的形式。
需要安装:npm i babel-preset-react -D
webpack没法编译jsx代码,它会找到babel进行编译这个代码,babel发现它是react内的语法,就会调用babel-preset-react插件进行解析
babel碰到<>按照html的语义, 使用React.createElement进行解析
碰到{}会按照js进行解析, {}中只能存放一个带返回值的js语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | /* React: 创建,修改React组件,管理组件的生命周期 ReactDOM: 操作真实Dom, 将虚拟Dom渲染到真实的Dom之上。 */ import React from "react" ; import ReactDOM from "react-dom" ; /* 在React中,不能像Vue中那样直接写html元素,要通过react的API创建元素React.createElelement() React.createElement有三个参数,并返回一个dom对象,也就是js对象 参数一:标签名字符串 参数二:属性对象 参数三及其更多:子元素 */ /* JSX的原理是什么? JSX是符合XML规范的JS语法 JSX只是一个语法糖,它内部运行的时候是把类似于HTML这样的标签代码转换为React.createElement的形式。 需要安装:npm i babel-preset-react -D webpack没法编译jsx代码,它会找到babel进行编译这个代码,babel发现它是react内的语法,就会调用babel-preset-react插件进行解析 */ /* babel碰到<>按照html的语义, 使用React.createElement进行解析 碰到{}会按照js进行解析, {}中只能存放一个带返回值的js语句 */ var list = [] for ( let i = 0; i < 10; i++) { var p = <p key={i} >这是 for 循环生成的p标签</p> list.push(p) } var myTitle = "这是标题的title" var h2D = <div> 这是一个jsx的h2标题 <h1 title={ myTitle }>JSX真好用</h1> { list } { /* 这是jsx中的注释 */ } </div> var divD = React.createElement( "div" , {title: "这是一个div" , id: "rootSub" }, "这是一个React创建的div" , h2D) /* 将react元素渲染到页面对应的位置上。 */ ReactDOM.render(divD, document.getElementById( "root" )) |
React组件定义方式
JS中定义对象的方式有2种:
1.使用构造函数
2.使用类
构造函数组件
js文件内部的构造函数组件
1 2 3 4 5 6 7 8 9 | /* React中,构造函数就是一个最基本的组件, 使用时把构造函数的名称当做html标签名使用。 React自定义的组件必须是大写字母开头,小写字母编译器默认是浏览器提供的组件。会从浏览器中去查找。 */ function Hello() { return <div> 这是使用Hello构造函数 创建的基本组件Hello </div> } |
jsx文件内的构造函数组件
1 2 3 4 5 6 7 8 9 | import React from "react" function World(props) { return <div> 接收到的参数:{props.name} <hr></hr> </div> } export default World |
可以通过对象参数扩散的方式进行批量传参
1 2 | { /* 如果传递的参数是个对象,可以进行对对象进行属性扩散进行批量传参 */ } <World {...person}></World> |
类组件
在类中定义的方法和利用构造函数创建对象时将方法放在构造函数protoType中,在内存中的位置是一样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | // 1.构造函数, 创建对象 function Person(name, age) { this .name = name; this .age = age; } //Person.prototype中定义的方法和属性是是定义在当前对象的protoType中的(A区域) Person.prototype.say = function () { console.log( 'hello' ) } Person.prototype.myId = 220 var p1 = new Person( 'jack' , 12); console.log(p1); // 2.类,创建对象, 类的本质也是一个构造方法实现的 class Per { //构造器, 调用new方法创建对象时,会调用个constructor constructor(name, age){ this .name = name this .age = age } //es6 定义的方法定义方式,类中的方法也是定义在当前对象的protoType中的(A区域) say() { console.log( '这是Person中的hello方法' ) } //class中的static方法和变量,是放在了constructor方法内部的原型对象中(B区域) static myId = 230; static myIdGet = function () { console.log( 'myIdGet' ) } } var p2 = new Per( 'jack22' , 122); console.log(p2); 类的重要特性:封装,继承,多态, // 3.类的重要特性:封装,继承,多态, //继承:实现功能的复用 class Chinese extends Per { constructor(address, sex, name, age){ super (name, age) this .address = address this .sex = sex } } var c1 = new Chinese( '北京' , '男' , 'jack' , 12) console.log(c1); Chinese.myIdGet(); //多态:父类中定义一个抽象空方法,不同的子类进行不同的重写实现 class Animal { say() { } } class Dog extends Animal { say(){ console.log( "wang wang" ); } } class Cat extends Animal { say(){ console.log( "miao miao" ); } } |
函数组件与类组件的区别
函数组件内部没有state, 只有从外部传入的props
类组件内部有state和props, 并且通过setState更新数据可以刷新UI界面
结论:
1.函数组件是无状态组件,类组件是有状态组件
2.类组件是一个组件模板,它有生命周期。函数组件没有生命周期
如何选择使用?
1.如果组件需要保存私有状态,并根据不同的状态执行不同的逻辑,那么使用类组件。
2.如果组件只用于UI展示,那么就用函数组件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | import React from "react" ; import ReactDOM from "react-dom" ; var h1Dom = React.createElement( "h2" , {title: "hello, jsx的h1" }, '这是jsx的子元素' ) //1.构造函数组件 的参数需要在参数中进行显示声明 function Hello(props) { return <div> Hello 构造函数 组件:{props.name} - {props.age} </div> } // 2.通过继承React.Component类,就得到一个类组件模板 // 类组件的参数 内部会将参数自动包装到props的内部 class Hello2 extends React.Component { constructor(props) { super (props) //在constructor中是不能直接通过this.props获取参数的,如果想获取参数,需要显示的在constructor中添加参数 console.log( this .props) console.log( '---------' ) console.log(props) //props外部传参,不能进行修改 //state:私有状态变量,调用setState修改可以更新UI this .state = { msg: 'des' , state: 10 } } render() { return <div> 这是一个React.Component的组件:{ this .props.name} - { this .props.age} </div> } } /* 函数组件与类组件的区别 函数组件内部没有state, 只有从外部传入的props 类组件内部有state和props, 并且通过setState更新数据可以刷新UI界面 结论: 1.函数组件是无状态组件,类组件是有状态组件 2.类组件是一个组件模板,它有生命周期。函数组件没有生命周期 如何选择使用? 1.如果组件需要保存私有状态,并根据不同的状态执行不同的逻辑,那么使用类组件。 2.如果组件只用于UI展示,那么就用函数组件。 */ ReactDOM.render(<div> {h1Dom} <Hello name= "jack" age={20}></Hello> <Hello2 name= "jack" age={20}></Hello2> </div>, document.getElementById( "root" )) |
函数组件与类组件的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 1.类组件基本使用 class CommentList extends React.Component { constructor(props) { super (props) this .state = { list : [ {user: 'zhang san' , content: '呵呵呵' }, {user: 'zhang san2' , content: '呵呵,55' }, {user: 'zhang san3' , content: '呵呵呵kk' }, {user: 'zhang san4' , content: '呵呵呵dfgfd' }, {user: 'zhang san5' , content: '呵呵呵fff' }, {user: 'zhang san6' , content: '呵呵呵dddd' }, {user: 'zhang san7' , content: '呵呵呵3333' }, ] } } render(){ return <div> { this .state.list.map((item,index) => { return <div key={index}> <h3>姓名:{item.user}</h3> <p>内容:{item.content}</p> </div> }) } </div> } } ReactDOM.render(<div> <CommentList></CommentList> </div>, document.getElementById( "root" )) |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)