官网:https://react.docschina.org
一、认知React
概述
React 起源于 Facebook(脸书) 的内部项目,它是一个用于构建用户界面的 javascript 库,Facebook用它来架设公司的Instagram网站,并于2013年5月开源。
React用于构建用户界面的 JavaScript 库,它不是一个完整的MVC框架
特点
- 声明式
你只需要描述UI看起来是什么样式,就跟写HTML一样,React负责渲染UI
- 基于组件
组件时React最重要的内容,组件表示页面中的部分内容
- 应用场景
使用React可以开发Web应用(ReactJs),使用React可以开发移动端(react-native),可以开发VR应用(react 360)
二、react写法基础
1、react应用
react有很多依赖库,其中有两个使我们必须引入的react.js、react-dom.js
下载地址
react.js 是核心,提供创建元素,组件等功能 https://unpkg.com/react@16/umd/react.development.js react-dom.js 提供DOM相关功能 https://unpkg.com/react-dom@16/umd/react-dom.development.js
写法 必要的三个步骤(依赖的引入顺序不能乱)
<body> <div id="app"></div> <script src="./utils/react.development.js"></script> <script src="./utils/react-dom.development.js"></script> <script> //1、 获取插入节点 const app=document.getElementById('app') // 2、创建虚拟dom const el=React.createElement const vnode=el( 'div', null, el('h1',{id:1000},'hello w'), ) // 3、把dom放到自定位置上 ReactDOM.render(vnode,app) </script> </body>
2、jsx介绍与用法
- JSX 执行更快,因为它在编译为JavaScript代码后进行了优化
- 它是类型安全的,在编译过程中就能发现错误
- 声明式语法更加直观,与HTML结构相同,降低了学习成本,提升开发效率速
- jsx语法中一定要有一个*元素包裹,否则编译报错,程序不能运行
引入依赖: https://unpkg.com/babel-standalone@6/babel.min.js
1)、引入相关依赖
<div id="app"></div> <script src="./utils//babel.min.js"></script> <script src="./utils/react.development.js"></script> <script src="./utils/react-dom.development.js"></script>
2)、创建dom元素
const app = document.getElementById('app') // 单行不需要(),多行需要 const vnode = (<div> <dl> <dt>asd</dt></dl> </div>)
3)、表达式
1、必须有最外成包裹 2、注释放在{}内 3、参数用{}单括号
const num = 1 const vnodes = ( <div> <h3>{num}</h3> <div>{num > 10 ? 'a' : 'b'}</div> </div> )
4)、动态绑定
1、动态属性:直接写{} 2、jsx class改成className 3、jsx for改成htmlFor 4、style 写{{fontSize:'14px',color:'red'}} 5、解析html dangerouslySetInnerHTML={{__html:html}}
const title = '按钮' const html = '<a herf="">ff</a>' const vnode1 = ( <div className='jj'> <button title={title}>{title}</button> </div> )
5)、数组
const users = ['11', '22', '33'] const vnode2 = ( <div> { // 方案1 }{ users.map((item, index) => { return (<h3 key={index}>{item}</h3>) }) } { // 方案2 } { users.map((item, index) => ( (<h3 key={index}>{item}</h3>) )) } </div> )
6)、插入
ReactDOM.render(vnode, app)
三、React脚手架
1、创建
npx create-react-app 项目名称
这些文件没啥用删掉他们
修改文件--至此初始化完成
2、组件
1)、函数组件
import React ,{Component}from 'react' function App(){ return( <div>普通</div> ) } const App=()=>{ return( <div> es6 </div> ) } export default App
2)、类组件
import React ,{Component}from 'react' class App extends Component{ render(){ return( <div>类写法</div> ) } } export default App
3、事件处理
该表事件绑定指向
export default class Solvethis extends Component { constructor(props) { super(props) // 方案1 this.fun = this.fun.bind(this) } render() { return ( <div> <button onClick={this.fun}>方案1</button> <button onClick={this.fun2.bind(this)}>方案2</button> <button onClick={() => this.fun()}>方案3</button> <button onClick={this.fun}>方案4</button> </div> ) } fun() { console.log(this.props) } fun2() { console.log(this.props) } fun3() { console.log(this.props) } fun4 = () => { console.log(this.props) } }
4、state状态
state状态只在class类组件才有,函数组件没有此功能。
export default class extends React.Component { constructor(props){ super(props) // 第一种初始化方式 this.state = { count : 0 } } /* // 第二种初始化方式 state = { count:1 } */ render(){ return ( <div>计数器 :{this.state.count}</div> ) } }
修改
语法1 this.setState({ key:value }) 语法2 官方推荐 this.setState(state => { key:value })
三、组件传值
1、props相关
props与state区别
- props 中存储的数据,都是外界传递到组件中的
- props 中的数据,都是只读的
- state 中的数据,都是可读可写的
- props 在函数声明或类申明的组件中都有
- state 只有类申明的组件中才有
1)、父子组件传值props
父组件
<Home name={'zhangzhang1'}></Home>
函数组件
const App=(props)=>{ return( <div> {props.name} </div> ) }
类组件
class App extends Component{ render(){ return( <div> <h3> {this.props.name} </h3> </div> ) } }
2)、props之children属性
父组件
<Home >我是children中的值</Home>
接收
class App extends Component{ render(){ return( <div> <h3> {this.props.children} </h3> </div> ) } }
3)、props-type限制传入类型
组件调用者可能不知道组件封装着需要什么样的数据,如果传入的数据不对,可能会导致程序异常,所以必须要对于props传入的数据类型进行校验。
下载:npm i -S prop-types
参数
- 类型: array、bool、func、number、object、string
- React元素类型: element
- 必填项: isRequired
- 特定结构的对象: shape({})
函数组件
import PrTypes from 'prop-types' const Cmp = (props) => {
Cmp.defaultProps = { //默认值
name: '标题'
}
Cmp.propTypes = { name: PrTypes.number } return( <div>{props.name}</div> )}
类组件
import PrTypes from 'prop-types' class Cmp extends Component { // 静态方法不能适用this static propTypes = { name: ProTypes.string } render() { return ( <div>{this.props.name}</div> ) } }
4)、受控组件
将state与表单项中的value值绑定在一起,有state的值来控制表单元素的值,称为受控组件。
<input type="text" value={this.state.username} onChange={this.inputChange.bind(this)} /> //注:多表单元素需优化事件方法 this.setState({ [e.target.name]: e.target.value })
5)、非受控组件
没有和state数据源进行关联的表单项,而是借助ref,使用元素DOM方式获取表单元素值
class App extends React.Component { constructor(props){ super(props) //创建 ref this.username = React.createRef() } render(){ return ( <div> <input type ="text" ref={this.username} /> <button onClick ={this.fn}>获取值</button> </div> ) } // 获取文本框的值 fn =() => { console.log(this.username.current.value) } }
6)、无修改不渲染
shouldComponentUpdate
import React, { Component, PureComponent } from 'react' export default class SApp extends Component { shouldComponentUpdate(nextProps, nextState) { /* 旧得props 这时的props还没更新 state同理 PureComponent 与Component 功能相似的,区别在于React.PureComponent 内部自动实现了
shouldComponentUpdate钩子,不需要手动进行比较操作。 */ if (nextProps.name == this.props.name) { return false } return true } render() { return ( <div>{this.props.name}</div> ) } }
PureComponent
React.PureComponent 与 React.Component 功能相似的,区别在于React.PureComponent 内部自动实现了 shouldComponentUpdate钩子,不需要手动进行比较操作。
import React, { Component, PureComponent } from 'react' export default class SApp extends PureComponent {
render() {
return (
<div>{this.props.name}</div>
)
}
}
2、其他组件通信
1)、父组件--子组件
原理:通过ref非受控组件 ,利用createRef进行子元素方法来改变内容
子组件
import React,{Component} from 'react' export default class Child extends Component{ state={ title:'sad' } render(){ return( <div> {this.state.title} </div> ) } setTitle(){ this.setState((state)=>{ title:'999' }) } }
父组件
import React,{Component,createRef} from 'react' import SonView from './components/SonListen' export default class Father extends Component{ constructor(props){ super(props) this.son=createRef() } render(){ return( <div> <SonView ref={this.son}></SonView> <button onClick={this.func.bind(this)}></button> </div> ) } func(){ const sonValue=this.son.current sonValue.setTitle() } }
2)、子组件---父组件
父组件将自己的某个方法传递给子组件,在方法里可以做任意操作,比如可以更改状态,子组件通过this.props接收到父组件的方法后调用。
<SonView sonClick={this.son}></SonView> 父组件
<button onClick={this.props.sonClick(id)}></button> 子组件
3)、跨组件通信
祖先--子孙
一、定义数据源
import React ,{creatContext} from 'react' let {Provider,Consumer}=creatContext() export{ Provider, //发布 Consumer //订阅 }
二、祖先
import React,{Component} from'react' import {Provider,Consumer} from './store' import Son1 from './Son1' export default class App extends Component{ constructor(props){ super(props) this.state={ name:'uuu' } } render(){ return( <div> <Provider value={this.state.name}> <Son1></Son1> </Provider> </div> ) } }
三、接收的后代
import React,{Component} from'react' import {Consumer} from './store' export default class Son1 extends Component{ constructor(props){ super(props) this.state={ name:'uuu' } } render(){ return( <div> <Consumer> { value=>{ <div>{value.name}</div> } } </Consumer> </div> ) } }
兄弟节点之前通信
原理:一个子物体挂在事件,另一个挂在属性,通过实践改变属性,来改变另一个组件接受的内容
祖先
state = { count: 1, setCount: () => { this.setState(state => { return { count: ++state.count } }) } }render() { let { count, setCount } = this.state return ( <div> <Provider value={{ count, setCount }}> {/* 兄弟 */} <Cmp1></Cmp1> <Cmp2></Cmp2> </Provider> </div> ) }
兄弟1
import React, { Component ,createContext} from 'react' export default class Cmp2 extends Component { // 只得到了默认数据 --> 没有包裹在Provider组件中 static contextType = createContext render() { return ( <div> <button onClick={this.setCount.bind(this)}>自增数据</button> </div> ) } setCount() { this.context.setCount() } }
兄弟2
<Consumer> { value => <h3>{value.count}</h3> } </Consumer> )