一、简介
1.官网
英文官网:http:// https://reactjs.org/
中文官网:https://react.docschina.org/
2.介绍
- 用于动态构建用户界面的 JavaScript 库(只关注于视图)
- 由Facebook开源
3.React高效的原因
- 使用虚拟(virtual)DOM, 不总是直接操作页面真实DOM。
-
DOM Diffing算法, 最小化页面重绘
4.相关js库
- react.js:React核心库。
- react-dom.js:提供操作DOM的react扩展库。
- babel.min.js:解析JSX语法代码转为JS代码的库。
5.创建虚拟DOM的两种方式
- 纯JS方式(一般不用)
- JSX方式
使用JS方式
<!-- 准备好一个容器 -->
<div id="test">
</div>
<!-- 1.先引入react核心库 -->
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<!-- 2.再引入react-dom,用于react操作dom -->
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script type="text/babel">/* 此处一定要写 */
// 1.创建虚拟dom
// const VDOM = React.createElement(标签名,标签属性,标签内容)
const VDOM = React.createElement('h1', { id: 'title' }, React.createElement('span', {}, 'Hello,React'))
// 2.渲染虚拟dom到页面
// ReactDom.render(虚拟Dom,容器)
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
使用JSX
<!-- 准备好一个容器 -->
<div id="test">
</div>
<!-- 1.先引入react核心库 -->
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<!-- 2.再引入react-dom,用于react操作dom -->
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<!-- 3.引入babel,用于将jsx转为js -->
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel">/* 此处一定要写 */
// 1.创建虚拟dom
const VDOM = (/* 此处一定不要写引号,因为不是字符串 */
<h1 id="title">
<span>Hello,React</span>
</h1>
)
// 2.渲染虚拟dom到页面
// ReactDom.render(虚拟Dom,容器)
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
二、JSX
全称: JavaScript XML
react定义的一种类似于XML的JS扩展语法: JS + XML本质是 React.createElement(component, props, ...children)方法的语法糖
作用: 用来简化创建虚拟DOM
- 写法:var ele = <h1>Hello JSX!</h1>
-
它不是字符串, 也不是HTML/XML标签
-
它最终产生的就是一个JS对象
基本语法规则:
- 定义虚拟dom时,不需要写引号。
- 标签中混入js表达式时要用{}。
-
样式的类名不要用class,用className。
-
内联样式,要用style={{key:value}}的形式去写。
-
虚拟dom只有一个跟标签。
-
标签必须闭合。
-
标签首字母:
(1).若小写字母开头,则将该标签转为html中同名元素,若html无该标签对应的同名元素则报错。(2).若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。
bable.js的作用:
- 浏览器不能直接解析JSX代码, 需要babel转译为纯JS的代码才能运行
-
只要用了JSX,都要加上type="text/babel", 声明需要babel来处理
例子:
<!-- 准备一个容器 -->
<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">
const myId = 'aTgUiGu'
const myData = 'Hello,rEact'
// 1.创建虚拟dom
const VDOM = (
<div>
<h2 id={myId.toLocaleLowerCase()} className="title">
<span style={{color:'white',fontSize:'20px'}}>{myData.toLocaleLowerCase()}</span>
</h2>
<input/>
</div>
)
// 2.渲染虚拟dom的页面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
一定注意区分:【js语句(js代码)】与【js表达式】
表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方,下面这些都是表达式:
-
a
-
a+b
-
demo(1)
-
arr.map()
-
function test () {}
语句(代码):
-
if(){}
-
for(){}
-
switch(){case:xxxx}
三、组件
1.组件分类
(1)函数式组件
函数式组件即函数定义的组件。
例如:
<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">
// 1.创建函数式组件
function Demo(){
console.log(this);//此处的this是undefined,因为babel编译后开启了严格模式
return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
// 2.渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById('test'))
/*
执行了ReactDOM.render(<Demo/>......之后,发生了什么?
1.react会解析组件标签,找到了Demo组件
2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟dom转为真是dom,随后呈现到页面中
*/
</script>
(2)类式组件
例如:
<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">
// 1.创建类式组件
class Mycomponent extends React.Component {
render() {
// render是放在那里的? 类的原型对象上,供实例使用。
// render的this是谁? Mycomponent的实例对象。<=> Mycomponent组件实例对象
return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
}
}
// 2.渲染组件到页面
ReactDOM.render(<Mycomponent/>,document.getElementById('test'))
/*
1.react会解析组件标签,找到了Mycomponent组件
2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用的原型上的render方法。
3.将虚拟dom转为真实dom,随后呈现在页面中。
*/
</script>
注意点:
- 组件名必须首字母大写
- 虚拟DOM元素只能有一个根元素
- 虚拟DOM元素必须有结束标签
基本流程:
-
React内部会创建组件实例对象
- 调用render()得到虚拟DOM, 并解析为真实DOM
-
插入到指定的页面元素内部
2.组件的三大核心
(1)state
state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
用法案例:
<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">
class Weather extends React.Component{
// 初始化状态
state = {isHot:true}
render(){
const {isHot} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot?'炎热':'凉爽'}</h1>
}
// 自定义方法 要用赋值语句的形式+箭头函数
changeWeather = ()=>{
const isHot = this.state.isHot
this.setState({isHot:!isHot})
}
}
ReactDOM.render(<Weather/>,document.getElementById('test'))
</script>
注意:
- 组件中render方法中的this为组件实例对象。
- 状态数据,不能直接修改或更新,需要使用setState方法修改。
(2)props
每个组件对象都会有props(properties的简写)属性
组件标签的所有属性都保存在props中
作用:通过标签属性从组件外向组件内传递变化的数据
注意: 组件内部不要修改props数据,在函数是组件中只能使用组件三大核心中的props。
用法:
-
内部读取某个属性值:this.props.name
- 对props中的属性值进行类型限制和必要性限制
第一种方式(React v15.5 开始已弃用):
Person.propTypes = {
name: React.PropTypes.string.isRequired,
age: React.PropTypes.number
}
第二种方式(新):使用prop-types库进限制(需要引入prop-types库)
Person.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.
}
3. 扩展属性: 将对象的所有属性通过props传递 :<Person {...person}/>
4. 默认属性值:
Person.defaultProps = {
age: 18,
sex:'男'
}
5. 组件类的构造函数
constructor(props){
super(props)
console.log(props)//打印所有属性
}
例如:
<div id="test"></div>
<div id="test1"></div>
<div id="test2"></div>
<script src="../js/react.development.js"></script>
<script src="../js/react-dom.development.js"></script>
<script src="../js/babel.min.js"></script>
<!-- 引入prop-types,用于对组件标签进行限制 -->
<script src="../js/prop-types.js"></script>
<script type="text/babel">
// 创建组件
class Person extends React.Component {
// 对标签属性进行类型、必要性限制
static propTypes = {
name:PropTypes.string.isRequired, // 指定name为字符串类型且必传
age:PropTypes.number, // 指定age为数字类型
sex:PropTypes.string, // 指定sex为字符串类型
speak:PropType.func // 指定speak为函数类型
}
// 设置默认值
static defaultProps = {
sex:'男',
age:18
}
render(){
const {name,age,sex} = this.props
// props是只读的
// this.props.name = 'jack' // 此行代码会报错,因为props是只读的
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
)
}
}
// 渲染组件
ReactDOM.render(<Person name="tom" sex="女" age={18} speak={speak}/>,document.getElementById('test'))
ReactDOM.render(<Person name="jerry" sex="男" age={19}/>,document.getElementById('test1'))
const p = {name:'老刘',age:18,sex:'女'}
ReactDOM.render(<Person {...p}/>,document.getElementById('test2'))
function speak(){
console.log('我说话了');
}
</script>
(3)refs
组件内的标签可以定义ref属性来标识自己
用法:
- 字符串形式的ref:<input ref="input1"/>
-
回调形式的ref:<input ref={(c)=>{this.input1 = c}} (开发常用)
- createRef创建ref容器
myRef = React.createRef()
<input ref={this.myRef}/>
案例:
字符串形式
<script type="text/babel">
// 创建组件
class Demo extends React.Component {
showData = () => {
let { input1 } = this.refs
alert(input1.value)
}
showData1 = () => {
let { input2 } = this.refs
alert(input2.value)
}
render() {
return (
<div>
<input type="text" placeholder="点击按钮提示数据" ref="input1" />
<button onClick={this.showData} ref="button100">点我提示左侧输入框数据</button>
<input type="text" placeholder="失去焦点提示数据提示数据" ref="input2" onBlur={this.showData1} />
</div>
)
}
}
// 渲染组件
ReactDOM.render(<Demo />, document.getElementById('test'))
</script>
回调形式
<script type="text/babel">
// 创建组件
class Demo extends React.Component {
showData = () => {
let { input1 } = this
alert(input1.value)
}
showData1 = () => {
let { input2 } = this
alert(input2.value)
}
render() {
return (
<div>
<input ref={c => this.input1 = c} type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点我提示左侧输入框数据</button>
<input type="text" placeholder="失去焦点提示数据提示数据" onBlur={this.showData1} ref={c => this.input2 = c} />
</div>
)
}
}
// 渲染组件
ReactDOM.render(<Demo />, document.getElementById('test'))
</script>
createRef创建ref容器
<script type="text/babel">
// 创建组件
class Demo extends React.Component {
// React.createRef()调用后可以反会一个容器,该容器可以存储ref所标识的节点,该容器是专人专用的
myRef = React.createRef()
myRef2 = React.createRef()
showData = () => {
alert(this.myRef.current.value);
}
showData1 = () => {
alert(this.myRef2.current.value)
}
render() {
return (
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点我提示左侧输入框数据</button>
<input type="text" placeholder="失去焦点提示数据提示数据" onBlur={this.showData1} ref={this.myRef2} />
</div>
)
}
}
// 渲染组件
ReactDOM.render(<Demo />, document.getElementById('test'))
</script>
3.包含表单的组件分类
(1)受控组件
<script type="text/babel">
class Login extends React.Component{
state = {
username:'',
password:''
}
saveUsername = (event)=>{
this.setState({username:event.target.value})
}
savePassword = (event)=>{
this.setState({password:event.target.value})
}
handleSubmit = (event)=>{
// 阻止默认提交
event.preventDefault()
const {username,password} = this.state
alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
}
render(){
return (
<form action="http://www.atguigu.com" onSubmit={this.handleSubmit}>
用户名:<input type="text" name="username" onChange={this.saveUsername} />
密码:<input type="password" name="password" onChange={this.savePassword} />
<button>登录</button>
</form>
)
}
}
// 渲染组件
ReactDOM.render(<Login />, document.getElementById('test'))
</script>
(2)非受控组件
<script type="text/babel">
class Login extends React.Component{
handleSubmit = (event)=>{
// 阻止默认提交
event.preventDefault()
const {username,password} = this
alert(`你输入的用户名是:${username.value},你输入的密码是:${password.value}`)
}
render(){
return (
<form action="http://www.atguigu.com" onSubmit={this.handleSubmit}>
用户名:<input type="text" name="username" ref={c =>this.username=c} />
密码:<input type="password" name="password" ref={c =>this.password=c} />
<button>登录</button>
</form>
)
}
}
// 渲染组件
ReactDOM.render(<Login />, document.getElementById('test'))
</script>
4.组件生命周期
(1)旧生命周期
生命周期的三个阶段(旧)
初始化阶段: 由ReactDOM.render()触发---初次渲染
- constructor()
- componentWillMount()
- render()
- componentDidMount()
更新阶段: 由组件内部this.setSate()或父组件重新render触发
-
shouldComponentUpdate()
-
componentWillUpdate()
-
render()
-
componentDidUpdate()
卸载组件: 由ReactDOM.unmountComponentAtNode()触发
componentWillUnmount()
(2)新生命周期
生命周期的三个阶段(新)
初始化阶段: 由ReactDOM.render()触发---初次渲染
-
constructor()
-
getDerivedStateFromProps
-
render()
-
componentDidMount()
更新阶段: 由组件内部this.setSate()或父组件重新render触发
-
getDerivedStateFromProps
-
shouldComponentUpdate()
-
render()
-
getSnapshotBeforeUpdate
-
componentDidUpdate()
卸载组件: 由ReactDOM.unmountComponentAtNode()触发
componentWillUnmount()
(3)重要钩子
-
render:初始化渲染或更新渲染调用
-
componentDidMount:开启监听, 发送ajax请求
-
componentWillUnmount:做一些收尾工作, 如: 清理定时器
(4)即将废弃的勾子
-
componentWillMount
-
componentWillReceiveProps
-
componentWillUpdate
现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。
新增钩子getDerivedStateFromProps的使用场景:使用罕见,若state的值在任何时候都取决于props,那么可以使用。
新增钩子getSnapshotBeforeUpdate的使用场景:
<script type="text/babel">
class NewsList extends React.Component {
state = {
newsArr: []
}
componentDidMount() {
setInterval(() => {
const { newsArr } = this.state
// 模拟新闻
const news = '新闻' + (newsArr.length + 1)
// 更新状态
this.setState({ newsArr: [news, ...newsArr] })
}, 1000);
}
getSnapshotBeforeUpdate() {
return this.refs.list.scrollHeight
}
componentDidUpdate(preProps, preState, height) {
this.refs.list.scrollTop += this.refs.list.scrollHeight - height
}
render() {
return (
<div className="list" ref="list">
{
this.state.newsArr.map((n, index) => {
return <div className="news" key={index}>{n}</div>
})
}
</div>
)
}
}
ReactDOM.render(<NewsList />, document.querySelector('#test'))
</script>
四、高阶函数和函数柯里化
1.高阶函数
如果一个函数符合下面两个规范的任何一个,那该函数就是高阶函数:
-
若a函数,接收的参数是一个函数,那么a函数就可以称之为高阶函数。
-
若a函数,调用的返回值依然是一个函数,那么a就可以称之为高阶函数。
案例:
<script type="text/babel">
class Login extends React.Component {
state = {
username: '',
password: ''
}
saveFormData = (dataType) => event => this.setState({ [dataType]: event.target.value })
handleSubmit = (event) => {
// 阻止默认提交
event.preventDefault()
const { username, password } = this.state
alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
}
render() {
return (
<form action="http://www.baidu.com" onSubmit={this.handleSubmit}>
{/* onChange={this.saveFormData('username')}意思是将this.saveFormData('username')函数的返回值交给了onChange */}
用户名:<input type="text" name="username" onChange={this.saveFormData('username')} />
密码:<input type="password" name="password" onChange={this.saveFormData('password')} />
<button>登录</button>
</form>
)
}
}
// 渲染组件
ReactDOM.render(<Login />, document.getElementById('test'))
</script>
2.函数柯里化
通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
简例:求和案例
function sum(a) {
return (b) => {
return c => {
return a + b + c
}
}
}
const result = sum(1)(2)(3)