React开发依赖,三个库的理解
开发React必须依赖三个库:
- react:包含react所必须的核心代码
- react-dom:react渲染在不同平台所需要的核心代码
- babel:将jsx转换成React代码的工具 这三个库是各司其职的,目的就是让每一个库只单纯做自己的事情:
- 在React的0.14版本之前是没有react-dom这个概念的,所有功能都包含在react里。
- 为什么要进行拆分呢?原因就是react-native。
- react包中包含了react和react-native所共同拥有的核心代码。
- react-dom针对web和native所完成的事情不同:
- web端:react-dom会将jsx最终渲染成真实的DOM,显示在浏览器中。
- native端:react-dom会将jsx最终渲染成原生的控件(比如Android中的Button,iOS中的UIButton)。 babel是什么呢? 前端使用非常广泛的编辑器、转移器。
- 比如当下很多浏览器并不支持ES6的语法,但是确实ES6的语法非常的简洁和方便,我们开发时希望使用它。
- 那么编写源码时我们就可以使用ES6来编写,之后通过Babel工具,将ES6转成大多数浏览器都支持的ES5的语法。 React和Babel的关系:
- 默认情况下开发React其实可以不使用babel。但是前提是我们自己使用
React.createElement
来编写源代码,它编写的代码非常的繁琐和可读性差。
- 那么我们就可以直接编写jsx(JavaScript XML)的语法,并且让babel帮助我们转换成
React.createElement
。 所以,我们在编写React代码时,这三个依赖都是必不可少的。
那么,如何添加这三个依赖:
- 直接CDN引入
react依赖: <script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script> react-dom依赖:<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script> babel依赖:<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
如果需要加载指定版本的 react
和 react-dom
,可以把 17
替换成所需加载的版本号。这里有一个crossorigin的属性,这个属性的目的是为了拿到跨域脚本的错误信息。
- 通过React的脚手架。
npm install create-react-app -g
npx create-react-app my-app cd my-app npm start
JSX语法
JSX是什么?
- JSX是一种JavaScript的语法扩展(eXtension),也在很多地方称之为JavaScript XML,因为看起就是一段XML语法。
- 它用于描述我们的UI界面,并且其完成可以和JavaScript融合在一起使用。
- 它不同于Vue中的模块语法,你不需要专门学习模块语法中的一些指令(比如v-for、v-if、v-else、v-bind)。
JSX的书写规范
- JSX的顶层只能有一个根元素,所以我们很多时候会在外层包裹一个div原生。
- 为了方便阅读,我们通常在jsx的外层包裹一个小括号(),这样可以方便阅读,并且jsx可以进行换行书写。
- JSX中的标签可以是单标签,也可以是双标签。注意:如果是单标签,必须以/>结尾。
JSX嵌入变量
- 当变量是Number、String、Array类型时,可以直接显示。
- 当变量是null、undefined、Boolean类型时,内容为空。
- 如果希望可以显示null、undefined、Boolean,那么需要转成字符串;
- 转换的方式有很多,比如toString方法、和空字符串拼接,String(变量)等方式;
- 对象类型不能作为子元素。
JSX嵌入表达式
- 运算表达式
- 三元运算符
- 执行一个函数
JSX中class属性的绑定
可以发挥自己的想象,react中className的绑定是很灵活的。
- 字符串拼接绑定
this.state={ isFlag: true } <p className={"aa bb "+(this.state.isFlag? "active":"")}>pppp</p>
- 数组绑定
this.state={ isFlag: true, classs: ["a","b","c"] } <p className={this.state.classs.join(" ")}>oooo</p>
JSX中style属性的绑定
- 通过键值对绑定即可。
this.state={ isFlag: true, styles: { color: "red", fontSize: "30px" } } <p style={{...this.state.styles,marginTop: this.state.isFlag? "200px":""}}>oooo</p>
React事件绑定
React 事件的命名采用小驼峰式(camelCase),而不是纯小写。我们需要通过{}传入一个事件处理函数,这个函数会在事件发生时被执行。
类组件中事件中的this绑定
- 在调用方法的时候利用bind绑定this
return ( <div> {/*这里的this表示的是render环境下的this,所以就是app*/} <button onClick={this.handleClick.bind(this)}>请点我一下!</button> </div> )
- 使用 ES6 class fields 语法,就是给类定义属性,让该属性等于一个箭头函数
btnClick = () => { console.log(this); console.log(this.state.message); }
- 事件监听时传入箭头函数(推荐): 在使用方法时,用一个箭头函数将其包裹,返回该函数
return ( <div> {/*这里的箭头函数中的this表示的是render环境下的this,所以就是app*/} <button onClick={() => { this.handleClick() }}>请点我一下!</button> </div> )
- 直接在构造函数中利用bind显示的绑定this
constructor(props) { super(props); this.handleClick = this.handleClick.bind(this) }
函数参数的传递
在执行事件函数时,有可能我们需要获取一些参数信息:比如event对象、其他参数。
- 如果只需要event对象 如果函数不需要传递参数,但是需要事件对象。则直接写(箭头函数绑定this的方法除外),由于我们传入的事件函数是系统内部调用的。所以会传入一个事件对象,我们就可以拿到。
- 如果只需要我们自己的参数 如果需要传参,但是不需要事件对象,则直接传递参数,没有事件对象什么事。
- 如果事件对象event和自定义参数都需要 获取的时候事件对象都是最后一个参数(通过bind绑定的this),而通过箭头函数绑定this,则在什么位置传入,他就在什么位置获取。
<button onClick={(e) => {this.handleClick(e,"pp")}}>anniu</button> // 传入的位置就是event <button onClick={this.handleClick.bind(this,"pp")}>anniu</button> // 系统自己传入,会自动作为最后一个参数。
条件渲染
React 中的条件渲染和 JavaScript 中的一样,使用 JavaScript 运算符 if
或者条件运算符去创建元素来表现当前的状态,然后让 React 根据它们来更新 UI。
- 通过三目运算符
{ this.state.isFlag? <div>正确</div>:<div>错误</div> }
- 通过逻辑与
{ this.state.isFlag&&<div>正确</div> }
- 通过if语句
实现vue中v-show的效果。我们知道v-show只是控制了display
属性,来实现元素的显示或者隐藏。
this.state = { isFlag: true } <div style={{display: this.state.isFlag ? "block" : "none" }}> 元素的显示或者隐藏 </div>
列表渲染
所有的列表渲染操作之后都需要去调用map来做渲染。
- 通过数组filter方法过滤后,需要调用map,来做渲染映射。
this.state = { list: [ "zh","llm","zhvllm" ] } { this.state.list.filter(item => item[0]==='z').map(item => { return <div>{item}</div> }) }
- 通过数组slice方法截取后,需要调用map, 来做渲染映射。
this.state = { list: [ "zh","llm","zhvllm" ] } { this.state.list.slice(0, 2).map(item => { return <div>{item}</div> }) }
列表渲染绑定key
let message = null if(this.state.isFlag) { message = <div>"true"</div> }else { message = <div>"false"</div> }
JSX的本质
实际上,jsx 仅仅只是 React.createElement(component, props, ...children) 函数的语法糖。所有的jsx最终都会被转换成React.createElement的函数调用。
createElement需要传递三个参数:
- type: 当前ReactElement的类型。
- 如果是标签元素,那么就使用字符串表示 “div”。
- 如果是组件元素,那么就直接使用组件的名称。
- config
- 所有jsx中的属性都在config中以对象的属性和值的形式存储。
- children
- 存放在标签中的内容,以children数组的方式进行存储。 我们可以通过这个网站:babel,来查看jsx是如何转化为React.createElement。
虚拟DOM
React.createElement 最终创建出来一个 ReactElement对象。
这个ReactElement对象是什么作用呢?
React利用ReactElement对象组成了一个JavaScript的对象树。它即是虚拟DOM (Virtual DOM)。
<script type="text/babel"> class ElHow extends React.Component { constructor(props) { super(props) this.state={ } } render () { const elementObject=React.createElement("div",null, /*#__PURE__*/React.createElement("div",null, /*#__PURE__*/React.createElement("p",null,"\u6587\u672C")), /*#__PURE__*/React.createElement("div",null, /*#__PURE__*/React.createElement("input",{ type: "text" }))); console.log(elementObject) return elementObject } } ReactDOM.render(<ElHow />,document.getElementById('root')) </script>
网络异常,图片无法展示
|
为什么要采用虚拟DOM,而不是直接修改真实的DOM呢?
- 很难跟踪状态发生的改变:原有的开发模式,我们很难跟踪到状态发生的改变,不方便针对我们应用程序进行调试。
- 操作真实DOM性能较低:传统的开发模式会进行频繁的DOM操作,而这一的做法性能非常的低。
一个购物车小案例
- 注意要维持state数据的不可变性。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <title>Document</title> <style> table { border-collapse: collapse } </style> </head> <body> <div id="root"></div> <script type="text/babel"> class ElHow extends React.Component { constructor(props) { super(props) this.state={ list: [{ name: "nodejs", id: 1, date: "2006-9", price: 85, num: 1, },{ name: "react", id: 2, date: "2006-2", num: 1, price: 59 },{ name: "vue", id: 3, date: "2008-10", num: 1, price: 39 },{ name: "javascript", id: 4, date: "2006-3", price: 128, num: 1 }] } } handleDown=(index) => { // console.log(index) this.setState({ list: this.state.list.map((item,indey) => { if(index==indey&&item.num>1) { return { ...item, num: item.num-1 } } return item }) }) } handleUp=(index) => { this.setState({ list: this.state.list.map((item,indey) => { if(index==indey) { return { ...item, num: item.num+1 } } return item }) }) } handleRemove=(index) => { this.setState({ list: this.state.list.filter((item,indey) => index!=indey) }) } render () { let total=0; this.state.list.forEach(item => { total+=(item.num*item.price) }) return ( <div> { !this.state.list.length? <div>购物车为空~</div>: ( <div> <table border="1"> <thead> <tr> <th>编号</th> <th>书籍名称</th> <th>出版日期</th> <th>价格</th> <th>购买数量</th> <th>操作</th> </tr> </thead> <tbody> { this.state.list.map((item,index) => { return ( <tr key={item.id}> <td>{item.id}</td> <td>《{item.name}》</td> <td>{item.date}</td> <td>¥{item.price}</td> <td> <button disabled={item.num===1} onClick={(e) => {this.handleDown(index)}} >-</button> {item.num} <button onClick={(e) => {this.handleUp(index)}} >+</button> </td> <td> <button onClick={(e) => {this.handleRemove(index)}} >移除</button></td> </tr> ) }) } </tbody> </table> <div>总价钱:¥{total}</div> </div> ) } </div> ) } } ReactDOM.render(<ElHow />,document.getElementById('root')) </script> </body> </html>