Reat简介
React:用于构建用户界面的 JavaScript 库。由 Facebook 开发且开源。是一个将视图渲染为html视图的开源库
第一章:React入门
相关js库
- react.development.js :React 核心库
- react-dom.development.js :提供 DOM 操作的 React 扩展库
- babel.min.js :解析 JSX 语法(js语法糖),转换为 JS 代码
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 此处一定要写babel,表示写的不是 JS,而是 JSX,并且靠 babel 翻译 -->
<script type="text/babel">
//1.创建虚拟DOM 此处是JSX 不要写引号,因为不是字符串
const VDOM = <h1>Hello,React</h1>
//2.渲染虚拟DOM到页面
// 导入核心库和扩展库后,会有 React 和 ReactDOM 两个对象
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
创建VDOM的两种方式
第一种 jsx方式(推荐).
<div id="test"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
//1.创建虚拟DOM
const VDOM = (
<h1>
<span>Hello,React</span>
</h1>
)
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
第二种原生js方式
<div id="test"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
//原生js不用写babel
<script type="text/javascript">
//1.创建虚拟DOM
const VDOM = React.createElement('h1',{id:'title'},React.createElement('span',{},'Hello,React'))
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
VDOM | DOM
关于虚拟 DOM:
- 本质是 Object 类型的对象(一般对象)
- 虚拟 DOM 比较“轻”,真实 DOM 比较“重”,因为虚拟 DOM 是 React 内部在用,无需真实 DOM 上那么多的属性。
- 虚拟 DOM 最终会被 React 转化为真实 DOM,呈现在页面上。
<script type="text/babel">
//1.创建虚拟DOM
const VDOM = (
<h1>
<span>Hello,React</span>
</h1>
)
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.getElementById('test'))
console.log('VDOM',VDOM); // VDOM {...}是一个对象
console.log('TDOM',document.querySelect('#test')); // TDOM <div>...</div>(真实DOM)
console.log(typeof VDOM); // object
console.log(VDOM instanceof Object); // true
</script>
React JSX
- 全称:JavaScript XML
- React 定义的类似于 XML 的 JS 扩展语法;本质是
React.createElement()
方法的语法糖 - 作用:简化创建虚拟 DOM
- 补充:js中,JSON的序列化和反序列化使用
parse()/stringify()
JSX 语法规则
- 定义虚拟 DOM 时,不要写引号
- 标签中混入 JS 表达式需要使用 {}
- 指定类名不用 class,使用 className
- 内联样式,使用
style={{ key: value }}
的形式 - 只能有一个根标签
- 标签必须闭合,单标签结尾必须添加 /:
<input type="text" />
- 标签首字母小写,则把标签转换为 HTML 对应的标签,若没有,则报错
- 标签首字母大写,则渲染对应组件,若没有定义组件,则报错
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>jsx语法规则</title>
<style>
.title {
background-color: orange;
width: 200px;
}
</style>
</head>
<body>
<div id="test"></div>
...
<script type="text/babel">
const myId = 'aTgUiGu'
const myData = 'HeLlo,rEaCt'
const VDOM = (
<div>
<h2 className="title" id={myId.toLowerCase()}>
<span style={{ color: 'white', fontSize: '19px' }}>{myData.toLowerCase()}</span>
</h2>
<input type="text" />
// <good>very good</good>
// <Child></Child>
</div>
)
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
</body>
</html>
补充:表达式与语句(代码)的区别.
表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方(如数值处理等)
a
a+b
demo(1)
arr.map() //可以遍历的时候使用
function test () {}
语句(代码),下面这些都是语句(如逻辑判断语句)
if(){}
for(){}
switch(){case:xxxx}
第二章:React面向组件
创建组件的两种方式
函数式组件
<div id="app"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
// 创建函数式组件
function Demo() {
console.log(this) //babel编译后开启严格模式 此处的this是undefind
return <h1>函数式组件,适用于简单组件的创建</h1>
}
// 挂载组件
ReactDOM.render(<Demo/>,document.querySelector("#app"))
</script>
要点:
- 组件名称首字母必须大写,否则会解析成普通标签导致报错(JSX 语法规则)
- 函数需返回一个虚拟 DOM
- 渲染组件时需要使用标签形式,同时标签必须闭合
渲染组件的过程:
1、React 解析标签,寻找对应组件
2、发现组件是函数式组件,则调用函数,将返回的虚拟 DOM 转换为真实 DOM ,并渲染到页面中
类式组件
类的基本知识
<script type="text/javascript">
/*
总结:
1. 类的实例不是必须写的,需要对类进行初始化操作,如添加指定属性时才写
2. 子类继承父类,子类中写了构造器,那么子类构造器中的super是必须要调用的
3. 类中定义的方法都是放在了类的原型对象上,供实例去使用
4. 实例调用子类的方法时,找不到会去父类的原型对象上去找,一直找到顶级window对象
*/
class Person {
// 构造器方法
constructor(name,age) {
// 构造器中的this指向类的实例对象(constructor是一个函数,由实例对象调用,所以this指向实例对象)
this.name = name
this.age = age
}
// 一般方法
speak() {
// 放在了类的原型对象上,供实例使用
// 通过Person实例调用speak方法时,speak中的this指向Person实例
console.log(`我的名字叫${this.name},今年${this.age}岁了`);
}
}
// Student类继承与Person类
class Student extends Person {
constructor(name,age,id) {
super(name,age)
this.id = id
}
// 重写从父类继承的方法
speak() {
console.log(`我的名字叫${this.name},今年${this.age}岁了,编号为${this.id}`);
}
}
const p1 = new Person('张三',18)
p1.speak()
const s1 = new Student('李四',16,'30461')
s1.speak()
</script>
组件渲染过程:
- React 解析组件标签,寻找组件
- 发现是类式组件,则 new 该类的实例对象,通过实例调用原型上的 render 方法
- 将 render 返回的虚拟 DOM 转为真实 DOM ,渲染到页面上
//创建类式组件
class MyComponent extends React.Component {
render() {
return <h2>我是用类定义的组件</h2>
}
}
//渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
第三章: 组件实例的核心属性
核心属性1:state | 状态
state 是组件实例对象最重要的属性,值为对象。又称为状态机,通过更新组件的 state 来更新对应的页面显示。
要点:
- 初始化 state
- React 中事件绑定
- this 指向问题
- setState 修改 state 状态
- constructor 、render 、自定义方法的调用次数
标准写法:
<script type="text/babel">
class Weather extends React.Component {
//构造器调用1次
constructor(props) {
super(props)
// 初始化状态
this.state = {isHot:true}
// 解决changeWeather中的this指向问题
this.change = this.changeWeather.bind(this)
}
render() {
//render调用n+1次
// 读取状态
const {isHot} = this.state
// 这里的this指向原型对象
// return <h1 onClick={this.changeWeather}>今天天气{isHot?'凉爽':'炎热'}</h1>
// 这里的this指向实例自身
return <h1 onClick={this.change}>今天天气{isHot ? '炎热' :'凉爽' }</h1>
}
changeWeather() {
//点几次changeWeather调用几次
// changeWeather放在实例对象的原型上,供实例使用
// changeWeather作为onClick的回调函数使用,不是通过实例调用,是直接调用
// 类中的方法默认开启了严格模式,this指向丢失
// 状态不可直接更改,必须借助React内置API
// this.state.isHot = !this.state.isHot
this.setState({isHot:!this.state.isHot})
}
}
function demo() {
// 修改状态
}
ReactDOM.render(<Weather/>,document.querySelector('#app'))
</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>
}
// 自定义方法 -- 赋值语句+箭头函数(中的this指向上下文中的this)
changeWeather = ()=>{
this.setState({isHot:!this.state.isHot})
}
}
ReactDOM.render(<Weather/>,document.querySelector('#app'))
</script>
核心属性2:props | 标签属性
每个组件对象都有 props 属性,组件标签的属性都保存在 props 中。(注意:props 是只读的,不能修改。)
标准写法:
<script type="text/babel">
// 创建组件
class Person extends React.Component {
render() {
const {name,age,sex} = this.props
return (
<div>
<h1>{name}</h1>
<h2>{age}</h2>
<h2>{sex}</h2>
</div>
)
}
}
// 渲染组件
// ReactDOM.render(<Person name="张三" age="18" sex="1"/>,document.querySelector('#app'))
// 批量传递props/批量传递标签属性
const obj = {name:"张三",age:18,sex:"男"}
ReactDOM.render(<Person {...obj}/>,document.querySelector('#app'))
</script>
- 限制标签属性 在 React 15.5 以前,React 身上有一个
PropTypes
属性可直接使用,即name:React.PropTypes.string.isRequired
,没有把PropTypes
单独封装为一个模块。
<div id="app1"></div>
<div id="app2"></div>
<div id="app3"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入prop-type 用于限制props属性标签 -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
// 假数据
const obj = {name:"张三",age:18,sex:"男"}
// 创建组件
class Person extends React.Component {
render() {
const {name,age,sex} = this.props
return (
<div>
<span>{name},</span>
<span>{age},</span>
<span>{sex}</span>
</div>
)
}
}
// 对标签属性进行限制
Person.propTypes = {
name:PropTypes.string.isRequired,//限制name必传且为String类型
age:PropTypes.number,//限制age为number类型
sex:PropTypes.string,//限制sex为String类型
speak:PropTypes.func//限制speak为函数类型
}
Person.defaultProps = {
age:18,//指定age默认值
sex:'保密'//指定sex默认值
}
function speak() {
console.log('hello');
}
// 渲染组件
ReactDOM.render(<Person name="李四" age={20} sex="男" speak={speak}/>,document.querySelector('#app1'))
ReactDOM.render(<Person name="李五"/>,document.querySelector('#app3'))
// 批量传递props/批量传递标签属性
ReactDOM.render(<Person {...obj}/>,document.querySelector('#app2'))
</script>
函数式组件使用props:
<script type="text/babel">
// 假数据
const obj = {name:"张三",age:18,sex:"男"}
// 创建组件
function Person(props) {
const {name,age,sex} = props
return (
<div>
<h1>{name}</h1>
<h2>{age}</h2>
<h2>{sex}</h2>
</div>
)
}
// 对标签属性进行限制
Person.propTypes = {
name:PropTypes.string.isRequired,//限制name必传且为String类型
age:PropTypes.number,//限制age为number类型
sex:PropTypes.string,//限制sex为String类型
speak:PropTypes.func//限制speak为函数类型
}
Person.defaultProps = {
age:18,//指定age默认值
sex:'保密'//指定sex默认值
}
ReactDOM.render(<Person {...obj}/>,document.querySelector('#app2'))
</script>
简写形式:
<!-- 引入prop-type 用于限制props属性标签 -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
// 假数据
const obj = {name:"张三",age:18,sex:"男"}
// 创建组件
class Person extends React.Component {
render() {
const {name,age,sex} = this.props
return (
<div>
<span>{name}</span>
<span>{age}</span>
<span>{sex}</span>
</div>
)
}
// 对标签属性进行限制
static propTypes = {
name:PropTypes.string.isRequired,//限制name必传且为String类型
age:PropTypes.number,//限制age为number类型
sex:PropTypes.string,//限制sex为String类型
speak:PropTypes.func//限制speak为函数类型
}
static defaultProps = {
age:18,//指定age默认值
sex:'保密'//指定sex默认值
}
}
function speak() {
console.log('hello');
}
// 渲染组件
ReactDOM.render(<Person name="李四" age={20} sex="男" speak={speak}/>,document.querySelector('#app1'))
ReactDOM.render(<Person name="李五"/>,document.querySelector('#app3'))
// 批量传递props/批量传递标签属性
ReactDOM.render(<Person {...obj}/>,document.querySelector('#app2'))
</script>
核心属性3:refs | 标识符
通过定义 ref 属性可以给标签添加标识。
1、字符串形式的ref(这种形式已过时,效率不高,官方不建议使用。)
<script type="text/babel">
class Demo extends React.Component {
showData = ()=>{
const {input1,innput2} = this.refs
console.log(input1);
}
render() {
return (
<div>
<input ref="input1" type="text"/>
<br/>
<br/>
<button onClick={this.showData}>点击确认文字</button>
<input ref="input2" type="text"/>
</div>
)
}
}
ReactDOM.render(<Demo/>,document.querySelector('#app'))
</script>
2、回调函数形式的ref(便捷 使用最多)
要点:
currentNode => this.input1 = currentNode
就是给组件实例添加 input1 属性,值为节点- 由于是箭头函数,因此 this 是 render 函数里的 this ,即组件实例
- 回调ref中调用次数问题:原文(如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数null,然后第二次会传入参数 DOM 元素。见官方文档)
<script type="text/babel">
class Demo extends React.Component {
showData = ()=>{
const {input1,input2} = this
console.log(input1);
}
render() {
return (
<div>
<input ref={(currentNode) => {this.input1=currentNode}} type="text"/>
<br/>
<br/>
<button onClick={this.showData}>点击确认文字</button>
<input ref="input2" type="text"/>
</div>
)
}
}
ReactDOM.render(<Demo/>,document.querySelector('#app'))
</script>
3、createRef API(官方推荐使用) 该方式通过调用 React.createRef
返回一个容器(对象)用于存储节点,且一个容器只能存储一个节点。
<script type="text/babel">
class Demo extends React.Component {
myRef = React.createRef()
myRef2 = React.createRef()
showData = ()=>{
console.log(this.myRef);//{current: input}
alert(this.myRef.current.value)
}
render() {
return (
<div>
<input ref={this.myRef} type="text"/>
<input ref={this.myRef2} type="text"/>//测试的,没加事件
<br/>
<br/>
<button onClick={this.showData}>点击确认文字</button>
</div>
)
}
}
ReactDOM.render(<Demo/>,document.querySelector('#app'))
</script>
事件处理
- React 使用自定义事件,而非原生 DOM 事件,即 onClick、onBlur :为了更好的兼容性
- React 的事件通过事件委托方式进行处理:为了高效
- 通过 event.target 可获取触发事件的 DOM 元素:勿过度使用 ref 当触发事件的元素和需要操作的元素为同一个时,可以不使用ref :
class Demo extends React.Component {
showData2 = (event) => {
alert(event.target.value)
}
render() {
return (
<div>
<input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />
</div>
)
}
}
受控组件&非受控组件
非受控组件:现用现取。即需要使用时,再获取节点得到数据 受控组件:类似于 Vue 双向绑定的从视图层绑定到数据层(推荐使用,因为非受控组件需要使用大量的 ref 。)
<script type="text/babel">
class Demo extends React.Component {
handleSubmit = (event) => {
event.preventDefault() //阻止表单默认提交
const {username,password} = this
alert(`${username.value},${password.value}`)
}
render() {
return (
<form action="" onSubmit={this.handleSubmit}>
用户名:<input ref={c => this.username = c} type="text"/>
密码: <input ref={c => this.password = c} type="password"/>
<button>登录</button>
</form>
)
}
}
ReactDOM.render(<Demo/>,document.querySelector('#app'))
</script>
受控组件
<script type="text/babel">
class Demo extends React.Component {
state = {
username:'1',
password:'2'
}
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="" onSubmit={this.handleSubmit}>
用户名:<input onChange = {this.saveUsername} type="text"/>
密码: <input onChange = {this.savePassword} type="password"/>
<button>登录</button>
</form>
)
}
}
ReactDOM.render(<Demo/>,document.querySelector('#app'))
</script>
补充:高阶函数&函数柯里化
**高阶函数:**参数为函数或者返回一个函数的函数,常见的如 Promise、setTimeout、Array.map()等
函数柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式 使用高阶函数简化受控组件
<script type="text/babel">
class Demo extends React.Component {
// 初始化state
state = {
username:'1',
password:'2'
}
// 自定义事件
saveFormData = (dateType)=>{
return (event) => {
this.setState({[dateType]:event.target.value})
}
}
handleSubmit = (event) => {
event.preventDefault() //阻止表单默认提交
const {username,password} = this.state
alert(`${username},${password}`)
}
render() {
return (
<form action="" onSubmit={this.handleSubmit}>
用户名:<input onChange = {this.saveFormData('username')} type="text"/>
密码: <input onChange = {this.saveFormData('password')} type="password"/>
<button>登录</button>
</form>
)
}
}
// 渲染组件
ReactDOM.render(<Demo/>,document.querySelector('#app'))
</script>
第四章: 生命周期(新)
1、初始化阶段
由ReactDOM.render()
触发 —— 初次渲染
constructor()
—— 类组件中的构造函数static getDerivedStateFromProps(props, state)
从props得到一个派生的状态【新增】,即state的值在任何时候都取决于propsrender()
—— 挂载组件componentDidMount()
—— 组件挂载完成 比较常用
2、更新阶段
由组件内部this.setSate()
或父组件重新render触发或强制更新forceUpdate()
getDerivedStateFromProps()
—— 从props得到一个派生的状态 【新增】shouldComponentUpdate()
—— 组件是否应该被更新(默认返回true)render()
—— 挂载组件getSnapshotBeforeUpdate()
—— 在更新之前获取快照【新增】,就是捕捉更新前的状态componentDidUpdate(prevProps, prevState, snapshotValue)
—— 组件完成更新
3、卸载组件
由ReactDOM.unmountComponentAtNode()
触发
componentWillUnmount()
—— 组件即将卸载
** <script type="text/babel">
class Demo extends React.Component {
state = {newsArr:[]}
componentDidMount() {
setInterval(() => {
// 获取状态
let {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>
<ul className="list" ref="list">
{
this.state.newsArr.map((n,index) => {
return <li key={index} className="news">{n}</li>
})
}
</ul>
</div>
)
}
}
ReactDOM.render(<Demo/>,document.querySelector("#app"))
</script>**