前言
公司实习的地方,使用react,然后打算学习一下react,并把基础知识点整理一下,毕业设计的前端,也使用react来搭建前端框架。
这里主要有如下react基础知识点:
- jsx语法
- 组件定义的两种方式:类和方法
- 组件三大属性:state、props、ref(这个为标签属性)
- 组件的组合
- 标签的受控组件和非受控组件
- react的生命周期
- react应用(基于create-react-app脚手架)
- react-axios
- react-router
- react-UI:antdesign
最重要的一个 没讲:redux
代码都已上库到GitHub上。
传送门: https://github.com/fengfanli/react-study
一、helloworld
先使用react写一个helloworld,体验一下。
先导入react开发版本js,有三个,react.development.js、react-dom.development.js、babel.min.js
react.js:React 的核心库
react-dom.js:提供操作DOM 的react 扩展库
babel.js:为babel库,两个作用1、将es6语法转化为es5,2、解析JSX语法代码为纯JS 语法代码的库(这里使用的是后者语法)
<body>
<div id="example"></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"> // 声明:告诉babel.js这里有jsx语法
// 1、创建虚拟DOM元素对象
var vDom = <h1>Hello React</h1> // 不是字符串 jsx 语法
// 2、将虚拟DOM渲染到页面真实DOM容器中
ReactDOM.render(vDom, document.getElementById("example"))
</script>
</body>
页面:
步骤(重点学习的地方):
在< script type=“text/babel”> 中编写:声明:告诉babel.js这里有jsx语法
2. 创建虚拟DOM元素对象:就是定义一个变量,将HTML标签赋值于变量
3. 将虚拟DOM渲染到页面真实DOM容器中
技术点:
- jsx语法:
var vDom = <h1>Hello React</h1>
- 渲染语法:
ReactDOM.render(vDom, document.getElementById("example"))
二、React jsx
2.1 jsx
- 全称: JavaScript XML
- react定义的一种类似于XML的JS扩展语法: XML+JS
- 作用: 用来创建react虚拟DOM(元素)对象
a. var ele = < h1>Hello JSX!< /h1>
b. 注意1: 它不是字符串, 也不是HTML/XML标签
c. 注意2: 它最终产生的就是一个JS对象 - 标签名任意: HTML标签或其它标签
- 标签属性任意: HTML标签属性或其它
- 基本语法规则
a. 遇到 <开头的代码, 以标签的语法解析: html同名标签转换为html同名元素, 其它标签需要特别解析
b. 遇到以 { 开头的代码,以JS语法解析: 标签中的js代码必须用{ }包含 - babel.js的作用
a. 浏览器不能直接解析JSX代码, 需要babel转译为纯JS的代码才能运行
b. 只要用了JSX,都要加上type=“text/babel”, 声明需要babel来处理
2.2 动态展示列表数据
功能: 在页面中遍历一个数组,将其显示在页面中。
问题: 如何将一个数据的数组 转换为 一个标签的数组 很常用,使用数组的map() 方法 和 箭头函数
<div id="example1"></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">
/**
* 功能:动态展示列表数据
* 问题:如何将一个数据的数组 转换为 一个标签的数组 很常用
* 使用数组的map() 方法 和 箭头函数
* */
const names = ['jQuery', 'zepto', 'angular', 'react', 'vue']
// 1、创建虚拟DOM
// li 标签一定要带 key属性,而且还不能一样 否则警告
const ul = (
<ul>
{names.map(name=> <li key={name}>{name}</li>)}
</ul>
)
// 2、渲染虚拟DOM
ReactDOM.render(ul, document.getElementById('example'))
</script>
页面:
步骤:
- 创建虚拟DOM
使用 {} 大括号写法,在里面写js语法,将数组遍历。 - 渲染虚拟DOM(固定形式写法,将定义好的jsx虚拟dom渲染到 真实dom上)
技术点:
3. {} 大括号写法:
4. 数组方法map():js方法,是对数组的遍历,里面为回调函数,方法形式:map(value)
:value 为数组的值,map(value, index)
:value 为数组的值,index为数组下标
2.3 虚拟dom创建的两种方式
- 使用原生js方法:const vDom1 = React.createElement(‘h1’, {id:‘myTitle’},‘hello’)
- 使用jsx方法:const vDom2 = < h3 id={myId.toUpperCase()}>{msg.toLowerCase()}< /h3>
2.4 代码实例
<body>
<div id="test01"></div>
<div id="test02"></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/javascript">
/**
* 需求,两种方式实现虚拟DOM的创建
* 1、使用原生js方法:const vDom1 = React.createElement('h1', {id:'myTitle'},'hello')
* 2、使用jsx方法:const vDom2 = <h3 id={myId.toUpperCase()}>{msg.toLowerCase()}</h3>
*/
const msg = 'I Like you!'
const myId = 'fengfanli'
// 1、创建虚拟DOM元素对象
// const element = React.createElement('h1', {id:'myTitle'},'hello')
const vDom1 = React.createElement('h2', {id: myId.toLowerCase()}, msg.toUpperCase())
// debugger
// 2、将虚拟DOM渲染到页面真实DOM容器中
ReactDOM.render(vDom1, document.getElementById('test01'))
</script>
<script type="text/babel">
// 1、创建虚拟DOM元素对象
const vDom2 = <h3 id={myId.toUpperCase()}>{msg.toLowerCase()}</h3>
// debugger
// 2、将虚拟DOM渲染到页面真实DOM容器中
ReactDOM.render(vDom2, document.getElementById('test02'))
</script>
</body>
页面:
2.5 步骤
- 创建虚拟dom
- 渲染虚拟dom
2.6 技术点
- 渲染虚拟dom的两种方法:原生js和jsx
三、组件化、模块化
3.1 组件
- 理解: 用来实现特定(局部)功能效果的代码集合(html/css/js)
- 为什么: 一个界面的功能更复杂
- 作用: 复用编码, 简化项目编码, 提高运行效率
3.2 组件化
当应用是以多组件的方式实现, 这个应用就是一个组件化的应用。
3.3 模块
- 理解: 向外提供特定功能的js程序, 一般就是一个js文件
- 为什么: js代码更多更复杂
- 作用: 复用js, 简化js的编写, 提高js运行效率
3.4 模块化
当应用的js都以模块来编写的, 这个应用就是一个模块化的应用
四、组件定义两种方式
- 方式一:使用方法定义,没有状态用此种方式(简单组件)
- 方式二:使用类定义,(复杂组件)
4.1 代码实例
<body>
<div id="example1"></div>
<div id="example2"></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、使用方法定义,没有状态用此种方式(简单组件)
* 2、使用类定义,(复杂组件)
*/
// 1.定义组件
//方式1: 工厂函数组件(简单组件):没有状态的组件
function MyComponent() {
return <h2>工厂函数组件(简单组件)</h2>
}
//方式2: ES6类组件(复杂组件)
class MyComponent2 extends React.Component{
render(){
console.log(this)
return <h2>工厂函数组件(简单组件)</h2>
}
}
// 2.渲染组件方式
ReactDOM.render(<MyComponent />, document.getElementById('example1'))
ReactDOM.render(<MyComponent2 />, document.getElementById('example2'))
</script>
</body>
页面:
4.2 技术点
- 两种定义组件的方式:方法(简单组件)、类(复杂组件)
- render()方法,在里面写UI,
五、组件三个属性:state、props、refs
5.1 component_state
5.1.1 需求说明
自定义组件,功能说明如下
- 显示 h2 标题,初始文本为:你喜欢我
- 点击标题更新为:我喜欢你
5.1.2 state
state是组件对象最重要的属性, 值是对象(可以包含多个数据)
组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
初始化状态:
constructor (props) {
super(props)
this.state = {
stateProp1 : value1,
stateProp2 : value2
}
}
4、 读取某个状态值
this.state.statePropertyName
// statePropertyName 自定义名称
5、 更新状态---->组件界面更新
this.setState({
stateProp1 : value1,
stateProp2 : value2
})
5.1.2 代码实例
<body>
<div id="example"></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. 显示 h2 标题,初始文本为:你喜欢我
* 2. 点击标题更新为:我喜欢你
* */
// 1.定义组件
class Like extends React.Component{
// 2)、构造器
constructor(props){
super(props)
// 初始化状态
this.state = {
isLikeMe: false
}
// 将新增方法中的this 强制绑定为组件对象
this.handleClick = this.handleClick.bind(this)
}
// 1)、重写组件类的方法
render(){
// 读取状态
const {isLikeMe} = this.state // 第二种方式
return <h2 onClick={this.handleClick}>{isLikeMe?'你喜欢我':'我喜欢你'}</h2>
}
// 3)、新添加的方法:内部的 this 默认不是组件对象,而是 undefined, 所以要在 构造器中 绑定方法
handleClick(){
console.log('handleClick()',this)
// 得到原有状态 并取反
const isLikeMe = !this.state.isLikeMe
//更新状态
this.setState({isLikeMe})
}
}
// 2.渲染组件
ReactDOM.render(<Like />, document.getElementById('example'))
</script>
</body>
5.1.3 步骤
- 定义组件类。
- 编写**render()**方法,在这里写UI组件。
- 编写构造器,和里面的state属性初始化。
- 然后在编写点击事件,这里需要注意这个方法需要绑定到类上,在构造器上进行bind()方法绑定。
5.1.4 步骤、技术点
- 编写组件类
- render方法
- constructor构造器、state属性
- 编写点击事件,在构造器bind绑定,注意更新state的方法和获取state的关键字
- 渲染组件:ReactDOM.render()
5.2 component_props
5.2.1 需求说明
需求: 自定义用来显示一个人员信息的组件
1). 姓名必须指定
2). 如果性别没有指定, 默认为男
3). 如果年龄没有指定, 默认为18
5.2.2 props
理解
- 每个组件对象都会有props(properties的简写)属性
- 组件标签的所有属性都保存在props中
作用
- 通过标签属性 从组件外向组件内 传递变化的数据
- 注意: 组件内部不要修改props数据
编码操作
- 内部读取某个属性值
this.props.propertyName
- 对props中的属性值进行类型限制和必要性限制
Person.propTypes = {
name: React.PropTypes.string.isRequired,
age: React.PropTypes.number.isRequired
}
3、 扩展属性: 将对象的所有属性通过props传递
<Person {...person}/>
4、 默认属性值
Person.defaultProps = {
name: 'Mary'
}
5、 组件类的构造函数
constructor (props) {
super(props)
console.log(props) // 查看所有属性
}
5.2.3 代码实例
<body>
<div id="example1"></div>
<div id="example2"></div>
<div id="example3"></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/prop-types.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
/**
* props属性 和 prop-types.js prop-types.js: 对值进行设定
* 需求:自定义用来显示一个人员信息的组件,效果如页面,说明
* 1、如果性别没有指定,默认为男
* 2、如果年龄没有指定,默认为18
*/
// 1、定义组件
/**
* 使用 对象组件
* 组件对象有三个 state props refs
**/
class Person extends React.Component{
render(){
console.log(this)
return(
<ul>
<li>姓名:{this.props.name}</li>
<li>年龄:{this.props.age}</li>
<li>性别:{this.props.sex}</li>
</ul>
)
}
}
// 指定属性 默认值
Person.defaultProps = {
age: 18,
sex: '男'
}
// 对 props 中的属性值进行类型限制和必要性限制
Person.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number
}
// 2、渲染组件
const p1 = {
name: 'Tom',
age: 18,
sex: '女'
}
ReactDOM.render(<Person name={p1.name} age={p1.age} sex={p1.sex} />, document.getElementById("example1"))
const p2 = {
name: 'Jack',
}
ReactDOM.render(<Person name={p2.name} age={20} />, document.getElementById("example2"))
/**
* ... 的作用
* 1、打包
* function fn(...as){} fn(1, 2, 3) : 打包的意思
* 2、解包
* const arr1 = [1, 2, 3] const arr2 = [6, ...arr1, 9] :解包的意思
*
* @type {
{name: string}}
*/
const p3 = {
name: 'Feng',
}
ReactDOM.render(<Person {...p3} />, document.getElementById("example3"))
</script>
</body>
页面:
5.2.4 步骤、技术点
新添加了一个js:prop-types,这个库是在子组件中,对父组件传给子组件的属性值进行加以设定。
- 在渲染虚拟dom,
ReactDOM.render(<Person />, document.getElementById("example3"))
向 HTML标签一样,添加属性,往里面传值,在组件类 Person中通过this.props.属性名
获取值。 - 扩展运算符(三点运算符)
...
:将对象的所有属性通过props传递
5.2.5 问题:区别state和props
请区别一下组件的props和state属性
- state: 组件自身内部可变化的数据
- props: 从组件外部向组件内部传递数据, 组件内部只读不修改
5.3 component_refs
5.3.1 需求描述
需求: 自定义组件, 功能说明如下:
- 点击按钮, 提示第一个输入框中的值
- 当第2个输入框失去焦点时, 提示这个输入框中的值
5.3.2 refs属性
组件内的标签都可以定义ref属性来标识自己
a.<input type="text" ref={input => this.msgInput = input}/>
b. 回调函数在组件初始化渲染完或卸载时自动调用在组件中可以通过
this.msgInput
来得到对应的真实DOM元素作用: 通过ref获取组件内容特定标签对象, 进行读取其相关数据
5.3.3 事件处理
- 通过onXxx属性指定组件的事件处理函数(注意大小写)
a. React使用的是自定义(合成)事件, 而不是使用的原生DOM事件
b. React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) - 通过event.target得到发生事件的DOM元素对象
<input onFocus={this.handleClick}/>
handleFocus(event) {
event.target //返回input对象
}
5.3.4 强烈注意
组件内置的方法中的this为组件对象
在组件类中自定义的方法中this为null
a. 强制绑定this: 通过函数对象的bind()
b. 箭头函数(ES6模块化编码时才能使用)
5.3.5 代码实例
<body>
<div id="example1"></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/prop-types.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
/**
* 事件 与 ref
* 需求:自定义组件,功能说明
* 1、界面如图所示
* 2、点击按钮,提示第一个输入框中的值
* 3、当第二个输入框失去焦点时,提示这个输入框中的值
*/
// 1、创建组件
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.showInput = this.showInput.bind(this)
this.handleBlur = this.handleBlur.bind(this)
}
showInput() {
const value = this.refs.content.value;
alert(value)
alert(this.input.value)
}
// 事件都有一个固定的形参
handleBlur(event) {
console.log(event)
console.log(event.target) //指的是标签
// event.target : 指的是 input 表单框
alert(event.target.value)
}
/* 最后一个比较特别,操作的 dom 元素,是发生事件的元素。*/
render() {
return (
<div>
<input type="text" ref="content"/>
<input type="text" ref={input => this.input = input}/>
<button onClick={this.showInput}>提升输入</button>
<input type="text" onBlur={this.handleBlur} placeholder="失去焦点提示内容"/>
</div>
)
}
}
// 2、渲染组件
ReactDOM.render(<MyComponent/>, document.getElementById("example1"))
</script>
</body>
展示:
5.3.6 步骤、技术点
大体步骤与前一样
- 点击事件要在constructor构造器中进行bind()方法绑定。
- ref使用方法一:
ref属性
的value值为唯一值,然后在点击事件中 通过this.refs.ref属性名
获取 - ref使用方法二:
ref属性
的value值为回调函数{input => this.inputMsg = input}
,返回一个,然后在点击事件中 通过this.inputMsg
获取
六、组件的组合
6.1 需求描述
功能: 组件化实现此功能
- 显示所有todo列表
- 输入文本, 点击按钮显示到列表的首位, 并清除输入的文本
6.2 代码实例
<body>
<div id="example"></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/prop-types.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
/**
* 界面中有一个文本框和按钮,按钮中显示下面的几个数据
* 下面是一个 ul 列表,默认遍历 数组中的数据。
* 事件:点击按钮,文本框中的数据出现在下面中的 ul 列表中
*/
/**
*问题:数据保存在哪个组件内?
* 看数据是某个组件需要(给它),还是某些组件需要(给共同的父组件)
*问题2:需要在子组件中改变父组件的状态
* 子组件不能直接改变父组件的状态
* 状态在哪个组件,更新状态的行为(方法)就应该定义在哪个组件
* 解决:父组件定义函数,传递给子组件,子组件调用
*
*组件化编写功能的流程
* 1、拆分组件
* 2、实现静态组件(只有静态界面,没有动态数据和交互)
* 3、实现动态组件
* 1)实现初始化数据动态显示
* 2) 实现交互功能
*/
// 主界面
class Counter extends React.Component {
constructor(props) {
super(props)
// 初始化状态
this.state = {
todos: ['吃饭', '睡觉', '打豆豆']
}
this.addTodos = this.addTodos.bind(this)
}
// 定义一个添加的函数
addTodos(todo) {
// this.state.todos.unshift(todo) //不能这么做
const {todos} = this.state
todos.unshift(todo)
// 更新状态
this.setState({todos})
}
render() {
const {todos} = this.state
return (
<div>
<h1>Simple TODO List</h1>
<Add count={todos.length} addTodos={this.addTodos}/>
<List todos={todos}/>
</div>
)
}
}
// 子界面
class Add extends React.Component {
constructor(prop) {
super(prop)
this.addData = this.addData.bind(this)
}
render() {
return (
<div>
<input type="text" ref={input => this.todoInput = input}/>
<button onClick={this.addData}>add # {this.props.count + 1} </button>
</div>
)
}
addData() {
// 1、读取输入的数据
const data = this.todoInput.value.trim();
// 2、检查合法性
if (!data) {
return
}
// 3、添加
console.log(data)
this.props.addTodos(data)
// 4、清除输入
this.todoInput.value = ''
}
}
// 数据校验,放在上面出错
Add.propTypes = {
count: PropTypes.number.isRequired,
addTodos: PropTypes.func.isRequired
}
// 子界面
class List extends React.Component {
render() {
const {todos} = this.props;
/* => : 箭头函数的意义,有两个:函数、return, 这里如果加 {}:方法体 的话,里面一定要有 return*/
/*todos.map((value, index)=><li key={index}>{value}</li>)*/
return (
<div>
<ul>
{todos.map((value, index) => {
return <li key={index}>{value}</li>
})}
</ul>
</div>
)
}
}
List.propTypes = {
todos: PropTypes.array.isRequired
}
ReactDOM.render(<Counter/>, document.getElementById('example'))
</script>
</body>
6.3 技术点(注意:数据在哪,操作方法就在哪)
- 设计三个组件,一个主页面Counter、一个添加页面Add、一个列表页面List
- state数据在主页面中,todos数组会传给List组件,todos长度、添加的方法addTodos()会传给Add组件。
- 注意,方法也可以作为数据传给子组件。通过
this.props
进行接收。 - 在Add组件中调用父组件的添加方法。
- 在List组件中通过
this.props.todos
获取数据,然后在render()方法中进行遍历渲染出来。
6.4 React工具使用,查看三个属性
在Google中安装扩展软件,安装React扩展工具。可以查看自己定义的组件,以及三大属性,其实只能看到state和props,这两个是在组件上,ref是在标签上,这个只能在元素中查看。
看截图:
Counter类组件
Add类组件
6.5 功能界面的组件化编码流程(无比重要)
拆分组件: 拆分界面,抽取组件
实现静态组件: 使用组件实现静态页面效果
实现动态组件
a. 动态显示初始化数据
b. 交互功能(从绑定事件监听开始)
七、收集表单数据
7.1 需求描述
需求: 自定义包含表单的组件
- 输入用户名密码后, 点击登陆提示输入信息
- 不提交表单
7.2 理解
问题: 在react应用中, 如何收集表单输入数据
包含表单的组件分类
a. 受控组件: 表单项输入数据能自动收集成状态
b. 非受控组件: 需要时才手动读取表单输入框中的数据
7.3 代码实例
<body>
<div id="example"></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/prop-types.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
/**
* 需求: 自定义包含表单的组件
* 1、界面如下所示
* 2、输入用户名密码后,点击登陆提示输入信息
* 3、不提交表单 event.preventDefault()
*/
class LoginForm extends React.Component {
constructor(prop) {
super(prop)
this.state = {
pwd: ''
}
this.handleLogin = this.handleLogin.bind(this)
this.handleChange = this.handleChange.bind(this)
}
render() {
return (
/**
* 用户名 为非受控组件
* 密码 为受控组件
*/
<form action="/test" onSubmit={this.handleLogin}>
用户名:<input type="text" ref={input => this.username = input}/>
密码: <input type="password" value={this.state.pwd} ref={input => this.password = input}
onChange={this.handleChange}/>
<input type="submit" value="登陆"/>
</form>
)
}
handleLogin(event) {
const uname = this.username.value // 操作DOM
const {pwd} = this.state // 操作react 属性
alert(`准备提交的用户名为${uname}, 密码${pwd}`)
// 阻止事件的默认行为(提交)
event.preventDefault()
}
handleChange(event) {
// 读取输入框的值
const password = event.target.value
// 更新pwd的状态
this.setState({pwd: password})
}
}
ReactDOM.render(<LoginForm/>, document.getElementById('example'))
</script>
</body>
页面:
7.4 步骤、技术点
步骤与前一致
- 这里需要理解**受控组件和非受控组件**。
- 受控组件:通过onChange()事件,将input值与state的值链接起来,进行统一。
八、组件生命周期
8.1 需求效果
需求: 自定义组件
- 让指定的文本做显示/隐藏的渐变动画
- 切换持续时间为2S
- 点击按钮从界面中移除组件界面
8.2 理解
- 组件对象从创建到死亡它会经历特定的生命周期阶段
- React组件对象包含一系列的勾子函数(生命周期回调函数), 在生命周期特定时刻回调
- 我们在定义组件时, 可以重写特定的生命周期回调函数, 做特定的工作
8.3 生命周期流程图
8.4 生命周期详述
- 组件的三个生命周期状态:
- Mount:插入真实 DOM
- Update:被重新渲染
- Unmount:被移出真实 DOM
- React 为每个状态都提供了勾子(hook)函数
- componentWillMount()(舍弃)
- componentDidMount()
- componentWillUpdate()
- componentDidUpdate()
- componentWillUnmount()
- 生命周期流程:
a. 第一次初始化渲染显示: ReactDOM.render()- constructor(): 创建对象初始化state
- componentWillMount() : 将要插入回调
- render() : 用于插入虚拟DOM回调
- componentDidMount() : 已经插入回调
b. 每次更新state: this.setSate() - componentWillUpdate() : 将要更新回调
- render() : 更新(重新渲染)
- componentDidUpdate() : 已经更新回调
c. 移除组件: ReactDOM.unmountComponentAtNode(containerDom) - componentWillUnmount() : 组件将要被移除回调
8.5 重要的勾子
- render(): 初始化渲染或更新渲染调用
- componentDidMount(): 开启监听, 发送ajax请求
- componentWillUnmount(): 做一些收尾工作, 如: 清理定时器
- componentWillReceiveProps(): 后面需要时讲
九、虚拟DOM与DOM Diff算法
9.1 代码实例
class HelloWorld extends React.Component {
constructor(props) {
super(props)
this.state = {
date: new Date()
}
}
componentDidMount () {
setInterval(() => {
this.setState({
date: new Date()
})
}, 1000)
}
render () {
console.log('render()')
return (
<p>
Hello, <input type="text" placeholder="Your name here"/>!
It is {this.state.date.toTimeString()}
</p>
)
}
}
ReactDOM.render(
<HelloWorld/>,
document.getElementById('example')
)
9.2 基本原理图
十、react引用(基于react脚手架)
10.1 使用create-react-app创建react应用
10.1.1 react脚手架
xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目
a. 包含了所有需要的配置
b. 指定好了所有的依赖
c. 可以直接安装/编译/运行一个简单效果react提供了一个用于创建react项目的脚手架库:
create-react-app
项目的整体技术架构为:
react + webpack + es6 + eslint
使用脚手架开发的项目的特点:
模块化, 组件化, 工程化
10.1.2 创建项目并启动
npm install -g create-react-app
create-react-app hello-react
cd hello-react
npm start
10.1.3 react脚手架项目结构
ReactNews
|--node_modules---第三方依赖模块文件夹
|--public
|-- index.html-----------------主页面
|--scripts
|-- build.js-------------------build打包引用配置
|-- start.js-------------------start运行引用配置
|--src------------源码文件夹
|--components-----------------react组件
|--index.js-------------------应用入口js
|--.gitignore------git版本管制忽略的配置
|--package.json----应用包配置文件
|--README.md-------应用描述说明的readme文件
10.2 项目结构
项目入口 js index.js
10.2 demo:评论管理
效果:
拆分组件:
应用组件: App
- state: comments/array
添加评论组件: CommentAdd - state: username/string, content/string
- props: add/func
评论列表组件: CommentList - props: comment/object, delete/func, index/number
评论项组件: CommentItem - props: comments/array, delete/func
实现静态组件
实现动态组件
1、 动态展示初始化数据
- 初始化状态数据
- 传递属性数据
2、 响应用户操作, 更新组件界面
- 绑定事件监听, 并处理
- 更新state
10.3 demo:评论管理
项目目录:
代码上库啦。
十一、react ajax
11.1 理解
11.1.1 前置说明
- React本身只关注于界面, 并不包含发送ajax请求的代码
- 前端应用需要通过ajax请求与后台进行交互(json数据)
- react应用中需要集成第三方ajax库(或自己封装)
11.1.2 常用的ajax请求库
jQuery: 比较重, 如果需要另外引入不建议使用
axios: 轻量级, 建议使用
a. 封装XmlHttpRequest对象的ajax
b. promise风格
c. 可以用在浏览器端和node服务器端fetch: 原生函数, 但老版本浏览器不支持
a. 不再使用XmlHttpRequest对象提交ajax请求
b. 为了兼容低版本的浏览器, 可以引入兼容库fetch.js
11.1.3 效果
需求:
- 界面效果如下
- 根据指定的关键字在github上搜索匹配的最受关注的库
- 显示库名, 点击链接查看库
- 测试接口: https://api.github.com/search/repositories?q=r&sort=stars
在这里插入图片描述
11.1.4 代码实例
<body>
<div id="example"></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/prop-types.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/javascript" src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script>
<script type="text/babel">
/**
* 需求:
* 1、界面效果如下,
* 2、根据指定的关键字在GitHub上搜索匹配的最受关注的库
* 3、显示库名,点击链接查看库
* 4、测试接口:https://api.github.com/search/repositories?q=r&sort=stars
*/
class MostStarRepo extends React.Component {
state = {
repoName: '',
repoUrl: ''
}
// 最后发请求
componentDidMount() {
// 使用 axios 发送 异步的Ajax 请求 re:可以改变可以传 sort=stars:排序模式
const url = 'https://api.github.com/search/repositories?q=re&sort=stars'
axios.get(url)
.then(response => {
const result = response.data
console.log(response);
// 得到数据
const {name, html_url} = result.items[0]
// 更新状态
this.setState({
repoName: name,
repoUrl: html_url
})
})
// 错误信息
.catch(error => {
alert(error.message)
})
// 使用 fetch 发送异步的 Ajax 请求
/*fetch(url)
.then(response => {
return response.json()
})
.then(data => {
// 得到数据
const {name, html_url} = data.items[0]
// 更新状态
this.setState({
repoName: name,
repoUrl: html_url
})
})*/
}
render() {
const {repoName, repoUrl} = this.state
if (!repoName) {
return <h2>Loading</h2>
} else {
return <h2>Most star repo is <a href={repoUrl}>{repoName}</a></h2>
}
}
}
ReactDOM.render(<MostStarRepo/>, document.getElementById('example'))
</script>
</body>
11.2 axios
11.2.1 文档
https://github.com/axios/axios
11.2.1 相关API
1、 GET请求
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
2、 POST请求
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
11.3 demo: github users
11.3.1 需求描述
11.3.2 拆分组件
App
- state: searchName/string
Search - props: setSearchName/func
List
- props: searchName/string
- state: firstView/bool, loading/bool, users/array, errMsg/string
11.3.3 代码实例
上库啦······ 库中代码为:13_react-app
- 编写静态组件
- 编写动态组件:
componentWillReceiveProps(nextProps)
: 监视接收到新的props, 发送ajax
使用axios库发送ajax请求
11.4 Fetch
11.4.1 文档
11.4.2 相关API
1、 GET请求
fetch(url).then(function(response) {
return response.json()
}).then(function(data) {
console.log(data)
}).catch(function(e) {
console.log(e)
});
2、 POST请求
fetch(url, {
method: "POST",
body: JSON.stringify(data),
}).then(function(data) {
console.log(data)
}).catch(function(e) {
console.log(e)
})
十二、几个重要技术总结
12.1 组件间通信
12.1.1 方式一: 通过props传递
- 共同的数据放在父组件上, 特有的数据放在自己组件内部(state)
- 通过props可以传递一般数据和函数数据, 只能一层一层传递
- 一般数据–>父组件传递数据给子组件–>子组件读取数据
- 函数数据–>子组件传递数据给父组件–>子组件调用函数
12.1.2 方式二: 使用消息订阅(subscribe)-发布(publish)机制
- 工具库: PubSubJS
- 下载: npm install pubsub-js --save
- 使用:
import PubSub from ‘pubsub-js’ //引入
PubSub.subscribe(‘delete’, function(data){ }); //订阅
PubSub.publish(‘delete’, data) //发布消息
12.1.3 方式三: redux
后面专门讲解
12.2 事件监听理解
12.2.1 原生DOM事件
- 绑定事件监听
a. 事件名(类型): 只有有限的几个, 不能随便写
b. 回调函数 - 触发事件
a. 用户操作界面
b. 事件名(类型)
c. 数据()
12.2.2 自定义事件(消息机制)
- 绑定事件监听
a. 事件名(类型): 任意
b. 回调函数: 通过形参接收数据, 在函数体处理事件 - 触发事件(编码)
a. 事件名(类型): 与绑定的事件监听的事件名一致
b. 数据: 会自动传递给回调函数
12.3 ES6常用新语法
- 定义常量/变量: const/let
- 解构赋值: let {a, b} = this.props import {aa} from ‘xxx’
- 对象的简洁表达: {a, b}
- 箭头函数:
a. 常用场景
组件的自定义方法: xxx = () => {}
参数匿名函数
b. 优点:
简洁
没有自己的this,使用引用this查找的是外部this - 扩展(三点)运算符: 拆解对象(const MyProps = {}, )
- 类: class/extends/constructor/super
- ES6模块化: export default | import
十三、react-router
13.1 项目理解
13.1.1 react-router的理解
- react的一个插件库
- 专门用来实现一个SPA应用
- 基于react的项目基本都会用到此库
13.1.2 SPA的理解
- 单页Web应用(single page web application,SPA)
- 整个应用只有一个完整的页面
- 点击页面中的链接不会刷新页面, 本身也不会向服务器发请求
- 当点击路由链接时, 只会做页面的局部更新
- 数据都需要通过ajax请求获取, 并在前端异步展现
13.1.3 路由的理解
什么是路由?
a. 一个路由就是一个映射关系(key:value)
b. key为路由路径, value可能是function/component路由分类
a. 后台路由: node服务器端路由, value是function, 用来处理客户端提交的请求并返回一个响应数据
b. 前台路由: 浏览器端路由, value是component, 当请求的是路由path时, 浏览器端前没有发送http请求, 但界面会更新显示对应的组件后台路由
a. 注册路由:router.get(path, function(req, res))
b. 当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据前端路由
a. 注册路由:< Route path="/about" component={About}>
b. 当浏览器的hash变为#about时, 当前路由组件就会变为About组件
13.1.4 前端路由的实现
history库
a. 网址: https://github.com/ReactTraining/history
b. 管理浏览器会话历史(history)的工具库
c. 包装的是原生BOM中window.history和window.location.hashhistory API
a. History.createBrowserHistory(): 得到封装window.history的管理对象
b. History.createHashHistory(): 得到封装window.location.hash的管理对象
c. history.push(): 添加一个新的历史记录
d. history.replace(): 用一个新的历史记录替换当前的记录
e. history.goBack(): 回退到上一个历史记录
f. history.goForword(): 前进到下一个历史记录
g. history.listen(function(location){}): 监视历史记录的变化
13.2 react-router相关API
13.2.1 react-router组件
<BrowserRouter>
<HashRouter>
<Route>
<Redirect>
<Link>
<NavLink>
<Switch>
13.2.2 其它
- history对象
- match对象
- withRouter函数
13.3 基本路由使用
13.3.1 效果
13.3.2 准备
- 下载
react-router
:npm install --save react-router@4
- 引入
bootstrap.css
:<link rel="stylesheet" href="/css/bootstrap.css">
13.4 嵌套路由使用
13.4.1 效果
13.5 向路由组件传递参数数据
13.5.1 效果
13.6 多种路由跳转方式(重要)
- 一种为标签方式跳转
- 一种为事件方式跳转:添加事件,需要借助history对象
原来没加router时,组件的属性有 props、state
加上route时,在Route下的组件上,有了history、location、match三个对象。
13.6.1 效果
13.6.2 代码实例
都上库啦。
代码为 14_react_router,三级路由都在这一个项目中。
十四、React UI
14.1 最流行的开源React UI组件库
14.1.1 material-ui(国外)
官网: http://www.material-ui.com/#/
github: https://github.com/callemall/material-ui
14.1.2 ant-design(国内蚂蚁金服)
PC官网: https://ant.design/index-cn
Github: https://github.com/ant-design/ant-design/
14.2 ant-design-mobile使用入门
14.2.1 效果
14.2.2 使用create-react-app创建react应用
npm install create-react-app -g
create-react-app antm-demo
cd antm-demo
npm start
14.2.3 搭建antd-mobile的基本开发环境
1、
下载
npm install antd-mobile --save
2、 src/App.jsx
import React, {Component} from 'react'
// 分别引入需要使用的组件
import Button from 'antd-mobile/lib/button'
import Toast from 'antd-mobile/lib/toast'
export default class App extends Component {
handleClick = () => {
Toast.info('提交成功', 2)
}
render() {
return (
<div>
<Button type="primary" onClick={this.handleClick}>提交</Button>
</div>
)
}
}
3、
src/index.js
import React from 'react';
import ReactDOM from 'react-dom'
import App from "./App"
// 引入整体css
import 'antd-mobile/dist/antd-mobile.css'
ReactDOM.render(<App />, document.getElementById('root'))
4、
index.html
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
<script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script>
<script>
if ('addEventListener' in document) {
document.addEventListener('DOMContentLoaded', function() {
FastClick.attach(document.body);
}, false);
}
if(!window.Promise) {
document.writeln('<script src="https://as.alipayobjects.com/g/component/es6-promise/3.2.2/es6-promise.min.js"'+'>'+'<'+'/'+'script>');
}
</script>
7.2.4. 实现按需打包(组件js/css)
1、 下载依赖包
yarn add react-app-rewired customize-cra babel-plugin-import --save
2、 修改默认配置:
package.json
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test --env=jsdom"
}
3、 config-overrides.js
const {injectBabelPlugin} = require('react-app-rewired');
module.exports = function override(config, env) {
config = injectBabelPlugin(['import', {libraryName: 'antd-mobile', style: 'css'}], config);
return config;
};
4、 编码
// import 'antd-mobile/dist/antd-mobile.css'
// import Button from 'antd-mobile/lib/button'
// import Toast from 'antd-mobile/lib/toast'
import {Button, Toast} from 'antd-mobile'
扩展,数组方法
1. map()
遍历数组
/**
* 功能:动态展示列表数据
*
* 问题:如何将一个数据的数组 转换为 一个标签的数组 很常用
* 使用数组的map() 方法 和 箭头函数
* */
const names = ['jQuery', 'zepto', 'angular', 'react', 'vue']
// 1、创建虚拟DOM
// li 标签一定要带 key属性,而且还不能一样 否则警告
const ul = (
<ul>
{names.map(name=> <li key={name}>{name}</li>)}
</ul>
)
2. unshift()
在数组的头部添加新值
3. splice()
splice(value, 1):删除
splice(value, 0, value2):替换
splice(value): 添加
4. filter()
过滤数组,将下标不等于0的过滤掉,返回一个新的数组。
const comments = [
{username: 'Tom', content: 'React 挺好的!'},
{username: 'Jack', content: 'React 挺简单的!'}
];
state.filter((comment, index) => {
return (index !== 0)
});