专业编程基础技术教程

网站首页 > 基础教程 正文

react基础

ccvgpt 2024-08-15 20:28:04 基础教程 8 ℃


基础认识

  • 官网
  • 特点
  • 声明式编程
  • 组件化
  • 支持客户端和服务端渲染
  • 高效
  • 虚拟DOM,不总是直接操作DOM,只是减少DOM的操作,操作虚拟DOM不会对页面进行重绘,当渲染的时候,才会进行渲染
  • DOM Diff 算法,最小化页面重绘,就是当页面变化时,通过计算那部分需要重绘,只重绘当前部分,减少页面的重绘区域
  • 单向数据流
  • js文件
  • react.js:react的核心库
  • react-dom.js:提供操作DOM的react扩展
  • babel.js:解析jsx语法
	<div id="app"></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">
 // 创建虚拟DOM元素对象
 let vDom = <h1>react hello</h1>
 // 将虚拟DOM渲染到页面真实DOM容器中
 ReactDOM.render(vDom,document.getElementById('app'))
 
 </script>
复制代码

JSX

  • react提供了创建虚拟DOM的方法
  • 虚拟DOM对象最终都会被react转换为真实的DOM
  • 编码时,只需要操作react的虚拟DOM相关数据,react会转换为真实DOM变化而更新界面
// react直接操作和JSX操作对比
<script type="text/javascript">
 const msg = 'MSG content'
const msgId = 'App'
// 创建虚拟DOM
// const VDom1 = React.createElement('标签名',{id:'xxx'},'内容')
const vDom1 = React.createElement('h1', {
 id: msgId.toLowerCase()
}, msg.toLowerCase())
// 渲染虚拟DOM
ReactDOM.render(vDom1, document.getElementById('app1'))
</script>
<script type="text/babel">
 // 创建虚拟DOM 
 const vDom2 = <h2 id={msgId.toUpperCase()}>{msg.toLowerCase()}</h2>;
// 渲染虚拟DOM 
ReactDOM.render(vDom2, document.getElementById('app2'))
</script>
复制代码
  • 动态渲染一个列表
  • 使用数组的Map函数返回所需要的列表内容
let arrs = [111,222,333,444]
// 创建虚拟DOM 
const ul = (
 <ul>
 {
 arrs.map((name,index) => <li key={index}>{name}</li>)
 }
 </ul>
)
// 渲染虚拟DOM 
ReactDOM.render(ul, document.getElementById('app1'))
复制代码

组件

  • 工厂函数创建组件
  • 使用工厂函数的效率比使用class高,因为工厂函数中不需要创建一系列的对象之类的
  • 当组件有状态(state)的时候就不适合使用了
// 工厂函数创建组件
function MyCom(){
 return <h2>工厂函数创建组件</h2>			
}
 	
// 渲染组件标签
// <MyCom />必须这么写
// <MyCom>这么写是错误的
ReactDOM.render(<MyCom />,document.getElementById('app'))
复制代码
  • class创建组件
// class定义组件
class MyCom2 extends React.Component{
 render(){
 return <h3>class定义组件</h3>
 }
}
// 渲染组件标签
// <MyCom2 />必须这么写
// <MyCom2>这么写是错误的
ReactDOM.render(<MyCom2 />,document.getElementById('app'))
复制代码

组件三大属性

state

  • state是组件对象的最重要的属性之一,值是一个对象,这个对象可以包含多个数据
  • 组件被称为状态机,通过更新组件的state来更新对应的页面显示,就是通过state来控制组件的重新渲染(重绘)
  • 初始化状态
constructor(props){
 super(props)
 this.state = {
 stateProp1:value1,
 stateProp2:value2
 }
}
复制代码
  • 读取某个状态值
this.state.statePropertyName
复制代码
  • 更新状态:组件重新渲染
this.setState({
 stateProp1:newValue1,
 stateProp2:newValue2
})
复制代码
  • 案例:点击切换内容
//定义组件
class Like extends React.Component{
 constructor(props){
 super(props)
 //初始化状态state
 this.state = {
 isShow:false
 }
 
 //将新增方法中的this强制绑定为组件对象
 this.handleClick = this.handleClick.bind(this)
 }
 
 // 新添加的方法:内置的this默认不是组件对象,是一个undefined
 handleClick(){
 //获取原始状态并取反
 let isShow = !this.state.isShow
 // 设置状态
 this.setState({isShow})
 }
 
 // React.Component中本身就具有render函数,在class中只是重写组件类的方法render,所以这里的this指向没有问题
 render(){
 //获取状态
 // const isShow = this.state.isShow
 const {isShow} = this.state
 //onClick是react的,区分原生的onclick
 // this.handleClick.bind(this)也可以,但是这种效率比较低,因为每次render都会执行一次bind进行绑定,而在constructor中只会在初始化的时候绑定一次
 return <h1 onClick={this.handleClick}>{isShow ? '我是帅哥' : '帅哥是我'}</h1>
 }
 
}
//渲染组件
ReactDOM.render(<Like/>,document.getElementById('app'))
复制代码

props

react基础

  • 案例:定义一个显示个人信息的组件
  • 姓名必须指定
  • 性别默认为男
  • 年龄默认为18
//创建组件
/* function Person(props) {
	return (
 	<ul>
 	<li>姓名:{props.name}</li>
 <li>性别:{props.sex}</li>
 <li>年龄:{props.age}</li>
 </ul>
 )
} */
// class创建
class Person extends React.Component{
 render(){
 return (
 <ul>
 <li>姓名:{this.props.name}</li>
 <li>性别:{this.props.sex}</li>
 <li>年龄:{this.props.age}</li>
 </ul>
 )
 }
}
		
//属性默认值
Person.defaultProps = {
 sex:'man',
 age:18
}
//属性的类型和必填
Person.propTypes = {
 name:PropTypes.string.isRequired,
 age:PropTypes.number
}
const p1 = {
 name:'nordon',
 sex:'man',
 age:22
}
// 渲染组件
// ReactDOM.render(<Person name={p1.name} sex={p1.sex} age={p1.age} />,document.getElementById('app'))
ReactDOM.render(<Person {...p1} />,document.getElementById('app'))
const p2 = {
	name:'lisan'
}	
ReactDOM.render(<Person name={p2.name} age={19}/>,document.getElementById('app2'))
复制代码

refs与事件处理

  • 案例:获取input中的值
//定义组件
class MyInput extends React.Component {
			
 constructor(props){
 super(props)
 this.handleClick = this.handleClick.bind(this)
 this.handleBlur = this.handleBlur.bind(this)
 }
 handleClick(){
 let input1 = this.refs.inputValue
 console.log(input1.value)
 console.log(this.newInput.value)
 }
 handleBlur(event){
 console.log(event.target.value)
 }
 render (){
 return (
 <div>
 <input type="text" ref="inputValue" /> 
 <input type="text" ref={inputValue => this.newInput = inputValue } /> 
 <button onClick={this.handleClick}>获取值</button> 
 <input type="text" placeholder="失去焦点获取值" onBlur={this.handleBlur} />
 </div>
 )
 }
}
		
//渲染组件
ReactDOM.render(<MyInput/>,document.getElementById('app'))
复制代码

ToDolist

  • 简单的tdolist案例
  1. 拆分组件
  2. 实现静态组件
  3. 仅仅只是静态页面,没有动态数据和交互
  4. 实现动态组件
  5. 实现初始化数据的动态显示
  6. 实现交互功能
// 定义组件
// 组件状态的更改必须在当前组件中进行
class App extends React.Component{ 
 constructor(props){
 super(props)
 this.state = {
 todos:['111','222','333']
 }
 this.addTodo = this.addTodo.bind(this)
 }
 addTodo(todo){
 let {todos} = this.state
 todos.unshift(todo)
 this.setState({
 todos
 })
 }
 render(){ 
 const {todos} = this.state
 return (
 <div>
 <h1>this is todolist demo</h1>
 <Add count={todos.length} addTodo={this.addTodo} />
 <List todos={todos} />
 </div>
 ) 
 }
}
class Add extends React.Component{ 
 constructor(props){
 super(props)
 this.handleAdd = this.handleAdd.bind(this)
 }
 handleAdd(){
 // console.log(this.refs.content.value);
 // <input type="text" ref="content" /> 
 // console.log(this.newTodos.value);
 const data = this.newTodos.value
 if(!data){
 return false;
 }
 this.newTodos.value = ''
 this.props.addTodo(data)
 }
 render(){
 return (
 <div>
 <input type="text" ref={inputValue => this.newTodos = inputValue} /> 
 <button onClick={this.handleAdd}>按钮#{this.props.count + 1}</button>
 </div>
 )
 }
}
Add.propTypes = {
 count:PropTypes.number.isRequired,
 addTodo:PropTypes.func.isRequired
}
class List extends React.Component{
 render(){
 let {todos} = this.props
 return (
 <div>
 <ul>
 {todos.map( (item,index) => <li key={index}>{item}</li> )}
 </ul>
 </div>
 )
 }
}
// name:PropTypes.string.isRequired,
List.propTypes = {
 todos:PropTypes.array.isRequired
}
// 渲染组件
ReactDOM.render(<App/>,document.getElementById('app'))
复制代码

表单

  • 通过三种当时获取表单的数据
  • 包含表单的组件分类
  • 受控组件:表单项输入数据能够自动收集成状态,案例中的age字段
  • 非受控组件:需要时才手动读取表单输入框中的数据,案例中的username和password字段
  • 大部分推荐使用受控组件,因为其更符合react的思想,不需要进行DOM的操作,而且react也不推荐过多的使用refs
class Login extends React.Component {
 constructor(props){
 super(props)
 this.state = {
 age:18
 }
 this.handleSubmit = this.handleSubmit.bind(this)
 this.handleChange = this.handleChange.bind(this)
 }
 handleSubmit(event){
 event.preventDefault();
 // 通过旧的refs获取username
 const username = this.refs.username.value
 // 通过新的refs获取username
 const pwd = this.pwd.value
 // 通过状态获取age
 const {age} = this.state
 console.log(username,pwd,age);
 }
 handleChange(event){
 // 由于原生的onchange事件并不是真的在change时触发事件,而是在失去焦点的时候才会触发change事件
 // react在onChange事件做了优化,会在change的时候就触发事件
 const age = event.target.value
 this.setState({
 age
 })
 }
 render(){
 return (
 <form action="" method="get" onSubmit={this.handleSubmit}>
 <p>
 username: <input type="text" ref="username" />
 </p>
 <p>
 password: <input type="password" ref={input => this.pwd = input} />
 </p>
 <p>
 age: <input type="number" value={this.state.age} onChange={this.handleChange} />
 </p>
 <p>
 <input type="submit" value="login" />
 </p>
 </form>
 )
 }
}
ReactDOM.render(<Login/>,document.getElementById('app'))
复制代码

组件生命周期

  • 生命周期理解
  • 就是一个组件对象从创建到结束的一个过程,在这个过程组件对象会经历特定的阶段,每个特定的阶段都会对应一个相应的勾子函数
  • 勾子函数本质就是生命周期的回调函数,在组件的生命周期特定时刻进行回调
  • React.Component已经定义好了一系列的勾子函数,若是需要在特定的时间节点做一些事情,可以重写特定的勾子函数,在勾子中实现自己的逻辑功能
  • 生命周期
  • 组件有三个生命周期状态
  • Mount:插入真实DOM,其对应的勾子函数为:componentWillMount()和componentDidMount()
  • Update:重新渲染,其对应的勾子函数为:componentWillUpdate()和componentDidMount()
  • Unmount:销毁真实DOM,其对应的勾子函数为:componentWillUnmount()
  • 生命周期流程
  • 首次初始化渲染显示:ReactDOM.render()
  • constructor():创建对象初始化state
  • componentWillMount():将要插入回调
  • render():插入虚拟DOM回调
  • componentDidMount():插入完成回调
  • 每次更新state:this.setState()
  • componentWillUpdate():将要更新回调
  • render():更新、重新渲染
  • componentDidUpdate():更新完成回调
  • 移除组件:ReactDOM.unmountComponentAtNode()
  • componentWillUnmount():组件将要销毁时的回调
  • 常用勾子
  • render():初始化渲染和更新都会调用
  • componentDidMount():开启监听、ajax请求等
  • componentWillUnmount():做一些收尾的工作,清除定时器等
  • 案例
  • 实现一个组件
  • 让文本实现显示/隐藏动画
  • 切换时间2s
  • 点击按钮 从界面中移除
// 定义组件 
class Life extends React.Component{
 constructor(props){
 super(props)
 this.state = {
 opacity:1,
 color:`#f0f`
 }
 this.cancelTime = this.cancelTime.bind(this)
 }
 componentDidMount(){
 // 定时器作用域问题
 // 1. 通过bind解决
 // 2. 箭头函数
 this.timer = setInterval( function() {
 let {opacity} = this.state
 opacity -= 0.1
 //不能使用opacity === 0
 // 因为js的计算存在误差
 if(opacity <= 0){
 opacity = 1
 }
 this.setState({
 opacity
 })
 }.bind(this),200)
 }
 cancelTime(){
 // 移除组件
 ReactDOM.unmountComponentAtNode(document.getElementById('app'))
 }
 componentWillUnmount(){
 // 销毁组件之前的勾子
 // 定时器必须清除,不然会造成内存泄露的问题
 clearInterval(this.timer)
 }
 render(){
 const {msg} = this.props
 const {...style} = this.state
 return (
 <div>
 <h1 style={style} >{msg}</h1>
 <button onClick={this.cancelTime}>取消定时器</button>
 </div>
 )
 }
}
// 渲染组件
ReactDOM.render(<Life msg="生命周期演示"/>,document.getElementById('app'))
复制代码
  • 问题
  • 样式绑定,首先将样式存在state中,在render中将样式帮上
//方式2:绑定样式
// 因为绑定样式是在js中使用,所以样式是使用对象的方式传入
const {opacity,color} = this.state
//这里需要使用双花括号,因为是在js中绑定样式,样式在一个对象中以键值对的形式存在
<h1 style={{opacity,color}} >{msg}</h1>
//方式2:绑定样式
// 使用...将样式以对象的方式取出,可以直接绑定至样式上 
const {...style} = this.state
<h1 style={style} >{msg}</h1>

最近发表
标签列表