共计 10041 个字符,预计需要花费 26 分钟才能阅读完成。
React 总结
入门
特点
Declarative(声明式编码)
Component-Based(组件化编码)
Learn Once, Write Anywhere(支持客户端与服务器渲染)
高效
高效原因
虚拟(virtual)DOM, 不总是直接操作 DOM
DOM Diff 算法, 最小化页面重绘, 减少重排重绘的次数
使用
相关 js 库
react.js: React 的核心库
react-dom.js: 提供操作 DOM 的 react 扩展库
babel.min.js: 解析 JSX 语法代码转为纯 JS 语法代码的库
编码
// 引入相关库
<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
// 创建虚拟 DOM 元素
const vDom = <h1>Hello React</h1> // 千万不要加引号
// 渲染虚拟 DOM 到页面真实 DOM 容器中
ReactDOM.render(vDom, document.getElementById(‘test’))
</script>
虚拟 Dom
React 提供了一些 API 来创建一种 特别 的一般 js 对象
var element = React.createElement(‘h1′, {id:’myTitle’},’hello’)
上面创建的就是一个简单的虚拟 DOM 对象
虚拟 DOM 对象最终都会被 React 转换为真实的 DOM
我们编码时基本只需要操作 react 的虚拟 DOM 相关数据, react 会转换为真实 DOM 变化而更新界面
创建虚拟 Dom 方法
<body>
<div id=”test”></div>
</body>
<script type=”text/babel”>
// 创建虚拟 DOM 对象
const vDom1 = React.createElement(‘p’,{id: ‘myId1’, className: ‘myClass1’}, ‘ 珂朵莉 ’,vDom3);
// 将虚拟 DOM 对象插入到页面的指定容器中, 渲染虚拟 DOM(元素)
ReactDOM.render(vDom1, document.getElementById(‘test1’)); // 方法一
// 方法二
const id = ‘myId2’;
const content = ‘ 我永远喜欢珂朵莉 ’;
const vDom3 = React.createElement(‘span’, {}, ‘daisiki’);
const vDom2 = <div><h2 id={id} className=”myClass2″>{content}</h2>{vDom3}</div>
// 将虚拟 DOM 对象插入到页面的指定容器中
ReactDOM.render(vDom2, document.getElementById(‘test2’));
const list = [‘1’, ‘2’, ‘3’, ‘4’];
ReactDOM.render(
<ul>
{list.map(( item,index) => <li key={index}>{item}</li> ) }
</ul>
,document.getElementById(‘test3’))
<script>
JSX
1)全称: JavaScript XML
2)react 定义的一种类似于 XML 的 JS 扩展语法: XML+JS
3)作用: 用来创建 react 虚拟 DOM(元素)对象
a.var ele = <h1>Hello JSX!</h1>
b. 注意 1: 它不是字符串, 也不是 HTML/XML 标签
c. 注意 2: 它最终产生的就是一个 JS 对象
4)标签名任意: HTML 标签或其它标签
5)标签属性任意: HTML 标签属性或其它
6)基本语法规则
a. 遇到 < 开头的代码, 以标签的语法解析: html 同名标签转换为 html 同名元素, 其它标签需要特别解析
b. 遇到以 {开头的代码,以 JS 语法解析: 标签中的 js 代码必须用 {} 包含, // 待定:* if/for 循环 不能用
7)babel.js 的作用
a. 浏览器不能直接解析 JSX 代码, 需要 babel 转译为纯 JS 的代码才能运行
b. 只要用了 JSX,都要加上 type=”text/babel”, 声明需要 babel 来处理
React 面向组件编程
自定义组件(Component) :
定义组件(2 种方式)
/* 方式 1: 工厂函数组件(简单组件)*/
function MyComponent () {
return <h2> 工厂函数组件(简单组件)</h2>
}
/* 方式 2: ES6 类组件(复杂组件)*/
class MyComponent2 extends React.Component {
render () {
console.log(this) // MyComponent2 的实例对象
return <h2>ES6 类组件(复杂组件)</h2>
}
}
渲染组件标签
ReactDOM.render(<MyComponent />, document.getElementById(‘example1’))
注意
组件名必须首字母大写
虚拟 DOM 元素只能有一个根元素
虚拟 DOM 元素必须有结束标签
单标签 <xx />: 当组件没有内容时,直接用单标签
双标签 <xx></xx> : 当组件有内容时,可以用双标签
render()渲染组件标签的基本流程
React 内部会创建组件实例对象
得到包含的虚拟 DOM 并解析为真实 DOM
插入到指定的页面元素内部
React 的 三大属性
state
state 是组件对象最重要的属性, 值是对象(可以包含多个数据)
组件被称为 ” 状态机 ”, 通过更新组件的 state 来更新对应的页面显示(重新渲染组件)
用法
初始化
在 constructor 中定义 this.state = {xxx: xxx},
不在 constructor 定义:state = {xxx: xxx}
读取 this.state.xxx
更新 this.setState({xxx: xxx})
注意
setState 只在合成事件和钩子函数中是“异步”的,在原生事件和 setTimeout 中都是同步的。
如果需要获取“异步”场景的 setState 的值 –> this.setState(partial, callback) 在 callback 中拿到最新的值
如果要在“异步”场景更新多次 setState –> this.setState((prevState, props) => {return newState})
props
每个组件对象都会有 props(properties 的简写)属性
组件标签的所有属性都保存在 props 中
作用
通过标签属性从组件外向组件内传递变化的数据
注意: 组件内部不要修改 props 数据
使用
约束属性的类型和必要性 static propTypes = {xxx: PropTypes.func.isRequired}
定义属性的默认值 static defaultProps = {xxx: xxx}
内部读取某个属性值,获取组件外向组件内传递的标签属性 this.props.xxx / const {name, age, sex} = this.props;
扩展属性: 将对象的所有属性通过 props 传递,设置 <List name={xxx}> / <Person {…person}/>
默认属性值:原来方案 Person.defaultProps = {name: ‘Mary’} 最新方案:static defaultProps = {
name: ‘Mary’}
组件类的构造函数
constructor (props) {
super(props)
console.log(props) // 查看所有属性
}
区别一下组件的 props 和 state 属性
state: 组件自身内部可变化的数据
props: 从组件外部向组件内部传递数据, 组件内部只读不修改
refs
介绍
组件内的标签都可以定义 ref 属性来标识自己,用来获取 DOM 元素 在组件中可以通过 this.msgInput 来得到对应的真实 DOM 元素
a. <input type=”text” ref={this.createRef}/> (推荐使用)
b. <input type=”text” ref={input => this.funcRef = input}/>
c. <input type=”text” ref=”stringRef” /> (即将废弃)
d. 回调函数在组件初始化渲染完或卸载时自动调用
作用: 通过 ref 获取组件内容特定标签对象, 进行读取其相关数据
使用
1.<input type=”text” ref={this.createRef}/>
2.<input type=”text” ref={(input) => this.funcRef = input}/>
3.<input type=”text” ref=”stringRef”/>
constructor(props) {
super(props);
this.createRef = React.createRef();
}
// 获取 input 标签的值
1. this.createRef.current
2. console.log(this.funcRef);
3. console.log(this.refs.stringRef);
事件处理
通过 onXxx 属性指定组件的事件处理函数(注意大小写)
a.React 使用的是自定义 (合成) 事件, 而不是使用的原生 DOM 事件
b.React 中的事件是通过事件委托方式处理的(委托给组件最外层的元素)
通过 event.target 得到发生事件的 DOM 元素对象
<input onFocus={this.handleClick}/>
handleFocus(event) {
event.target // 返回 input 对象
}
强烈注意
1)组件内置的方法中的 this 为组件对象
2)在组件类中自定义的方法中 this 为 undefined
a. 强制绑定 this: 通过函数对象的 bind()
b. 箭头函数(ES6 模块化编码时才能使用)
React 组件中函数 this 指向规则
组件内置的方法中的 this 为组件的实例对象
在组件类中自定义的方法中 this 为 undefined
通过函数对象的 bind()强制绑定 this 指向为组件实例对象,也就是 this
箭头函数 this 就能指向实例对象(ES6 模块化编码时才能使用)
工厂函数组件中 this 为 undefined
ES6 类组件(复杂组件)this 为组件的实例对象
表单的组件分类
受控组件: 表单项输入数据能自动收集成状态(onChange)
非受控组件: 需要时才手动读取表单输入框中的数据(onClick)
组件化编码流程和套路
组件编码流程:
1. 拆分组件:根据页面功能拆分
APP
AddTodo
TodoList
2. 实现静态组件
先实现大的组件(最外层的),再实现里面的组件
先有一个基本的显示效果
3. 实现动态组件
1. 需不需要定义状态数据?看页面是否有变化,有变化就要定义状态数据
2. 状态数据定义在哪里?APP
如果数据是单个组件需要,就定义在单个组件中
如果数据是多个组件需要,就定义在公共的父组件中,也就是引用了那几个多组件的父组件
3. 状态数据定义为什么?
定义成对象、数组、基本数据类型。
方便插入数据和遍历展示,所以用数组
4. 子组件如何操作父组件的数据
父组件定义操作数据的方法(数据再哪,操作数据的方法就在在哪)
父组件将操作数据的方法传给子组件,子组件调用就能修改父组件的数据
组件的生命周期函数
理解
组件对象从创建到死亡它会经历特定的生命周期阶段
React 组件对象包含一系列的勾子函数(生命周期回调函数), 在生命周期特定时刻回调
我们在定义组件时, 可以重写特定的生命周期回调函数, 做特定的工作
生命周期详述
组件的三个生命周期状态:
Mount:插入真实 DOM,渲染时
Update:被重新渲染,更新时
Unmount:被移出真实 DO,, 卸载时
React 为每个状态都提供了勾子 (hook) 函数
componentWillMount()
componentDidMount()
componentWillUpdate()
componentDidUpdate()
componentWillUnmount()
生命周期流程:
第一次初始化渲染显示: ReactDOM.render()
constructor(): 创建对象初始化 state
componentWillMount() : 将要插入回调
render() : 用于插入虚拟 DOM 回调
componentDidMount() : 已经插入回调
每次更新 state: this.setSate()
componentWillUpdate() : 将要更新回调
render() : 更新(重新渲染)
componentDidUpdate() : 已经更新回调
移除组件: ReactDOM.unmountComponentAtNode(containerDom)
componentWillUnmount() : 组件将要被移除回调
生命周期函数作用:
constructor()`
* 初始化 state
* 修改函数的 this 指向
* 只会执行一次
static getDerivedStateFromProps(nextProps, prevState)`
* 一般不用
* 使组件能够根据 props 的更改来更新其内部状态
render()`
* 渲染组件
componentDidMount()`
* 发送 ajax 请求
* 执行异步任务
* 只会执行一次
shouldComponentUpdate(nextProps, nextState)`
* react 性能优化,减少不必要的 render
getSnapshotBeforeUpdate(prevProps, prevState)`
* 一般不用
* 可以在渲染之前得到 DOM 对象从而获取一些信息
componentDidUpdate(prevProps, prevState, snapshot)`
* 更新组件完成时对 DOM 进行操作
* 发送 ajax 请求(注意不要陷入死循环)
componentWillUnmount()`
* 清除定时器,收尾工作等
重要的生命周期(必须掌握)
constructor()
render() 初始化渲染或更新渲染调用
componentDidMount() 开启监听, 发送 ajax 请求
componentWillUnmount() 做一些收尾工作, 如: 清理定时器
componentDidUpdate() 可以获取更新后 DOM 元素,从而进行操作
shouldComponentUpdate 专门用来做 React 性能优化的:将之前的状态 / 属性和当前的状态 / 属性进行对比,如果一样,就不更新,如果不一样就更新返回值为 true 就更新返回值为 false 就不更新
React 脚手架
介绍
xxx 脚手架: 用来帮助程序员快速创建一个基于 xxx 库的模板项目
a. 包含了所有需要的配置
b. 指定好了所有的依赖
c. 可以直接安装 / 编译 / 运行一个简单效果
react 提供了一个用于创建 react 项目的脚手架库: create-react-app
项目的整体技术架构为: react + webpack + es6 + eslint + babel
使用脚手架开发的项目的特点: 模块化, 组件化, 工程化
创建项目并启动
npm install -g create-react-app // 全局安装 create-react-app
create-react-app hello-react // 创建一个项目名称为 hello-react 的脚手架
npm start // 必须在 hello-react 文件目录下运行
react 脚手架项目结构
ReactNews
|–node_modules— 第三方依赖模块文件夹
|–public
|– index.html—————– 主页面
|–src———— 源码文件夹
|–components—————–react 组件
|–index.js——————- 应用入口 js
|–.gitignore——git 版本管制忽略的配置
|–package.json—- 应用包配置文件
|–README.md——- 应用描述说明的 readme 文件
react ajax
前置说明
React 本身只关注于界面, 并不包含发送 ajax 请求的代码
前端应用需要通过 ajax 请求与后台进行交互(json 数据)
react 应用中需要集成第三方 ajax 库(或自己封装)
常用的 ajax 请求库
jQuery: 比较重, 如果需要另外引入不建议使用
axios: 轻量级, 建议使用
a. 封装 XmlHttpRequest 对象的 ajax
b. promise 风格
c. 可以用在浏览器端和 node 服务器端
fetch: 原生函数, 但老版本浏览器不支持
a. 不再使用 XmlHttpRequest 对象提交 ajax 请求
b. 为了兼容低版本的浏览器, 可以引入兼容库 fetch.js
https://github.github.io/fetch/
https://segmentfault.com/a/11…
componentDidMount(){
// 发送 Ajax 请求
/* axios.get(‘https://api.github.com/search/repositories?q=r&sort=stars’)
//axios.get(‘/user’, {params: {q:r,sort:stars}})
//axios.post(‘/user’, {firstName: ‘Fred’,lastName: ‘Flintstone’})
.then(response => {
const {name, html_url} = response.data.items[0];
this.setState({name,url: html_url})
})
.catch(err =>{
console.log(err);
})*/
fetch(‘https://api.github.com/search/repositories?q=v&sort=stars’)
//get: fetch(url) post: fetch(url, {method: “POST” body:JSON.stringify(data),})
.then(res => res.json())
.then(response =>{
console.log(response);
const {name, html_url} = response.items[0];
this.setState({name,url: html_url})
})
.catch(err =>{
console.log(err);
})
}
react 路由
相关理解
react-router 的理解
react 的一个插件库
专门用来实现一个 SPA 应用
基于 react 的项目基本都会用到此库
SPA 的理解
单页 Web 应用(single page web application,SPA)
整个应用只有一个完整的页面
点击页面中的链接不会刷新页面, 本身也不会向服务器发请求
当点击路由链接时, 只会做页面的局部更新,网址也会对应的改变
数据都需要通过 ajax 请求获取, 并在前端异步展现
路由的理解
什么是路由?
一个路由就是一个映射关系(key:value)
key 为路由路径, value 可能是 function/component
路由分类
后台路由: node 服务器端路由, value 是 function, 用来处理客户端提交的请求并返回一个响应数据
a. 注册路由: router.get(path, function(req, res))
b. 当 node 接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据
前台路由: 浏览器端路由, value 是 component(组件), 当请求的是路由 path 时, 浏览器端前没有发送 http 请求, 但界面会更新显示对应的组件
a. 注册路由: <Route path=”/about” component={About} />
b. 当浏览器的 hash 变为 #about 时, 当前路由组件就会变为 About 组件
区别:
* 前端路由
* 不需要发送请求
* 不会刷新整个页面,局部更新
* 会修改 url 地址和浏览器历史记录
* value 是 component
* 后端路由
* 会发送请求
* 会刷新整个页面
* 会修改 url 地址和浏览器历史记录
* value 是 callback
使用前端路由的作用:
用来开发 SPA(单页面应用)
整个应用一个完整页面
不需要发送请求
不会刷新整个页面,局部更新
会修改 url 地址和浏览器历史记录
前端路由的实现
history 库
a. 网址: https://github.com/ReactTraining/history
b. 管理浏览器会话历史 (history) 的工具库
c. 包装的是原生 BOM 中 window.history 和 window.location.hash
history 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){}): 监视历史记录的变化
react-router 相关 API
组件
1) <BrowserRouter>
browserHistory 是使用 React-Router 的应用推荐的 history 方案。
它使用浏览器中的 History API 用于处理 URL,创建一个像 example.com/list/123 这样真实的 URL
<BrowserRouter><App /></BrowserRouter>
2) <HashRouter>
3) <Route>
<Route path=”/about” component={About}/>
<Route path=”/home/message/:id” component={MessageDetail}/>
一旦 url 变为 path 对应的值,就加载 component 中的组件进行显示
4) <Redirect>
<Redirect to=”/about”/>
什么路径都匹配,一旦匹配上就跳转到指定网址, 与 Switch 配合使用
5) <Link>
只修改 url 的地址,不会发送请求
<Link to=”/home”> 珂朵莉 </Link>
6) <NavLink>
只修改 url 的地址,不会发送请求,并且多了一个 class:active
export default function MyNavLink(props) {
return <NavLink {…props} activeClassName=’activeClass’/>
}
<MyNavLink to=”/about”>About</MyNavLink>
7) <Switch>
切换显示(针对内部组件 – 子组件)–> 从上到下匹配,一旦有一个匹配上,其他就不看了
<Switch>
<Route path=”/about” component={About}/>
<Route path=”/home” component={Home}/>
<Redirect to=”/about”/>
</Switch>
路由组件:通过 Route 组件加载的组件(import {withRouter} from ‘react-router-dom’)
history 修改浏览器历史记录
location 获取请求 path
match 获取 params 参数
withRouter 作用:给非路由组件传递路由组件三个属性(history、location、match)@withRouter
react-ui 库
material-ui(国外)
官网: http://www.material-ui.com/#/
github: https://github.com/callemall/…
ant-design(国内蚂蚁金服)
PC 官网: https://ant.design/index-cn
移动官网: https://mobile.ant.design/ind…
Github: https://github.com/ant-design…
Github: https://github.com/ant-design…