前言
React是一个用于构建用户界面的javaScript库,起源于facebook的内部项目,在13年f进行开源
17版本官网:React – A JavaScript library for building user interfaces
18版本官网:React 官方中文文档
特点:
- 声明式编码,组件化编码能提高开发效率和组件复用性
- React Native 编写原生应用
- 高效(优秀的Diffing算法)
一、基础
主要核心,依赖下面四个文件<!-- 引入核心库。全局出现React对象--><script type="text/javascript" src="./React-js/16.8/react.development.js"></script><!-- 用于支持react操作DOM。全局出现ReactDOM对象--><script text="text/javascript" src="./React-js/16.8/react-dom.development.js"></script><!-- 用于将jsx转换为js --><script text="text/javascript" src="./React-js/16.8/babel.min.js"></script><!-- 用于对组件标签属性进行限制。全局出现PropTypes对象 --><script src="./React-js/16.8/prop-types.js"></script>
1、基本使用
1.1、虚拟dom
关于虚拟DOM:
- 本质是object类型的对象(一般对象)
- 虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM 上:那么多的属性。
- 虚拟DOM最终会被React转化为真实DOM。呈现在页面上
<body><div id="test"></div><!-- 引入核心库 --><script type="text/javascript" src="./React-js/16.8/react.development.js"></script><!-- 用于支持react操作DOM --><script text="text/javascript" src="./React-js/16.8/react-dom.development.js"></script><!-- 用于将jsx转换为js --><script text="text/javascript" src="./React-js/16.8/babel.min.js"></script><!-- 一定是babel --><script type="text/babel">// 创建虚拟domconst VDOM = <h1>Hello.React</h1>const VDOM2 = <h1>----------------</h1>// 渲染虚拟DOM到页面(后面的会替换之前)ReactDOM.render(VDOM,document.getElementById('test'))ReactDOM.render(VDOM2,document.getElementById('test'))</script>
</body>
2.2、JSX的语法规则
1、全称: JavaScript XML。
2、react定义的一种类似于XML的JS扩展语法: JS + XML本质是React.createElement(component, props, ...children)方法的语法糖
3、作用: 用来简化创建虚拟DOM
写法:var ele = <h1>Hello JSX!</h1>
注意1:它不是字符串, 也不是HTML/XML标签
注意2:它最终产生的就是一个JS对象
- 定义虚拟DOM时,不要写引号。
- 标签中混入JS表达式时要用{}-
- 样式的类名指定不要用class,要用className.
- 内联样式,要用style={{key : value}}的形式去写。
- 只有一个根标签
- 标签必须闭合
- 标签首字母
- (1).若小写字母开头,则将改标签转为htm1中同名元素,若htm1中无该标签对应的同名元素,则报错。
- (2).若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。
<body><div id="test"></div><!-- 引入核心库 --><script type="text/javascript" src="./React-js/16.8/react.development.js"></script><!-- 用于支持react操作DOM --><script text="text/javascript" src="./React-js/16.8/react-dom.development.js"></script><!-- 用于将jsx转换为js --><script text="text/javascript" src="./React-js/16.8/babel.min.js"></script><!-- js写法 --><script type="text/javascript">// 创建虚拟domconst VDOM = React.createElement('h1',{id:'title'},React.createElement('span',{},'js写法'))// 渲染虚拟DOM到页面(后面的会替换之前)ReactDOM.render(VDOM, document.getElementById('test'))</script><!-- jsx写法 --><script type="text/babel">const data = ['抽烟','喝酒','烫头']const obj = {name1:'抽烟',name2:'喝酒',name3:'烫头'}const myId = 'song'const myData = 'HELLO'const VDOM = (<div><h1 className="box" id={myId}><span style={{ color: 'red', fontSize: '40px' }}>{myData.toLocaleLowerCase()}</span></h1><input type="text" /><ul>{// data // 直接使用数组,会自动遍历// obj // 对象,会报错data.map((item,i)=><li key={i}>{item}</li>)}</ul></div>)// 渲染虚拟DOM到页面(后面的会替换之前)ReactDOM.render(VDOM, document.getElementById('test'))</script>
</body>
2、函数式组件
<script type="text/babel">// 定义函数组件function Demo() {console.log(this); // 经过babel转化开启严格模式,this没有明确的调用,所以为undefinedreturn <h2>函数定义的组件</h2>}// 渲染组件到页面ReactDOM.render(<Demo />, document.getElementById('test'))/*执行了ReactDOM.render( <MyComponent/>.......之后,发生了什么?1.React解析组件标签,找到了MyComponent组件。2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOW转为真实DOM,随后呈现在页面中。*/</script>
3、类组件
<script type="text/babel">// 定义类组件class Demo extends React.Component{render(){// render:类的原型对象上(可在浏览器控制台输入Demo回车测试),供实例使用// this指向Demo的实例对象。俗称:组件对象或组件实例对象console.log('render中this',this);return (<h2>类定义的组件</h2>)}}// 渲染组件到页面ReactDOM.render(<Demo />, document.getElementById('test'))/*执行了ReactDOM.render( <MyComponent/>.......之后,发生了什么?1.React解析组件标签,找到了MyComponent组件。2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用原型上的render方法3.将返回的虚拟DOW转为真实DOM,随后呈现在页面中。*/</script>
3.1、constructor
<script type="text/babel">// 定义类组件class Weather extends React.Component {// 构造器是否接受props,是否传递super,取决于:是否需要在构造器中通过this访问props// 若是写了构造器,不给super传props则,在构造器中拿不到props。为undefinedconstructor(props) { // 构造器只调用一次 // super(props)super()console.log(this.props);}render() {return (<h2 ></h2>)}}const p = { name: 'tom', age: 18, sex: '女' }// 渲染组件到页面ReactDOM.render(<Weather {...p} />, document.getElementById('test'))</script>
4、组件的三大核心
4.1、state:存放状态
<script type="text/babel">// 定义类组件class Weather extends React.Component {constructor(props) { // 构造器只调用一次 super(props)// 初始化状态this.state = {isHot: false,wind: '大风'}// 改变原型上的demo的this指向,并把原型上的demo赋值到实例上的demo上。处理下面this为undefindthis.demo = this.demo.bind(this)}render() { // 调用1+n次。1:初始化 n:状态更新的次数return (<h2 onClick={this.demo}>今天天气{this.state.isHot ? '炎热' : '很冷'}</h2>)}demo() {// demo放在哪里? Weather的原型对象上,供实例使用//由于demo是作为onclick的回调,所以不是通过实例调用的,是直接调用//类中的方法默认开启了局部的严格模式,所以demo中的this为undefinedconsole.log('this', this); // undefind// 不能直接修改值。数据虽然变化,但页面不刷新// this.state.isHot = !this.state.isHot// 注意:需要通过setState方法来修改状态this.setState({ isHot: !this.state.isHot })}}// 渲染组件到页面ReactDOM.render(<Weather />, document.getElementById('test'))function demo() {// console.log('被点击');alert('被点击')}</script>
(1)、state简写
<script type="text/babel">// 定义类组件class Weather extends React.Component {// 初始化状态state = { isHot: false, wind: '大风' }render() { return (<h2 onClick={this.demo}>今天天气{this.state.isHot ? '炎热' : '很冷'}</h2>)}// 自定义方法---需要赋值语句的形式+箭头函数demo = ()=> {console.log('this', this); // undefindthis.setState({ isHot: !this.state.isHot })}}// 渲染组件到页面ReactDOM.render(<Weather />, document.getElementById('test'))</script><!-- 1、组件中 render方法中的this 为组件实例对象-2、组件自定义的方法中this为 undefined,如何解决?a.强制绑定this:通过函数对象bindb.箭头函数3、状态数据,不能直接修改或更新-->
4.2、props:接收参数
<script type="text/babel">// 定义类组件class Weather extends React.Component {render() {console.log(this);return (<ul><li>姓名:{this.props.name}</li><li>性别:{this.props.sex}</li><li>年龄:{this.props.age}---{this.props.flag}</li></ul>)}}const p = { name: 'tom', age: 18, sex: '女' }// 渲染组件到页面ReactDOM.render(<Weather {...p} flag={666}/>, document.getElementById('test'))</script>
(1)、props限制
<!-- 用于对组件标签属性进行限制。全局出现PropTypes对象 -->
<script src="./React-js/16.8/prop-types.js"></script>
<script type="text/babel">// 定义类组件class Weather extends React.Component {render() {console.log(this);// 注意:props是只读的this.props.speak()return (<ul><li>姓名:{this.props.name}</li><li>性别:{this.props.sex}</li><li>年龄:{this.props.age+1}-----</li></ul>)}}// propTypes:给类加规则Weather.propTypes = {// 在15以及以下版本// name:React.PropTypes.string// 16版本及以上,需要通过引入PropTypes对象name:PropTypes.string,sex:PropTypes.string,age:PropTypes.number.isRequired, // isRequired。必传speak: PropTypes.func // 限制为函数}// 设置不传时的默认值Weather.defaultProps = {sex:'我是默认值'}// 渲染组件到页面ReactDOM.render(<Weather name="song" age={666} speak={fun}/>, document.getElementById('test'))function fun (){console.log('我是函数');}</script>
(2)、简写
<script type="text/babel">class Weather extends React.Component {// 写在类里面,相当于给类加属性static propTypes = {name: PropTypes.string,sex: PropTypes.string,age: PropTypes.number.isRequired, // isRequired。必传speak: PropTypes.func // 限制为函数}static defaultProps = {sex: '我是默认值'}render() {console.log(this);// 注意:props是只读的this.props.speak()return (<ul><li>姓名:{this.props.name}</li><li>性别:{this.props.sex}</li><li>年龄:{this.props.age + 1}-----</li></ul>)}}// 渲染组件到页面ReactDOM.render(<Weather name="song" age={666} speak={fun} />, document.getElementById('test'))function fun() {console.log('我是函数');}</script>
(3)、在函数组件的使用
// 定义函数组件function Weather(props) {console.log(this, props); // 经过babel转化开启严格模式,this没有明确的调用,所以为undefinedreturn (<ul><li>姓名:{props.name}</li><li>性别:{props.sex}</li><li>年龄:{props.age + 1}-----</li></ul>)}Weather.propTypes = {name: PropTypes.string,sex: PropTypes.string,age: PropTypes.number.isRequired,}Weather.defaultProps = {sex: '我是默认值'}// 渲染组件到页面ReactDOM.render(<Weather name="song" age={666} />, document.getElementById('test'))
4.3、refs与事件处理
(1)、字符串形式的ref
class Demo extends React.Component{// 展示左侧输入框的数据showData = ()=>{alert(this.refs.inp1.value)}// 展示左侧输入框的数据showData2 = ()=>{alert(this.refs.inp2.value)}render(){return(<div><input ref="inp1" type="text" placeholder="点击按钮提示数据"/><button onClick={this.showData}>点击提示左侧的数据</button> <input ref="inp2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/></div>)}}
(2)、回调函数形式的ref
class Demo extends React.Component {state = { isHot: true }// 展示左侧输入框的数据showData = () => {alert(this.inp1.value)}changWeacter = () => this.setState({ isHot: !this.state.isHot })/*** ref若是以下面内联函数的方式定义,它在更新过程中会别执行两次,* 第一次传入参数为null,第二次才是DOM元素* 因为每次渲染时都会创建新的函数实例,所以React清空旧的ref,被设置新的、* ** 不过可以把回调函数定义成class的绑定函数的方式可以避免,* */saveInp = (c)=>{this.inp2 = c; console.log('绑定函数@', c)}render() {const { isHot } = this.state// return (<div><h1>今天天气{this.state.isHot ? '炎热' : '很冷'}</h1>{/* c:input标签。相当于往Demo身上添加了inp1属性,把input标签赋值给它。内联函数方式*/}<input ref={c => { this.inp1 = c; console.log('@', c) }} type="text" placeholder="点击按钮提示数据" />{/* 使用class绑定函数的方式 */}<input ref={this.saveInp} type="text" placeholder="点击按钮提示数据" /><button onClick={this.showData}>点击提示左侧的数据</button> <button onClick={this.changWeacter}>点击切换天气</button></div>)}}
(3)、API形式的ref
class Demo extends React.Component {/** React.createRef* 存放被ref标识的节点,但只能单独存一个*/ myRef = React.createRef()myRef2 = React.createRef()// 展示左侧输入框的数据showData = () => {console.log(this.myRef,this.myRef2);}render() {// return (<div><input ref={this.myRef} type="text" placeholder="点击按钮提示数据" /><input ref={this.myRef2} type="text" placeholder="点击按钮提示数据" /><button onClick={this.showData}>点击提示左侧的数据</button> </div>)}}
5、事件处理
class Demo extends React.Component {/** * 1、通过onXxx属性指定事件处理函数(注意大小写)* (1)React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 ---为了处理兼容* (2)React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) --- 为了高效* 2、通过event.target得到发生事件的DOM元素对象*/myRef = React.createRef()myRef2 = React.createRef()// 展示左侧输入框的数据showData = () => {console.log(this.myRef, this.myRef2);}showData2 = (e) => {console.log(e.target.value);}render() {// return (<div><input ref={this.myRef} type="text" placeholder="点击按钮提示数据" /><button onClick={this.showData}>点击提示左侧的数据</button> <input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" /></div>)}}
5.1、函数柯里化与高阶函数
/** * *高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。* 1.若A雨数,接收的参数是一个函数,那么A就可以称之为高阶函数。* 2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。* 常见的高阶函数有: Promise、setTimeout、arr.map()等等* * *函数的柯里化: 通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。*/// 普通写法
function sum(a,b,c){return a+b+c
}
const res = sum(1,2,3)// 柯里化写法
function sum(a){return (b)=>{return (c)=>{return a+b+c}}
}
const res = sum(1)(2)(3)
/* 普通写法: */class Demo extends React.Component {state = {username:'',password:''}// 保存saveUsername = (e)=>{this.setState({username:e.target.value})}savePassword = (e)=>{this.setState({password:e.target.value})}// 展示左侧输入框的数据handleSubmit = (e)=>{e.preventDefault() // 阻止默认行为const {username,password } = this.stateconsole.log('用户名密码:',username,password);}render() {// return (<form onSubmit={this.handleSubmit}>用户名:<input onChange={this.saveUsername} type="text" />密码:<input onChange={this.savePassword} type="password" /><button>登录</button></form>)}}/* 柯里化写法: */class Demo extends React.Component {state = {username: '',password: ''}// 保存。柯里化写法saveData = (dataType) => {console.log('dataType', dataType);return (e) => {this.setState({ [dataType]: e.target.value })}}handleSubmit = (e) => {e.preventDefault() // 阻止默认行为const { username, password } = this.stateconsole.log('用户名密码:', username, password);}render() {// return (<form onSubmit={this.handleSubmit}>用户名:<input onChange={this.saveData('username')} type="text" />密码:<input onChange={this.saveData('password')} type="password" /><button>登录</button></form>)}}/* 不使用柯里化写法: */class Demo extends React.Component {state = {username: '',password: ''}// 保存。saveData = (dataType, data) => {console.log('dataType', dataType, data);this.setState({ [dataType]: data })}handleSubmit = (e) => {e.preventDefault() // 阻止默认行为const { username, password } = this.stateconsole.log('用户名密码:', username, password);}render() {return (<form onSubmit={this.handleSubmit}>用户名:<input onChange={(e) => this.saveData('username', e.target.value)} type="text" />密码:<input onChange={(e) => this.saveData('password', e.target.value)} type="password" /><button>登录</button></form>)}}
6、生命周期
6.1、旧版
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1.constructor()
2.componentWillMount()
3.render()
4.componentDidMount()
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
1.shouldComponentUpdate()
2.componentWillUpdate()
3.render()
4.componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1.componentWillUnmount()
(1)、案例
6.2、新版
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
- constructor()
- getDerivedStateFromProps
- render()
- componentDidMount()
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
- getDerivedStateFromProps
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate
- componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount()