react基础01-jsx、渲染虚拟DOM、函数组件和类组件、类组件的三个核心(state、props、ref)
react介绍:
用于动态构建用户界面的JavaScript库,由facebook开源
react特点:
1、声明式
命名式:获取dom,设置属性,等操作,每一步都要亲力亲为
声明式:告诉react想到达到的效果,react去处理
* 大部分ES5的方法都是声明式,例如forEach、map等,只需要会用,不用管内部怎么实现的
2、组件:灵活、复用
3、虚拟DOM:提高性能,高效的diff算法
4、react native编写原生应用
5、单向数据流
vue:Object.defineProperty() 双向响应式数据流
react:redux(观察者模式) 单向响应数据流
react高效的原因:
使用虚拟dom,不总是直接操作页面真实dom
react基本使用:
相关js库:
1、reactjs:react核心库
2、react-dom.js:提供操作dom的react扩展库
3、babel.min.js:解析jsx语法转为js的库
创建虚拟dom的两种方式
1、jsx(js的语法糖,需要通过babel转为js供浏览器识别)
<body> <div id="test"></div> <script src="./js/react.development.js"></script> <script src="./js/react-dom.development.js"></script> <script src="./js/babel.min.js"></script> <script type="text/babel"> /* text/babel 表示这里要写jsx,并且用babel转成js */ const vDOM = ( <h1 id="title" onClick={() => { console.log(13) }} > <span>helloreact</span> </h1> ) ReactDOM.render(vDOM, document.querySelector('#test')) </script> </body>
2、js
<body> <div id="test"></div> <script src="./js/react.development.js"></script> <script src="./js/react-dom.development.js"></script> <script type="text/javascript"> const vDOM = React.createElement( 'h1', // 标签名 { id: 'title' }, // 标签属性 React.createElement('span', {}, 'helloreact') // 内容 ) ReactDOM.render(vDOM, document.querySelector('#test')) </script> </body>
效果都是在页面中插入dom
它俩的区别:
1、虚拟dom本质是Object类型的对象(真实dom也是)
2、虚拟dom比较“轻”,真实dom比较“重”,虚拟dom是react内部使用,不需要那么多的属性
3、虚拟dom最终会被react转化为真实dom
jsx
1、全称:JavaScript XML
2、react定义的一种类似于XML的js扩展语法:js+XML,本质是React.createElement(component, props, ...children)的语法糖
3、作用:用来简化创建虚拟DOM
写法:const ele= <h1>hello jsx</h1>
注意:它不是字符串,也不是html/xml标签。它最终产生的是一个js对象
4、标签名任意:html标签或其它标签
5、标签属性任意:html标签属性或其它
6、基本语法规则
①定义虚拟dom时,不要写引号
②标签中混入js表达式时要用 {}
表达式和语句的区别:
表达式会产生一个值,可以放在任何一个需要值的地方。以下这些都是表达式:
a
a+b
arr.map()
function test() {}
下面这些都是js语句:
if() {}
for() {}
switch() { case: xxx }
③样式的类名指定不要用class,要用className
④内联样式,要用 style={{key: value}} 的形式去写
⑤只能有一个根标签
⑥标签必须闭合
⑦标签首字母
若小写字母开头,则将该标签转为html中同名元素,若html中没有与该标签对应的同名元素,则报警告
若大写字母开头,则渲染对应的组件,如果组件没有定义,则报错
const vDOM = ( <> <h1 id="title" className="orange" style={{ color: '#fff', fontSize: '22px' }} > <span>helloreact</span> </h1> <h1 className="orange"> <span>helloreact</span> </h1> <Add /> </> )
7、babel.js的作用
①浏览器不能直接解析jsx代码,需要babel转换为js代码才能运行
②只要用了jsx,都要加上 type='text/babel' ,声明需要babel处理
渲染虚拟dom(元素)
1、语法:ReactDOM.render(virtualDOM, containerDOM)
2、作用:将虚拟dom元素渲染到页面中的真实容器dom中显示
3、参数说明:
virtualDOM:js或jsx创建的虚拟dom对象
containerDOM:用来包含虚拟dom元素的真实dom元素对象,一般是一个div
函数式组件
function Demo() { console.log(this) // 此处的this是undefined,因为babel编译后开启了严格模式 return <h2>h2</h2> } ReactDOM.render(<Demo />, document.querySelector('#test')) /* render函数在干嘛: 1、react解析组件标签,找到了Demo组件 2、发现组件是使用函数定义的,于是调用该函数,将返回的虚拟dom转为真实dom,插入到#test中 */
tip:严格模式下,函数中的this会指向undefined
'use strict' demo() function demo() { console.log(this) // 函数体处于严格模式下,this会被绑定到undefined }
注意:函数体处于严格模式下,函数内的this才会指向undefined;而函数的调用处于严格模式下,函数内的this还是指向window
function demo() { console.log(this) // window } 'use strict' demo() // 函数的调用处于严格模式下,函数内的this还是指向window
类组件
class Demo extends React.Component { render() { console.log(this) // 指向当前类(Demo组件)的实例对象 Demo {...} return <h1>title-类组件</h1> } } ReactDOM.render(<Demo />, document.querySelector('#test')) /* render函数在干嘛: 1、react解析组件标签,找到了Demo组件 2、发现组件是使用类定义的,于是new出来该类的实例,并调用该实例调用到原型上的render方法 3、将render返回的虚拟dom转为真实dom,插入到#test中 */
类组件三大核心属性1:state
小案例:声明与更改state
class Weather extends React.Component { // constructor调用一次,有几个Weather实例就调用几次 constructor(props) { super(props) this.state = { isHot: true, wind: '风很大' } } // render调用1+n次,1是初始化时,后期每次点击都会触发render以更新dom render() { const { isHot, wind } = this.state return ( <h1 // onClick={this.change} // 通过this查到到原型链上的change方法,点击时从堆中直接调用change,不是通过实例去调用的,所以这里this指向undefined(严格模式指向undefined,非严格模式下函数中this指向window) onClick={this.change.bind(this)} // 通过bind将change中的this由undefined改成this > 今天天气很{isHot ? '炎热' : '凉爽'},{wind} </h1> ) } // change调用n次,点击几次就调用几次 change() { const isHot = !this.state.isHot // this.state.isHot = isHot // state不可直接更改 this.setState({ isHot }) // 只会更改isHot,wind依然保留 } } ReactDOM.render(<Weather />, document.querySelector('#test'))
state的简写方式:
class Weather extends React.Component { state = { isHot: true, wind: '风很大' } // 初始化状态,类中可以通过赋值语句直接将state设置给实例 render() { const { isHot, wind } = this.state return ( <h1 onClick={this.change}> 今天天气很{isHot ? '炎热' : '凉爽'},{wind} </h1> ) } // 自定义方法,直接通过赋值语句给实例身上添加change方法 change = () => { const isHot = !this.state.isHot this.setState({ isHot }) } } ReactDOM.render(<Weather />, document.querySelector('#test'))
总结:
1、组件中render中的this为实例对象
2、组件自定义方法(一般都是作为事件的回调去使用)中this为undefined,如何解决:
第一种:通过bind改变this指向
第二种:箭头函数
** 类中方法的this指向问题
class Person { constructor(name, age) { this.name = name this.age = age } speak() { console.log(this) } } const p = new Person('小明', 20) p.speak() // 实例调用,this指向实例对象 const aa = p.speak aa() // 直接调用,this指向undefined,类中的方法默认开启了局部严格模式
3、state中的数据不能直接修改,如this.state.isHot=isHot,应该使用setState
类组件三大核心属性2:props
<div id="test"></div> <div id="test1"></div> <script src="./js/react.development.js"></script> <script src="./js/react-dom.development.js"></script> <script src="./js/babel.min.js"></script> <script src="./js/prop-types.js"></script> <script type="text/babel"> class Person extends React.Component { render() { const { name, sex, age, speak } = this.props console.log(this) speak && speak() return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age}</li> </ul> ) } } // 对标签属性进行类型、必要性的限制 15.5之前写法:React.PropTypes.string.isRequired Person.propTypes = { name: PropTypes.string.isRequired, sex: PropTypes.string, age: PropTypes.number, speak: PropTypes.func } // 标签属性设置默认值 Person.defaultProps = { sex: '保密', age: 16 } const p = { name: '小明', sex: '男', age: 18 } ReactDOM.render(<Person {...p} />, document.querySelector('#test')) // {...p}这里的{}是表明这里面的 ...p 是js,但js中不允许通过...来展开对象;引入的react+babel允许使用展开运算符展开对象,仅适用于标签属性的传递 ReactDOM.render( <Person name="小红" sex="女" age={12} speak={speak} />, document.querySelector('#test1') ) function speak() { console.log('说话') } </script>
可以将属性限制写在类中:
static propTypes = { name: PropTypes.string.isRequired, sex: PropTypes.string, age: PropTypes.number, speak: PropTypes.func } static defaultProps = { sex: '保密', age: 16 }
类组件中的构造器与props:
构造器函数的这两个作用,第一个,可以在类中直接 state={...} ,这是给实例初始化状态;第二个,为事件处理函数绑定实例,
可以直接写onClick={this.changeWeather.bind(this)}或箭头函数解决
如果写了构造器,一定要在第一行写上super()
构造器的super传不传props?
constructor(props){ super(props) // 如果希望在构造器中通过this访问props,这里就传props进去 console.log(this.props) }
由此可见,类中的constructor能不写就不写吧,没啥用
函数组件使用props(类组件的三大属性state、props、refs,函数组件只能使用props。后期的hooks都会支持)
function Person(props) { const { name, sex, age, speak } = props speak() return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age}</li> </ul> ) } Person.propTypes = { name: PropTypes.string.isRequired, sex: PropTypes.string, age: PropTypes.number, speak: PropTypes.func } Person.defaultProps = { sex: '保密', age: 16 } const p = { name: '小明', speak } ReactDOM.render(<Person {...p} />, document.querySelector('#test')) function speak() { console.log('说话') }
类组件三大核心属性3:refs与事件处理
组件内的标签可以通过ref来标记自己
class Demo extends React.Component { state = { isHot: true } render() { const { state: { isHot }, showInput1, saveInput2 } = this return ( <div> <h1 onClick={() => this.setState({ isHot: !isHot })}> 今天天气很{isHot ? '炎热' : '凉爽'} </h1> {/* ref第一种方式:字符串形式,存在效率问题,未来可能会被移除 */} <input ref="input1" type="text" /> <button onClick={showInput1}>点击打印</button> {/* ref第二种形式:回调形式,推荐将ref的回调函数定义成class的绑定函数 */} <input // ref={(ele) => { // this.input2 = ele // console.log(ele) // 以内联函数的形式定义时,在更新的过程中会执行2次,第一次传入null,第二次传入dom。因为每次渲染时会创建一个新的函数实例,react清空旧的设置新的 // }} ref={saveInput2} // 回调形式推荐写法 onBlur={() => { const { input2 } = this console.log(input2.value) }} type="text" /> </div> ) } showInput1 = () => { const { input1 } = this.refs console.log(input1.value) } saveInput2 = (ele) => { this.input2 = ele console.log(ele) // 当状态改变时,不会触发到这里,因为已经放在实例自身了 } } ReactDOM.render(<Demo />, document.querySelector('#test'))
react推荐使用第三种方式:通过createRef创建ref容器
class Demo extends React.Component { input1Ref = React.createRef() input2Ref = React.createRef() render() { const { input1Ref, input2Ref } = this return ( <div> <input ref={input1Ref} type="text" /> <button onClick={() => console.log(input1Ref.current.value)}> 点击打印 </button> <input ref={input2Ref} onBlur={() => console.log(input2Ref.current.value)} type="text" /> </div> ) } } ReactDOM.render(<Demo />, document.querySelector('#test'))
勿过度使用Refs:当事件的元素正好是需要操作的元素,这里的ref就可以不写了,回调中的参数是事件源,e.target可以获取发生事件的dom对象
<input onBlur={(e) => console.log(e.target.value)} type="text" />
下载插件和启动:
下载插件:
npm i @babel/preset-react webpack-dev-server -D npm i react react-dom
react是react的核心语法,react-dom是用来解析jsx的语法
启动:在原来的webpack搭的架子里执行npm run dev,localhost:9001打开页面
ReactDOM的参数:
ReactDOM.render(<App />, document.getElementById('root'), () => {
console.log('启动react')
})
参数一:需要渲染的节点或组件
参数二:将渲染好的DOM节点放到哪个根节点上
参数三:成功的回调
jsx语法需要注意的地方:
class必须要写成className,因为class是关键字
<label htmlFor='username'></label> label中的for要写成htmlFor,for是循环的关键字
react中组件的创建:
1、组件的名称必须要大写(App),为了区分组件和标签
2、通过ES6的继承实现组件的创建,继承与React.Component
3、组件内部必须要有一个render函数,render函数中必须返回一个jsx语法(jsx中只能有一个根节点,可以写成<></>或<Fragment></Fragment>都不会渲染成dom)
4、组件中constructor是选填的,constructor用来做组件的初始化;存放组件需要的一些状态,如果需要存放必须要写在constructor中的this.state中;如果写了constructor必须要写super(),不写的话this指向不正确
import React, { Fragment } from 'react' // Fragment:react内置组件,用来做容器盒子,不会被渲染到页面上 class App extends React.Component { // constructor是用来做组件的初始化 constructor() { super() // constructor中必须要写super(),不写的话this指向会发生错误 this.state = { name: '小华' } } render() { let { name } = this.state return ( <div className="app"> <h1>App</h1> </div> ) } } export default App
react中的事件:
语法:给元素添加 on事件名 (事件首字母必须要大写,原生中可以小写也可以大写)= {事件函数}
第一种:(需要传参)
<h2 onClick={this.handleClick.bind(this,'wxm')}></h2>
第二种:(不需要传参)
constructor(){ super() this.handleClick=this.handleClick.bind(this) // 初始化 } <h2 onClick={this.handleClick}></h2>
第三种:(少见,ui层和逻辑层未分离)
<h2 onClick={()=>{console.log('xxxxxxxxxx')}}></h2>
注意事项:
1、react中的事件函数不要加(),加了就是自动执行的意思
2、react中的事件函数默认情况下this的指向是undefined,如果需要将this指向为当前组件需要通过bind绑定
3、因为render函数会多次执行,因此bind绑定的时候可以放在constructor中。当需要传参时在render函数内部的事件中bind,不需要传参可以在constructor中进行bind(this.handleClick=this.handleClick.bind(this))
this.setState():
语法:
第一种:
this.setState({ key:value },()=>{})
第二种:
this.setState(()=>({ key:value }),()=>{})
1、在react中,如果需要修改state中的值,必须要通过this.setState()修改
2、this.setState()是异步的
3、this.setState()中的第二个参数是一个回调函数。作用:①验证数据是否修改成功②可以获取数据更新后最新的dom结构
4、只要this.setState()执行了那么render函数就会执行
组件传值:
父传子:
传递:给子组件标签上绑定一个自定义属性,值为需要传递的数据
<One username={this.state.name}></One>
接收:在子组件render函数中通过props接收
let { username } = this.props
子传父:
传递:在子组件中通过 this.props.事件函数 来进行传值
<button onClick={this.handleAdd.bind(this)}>点击发送给父组件</button>
handleAdd() { this.props.toApp('我是从two组件传来的') }
接收:在子组件标签上绑定一个自定义属性,值为需要接收参数的函数
<Two toApp={this.handle2.bind(this)}></Two> handle2(value) { this.setState({ towValue: value }) }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· 地球OL攻略 —— 某应届生求职总结