1. React简介
1.1 React官网
1.2 React的特点
- Declarative(声明式编程)
- Component-Based(组件化编码)
- Learn Once,Write Anywhrer(支持客户与服务器渲染)
- 高效
- 单向数据流
1.3 React高效的原因
- 虚拟(virtual)DOM,不总是直接操作DOM
- DOM Diff算法,最小化页面重绘
2.前端三大主流框架
三大框架一大抄 vue.js抄袭Angular.js,Angular2反过来抄vue组件化
- Angular.js:出来较早的前端的框架,学习曲线比较陡,NG1学习起来比较麻烦,NG2~NG5开始,进行了一些列的改革,也提供了组件化开发的概念;从NG2开始,也支持使用TS(TypeScript)进行编程
- Vue.js:最火(关注的人比较多)的一门前端框架,它是中国人开发的,对我们来说,文档友好一些;
- React.js:最流行(用的人比较多)的一门框架,因为它的设计很优秀
3.React与Vue对比
组件化方面
- 什么是模块化: 从代码的角度进 行分析的;把一些可复用的代码,抽离为单个模块;便于项目的维护和开发;
- 什么是组件化: 从UI界面的角度 来进行分析的;把一些可复用的UI元素,抽离为单独的组件;
- 组件化的好处: 随着项目规模的增大,手里的组件就越来越多;节省开发成本,便于把现有组件,拼接为一个完整的页面。
- Vue是如何实现组件化的: 通过
.vue
文件,来创建对应的组件;- template 结构
- script 行为
- style 样式
- React是如何让实现组件化的: React中有组件化概念,但是没有像vue这样的组件模板文件;React中,一切都是以js表现的;因此要学习React,JS要合格;ES6和ES7(async和await)要会用。
开发团队方面
- React是由FaceBook前端官方团队进行维护和更新的;因此,React的维护开发团队,技术实力比较雄厚;
- Vue:第一版。主要是作者尤雨溪专门进行维护的,当Vue更新到2.x版版本后,也有了一个以尤雨溪为主的开源小团队,进行相关维护;
社区方面
- 在社区方面,React由于诞生的较早,所以社区比较强大,一些常见的问题、坑、最优解决方案,文档、博客在社区中都是可以很方便就能找到的
- Vue是近两年才获取来的,所以,在社区方面相对React来说,要小一些,可能有一些坑,还没人踩过。
移动APP开发体验方面
- Vue,结合Weex这门技术,提供了迁移到移动端App开发的体验(Weex,目前指示一个小玩具,并没有很大的成功案例)
- React,结合ReactNative,也提供了迁移到移动App的开发体验(RN用到最多,也是最火最流行的)
4. React的基本使用
2.1 示例
1 |
|
2.2相关库
react.js:React核心库
react-dom.js:供操作DOM的react扩展库
babel.min.js:解析JSX语法代码转为纯JS语法代码库
5. React JSX
1 | const element = <h1>Hello, world!</h1>; |
这个有趣的标签语法既不是字符串也不是 HTML。
它被称为 JSX,是一个 JavaScript 的语法扩展。我们建议在 React 中配合使用 JSX,JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式。JSX 可能会使人联想到模版语言,但它具有 JavaScript 的全部功能。
JSX 可以生成 React “元素”。我们将在下一章节中探讨如何将这些元素渲染为 DOM。下面我们看下学习 JSX 所需的基础知识。
3.1 虚拟DOM
React提供了一些API来创建一钟“特别”的一般js对象
- ```js
var element = React.createElement(‘h1’,{id:’mytitle’},’hello’)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
- 上面创建的就是一个简单的虚拟DOM对象
虚拟DOM对象最终会被React转换为真实的DOM
我们编码时基本只需要操作react虚拟DOM相关数据,react会转换为真实DOM变化而更新界面
#### 3.2 JSX
1. 全称: JavaScript XML
2. react定义的一种类似于XML的JS扩展yy垡:XML+JS
3. 作用:用来创建react虚拟DOM(元素)对象
> var ele = <h1>Hello JSX!</h1>
>
> 注意1:它不是字符串,也不是HTML/XML标签
>
> 注意2:它最终产生的标签就是一个JS对象
4. 标签名任意:HTML标签或其他标签
5. 标签属性任意:HTML标签属性或其他
6. 基本语法规则
> 遇到<开头的代码,以标签语法解析:html同名标签转换为html标签元素,其他标签需要特别解析
>
> 遇到{开头的代码,以js语法解析:标签钟js代码必须用{}包含
7. babel.js的作用
> 浏览器不能直接解析jsx代码,需要babel转译为js代码才能被浏览器识别运行
>
> 只要用了JSX,都要加上type=“test/babel",声明需要babel处理
#### 3.3 渲染虚拟DOM(元素)
1. 语法:`ReactDOM.render(virtualDOM,containerDOM)`
2. 作用:将虚拟DOM元素渲染到页面中的真实容器DOM显示
3. 参数说明:
- 参数一:纯js或jsx创建的虚拟dom对象
- 参数二:用来包含虚拟DOM元素的真实dom元素对象(一般是一个div)
#### 3.4 创建虚拟DOM的两种方式
1. 纯JS(一般不用)
```js
React.createElement('h1',{id:'mytitle'},'title')
JSX:
1
<h1 id='myTitle'>{title}</h1>
3.5 JSX练习
需求:动态展示列表数据
6. 模块与组件和模块化组件的理解
4.1 模块
- 理解:向外提供特定功能的js程序,一般就是一个js文件
- 为什么:js代码更多更复杂
- 作用:复用js,简化js的编写,提高js的运行效率
4.2 组件
- 理解: 用来实现特定(局部)功能效果的代码集合(html/js/css)
- 为什么:一个界面的功能更复杂
- 作用:提高代码的复用性,便于程序可维护性,提高运行效率
4.3 模块化
当应用的js都以模块来编写,这个应用就是一个模块化应用
4.4 组件化
当应用是以多组件的方式实现,这个应用就是一个组件化应用
7. React面向组件编程
5.1 自定义组件(Component)
1 | <!DOCTYPE html> |
5.2 组件的三大属性 state
- state是组件对象最重要的属性,值是对象(可以包含多个数据,类似Vue中vm实例的data属性)
- 组件被称为”状态机“,通过更新组件的state来更新对应的页面显示(重新渲组件)
1 | <!DOCTYPE html> |
5.3 组件的三大属性 props
1 | <!DOCTYPE html> |
5.4 组件的三大属性 refs 与事件处理
1 | <!DOCTYPE html> |
5.5 组合组件的应用
Dmoe01
1 | <!DOCTYPE html> |
Demo02
1 | <!DOCTYPE html> |
8.组件的生命周期
8.1 React生命周期图谱
示例:
1 | <!DOCTYPE html> |
8.2 生命周期流程:
- 第一次初始化渲染显示:ReactDOM.render()
- constructor():创建对象初始化state
- componentWillMount():将要插入回调
- render():用于插入虚拟DOM
- componentDidMount():已经插入回调
- 每次更新state:this.setState()
- componentWillUpdate():将要更新回调
- render():更新(重新渲染)
- componentDidUdate():已经更新回调
- 移除组件:ReactDOM.unmountComponentAtNode(containerDom)
- componentWillUnmount():组件将要被移除
9.虚拟DOM与Diff算法
9.1基本原理
创建虚拟DOM树(一般JS对象)—>真实DOM树—>绘制界面显示—>getState()更新状态—>重新创建虚拟DOM树—>新/旧树比较差异—>更新差异对应真实DOM—>局部界面重绘
9.2Diif算法
tree diff;
新旧两颗DOM树,逐层对比的过程,就是Tree Diff;当整颗DOM逐层对比完毕,则所有需要被按需更新的元素,必然能够找到
component diff;
在进行Tree Diff的时候,每一层中,组件级别的对比,叫做Component Diff;
如果对比前后,组件的类型相同,则暂时认为组件不要被更新
如果对比前后,组件类型不同,则需要更新
element diff;
在进行组件对比的时候,如果两个组件类型相同,则需要进行元素级别的对比,这叫做ElementDiff;
10. React应用
10.1 react 脚手架
- xxx脚手架:用于帮助程序猿快速创建一个基于xxx库的模板项目
- react提供了一个用于创建react项目的脚手架库:create-react-app
- 项目的整体技术架构为:react+webpack+es6+eslint
- 使用脚手架开发的项目的特点:模块化,组件化,工程化
1 | npm i -g create-react-app // 全局安装create-react-app |
11.1 Props传递
- 共同的数据放在父组件上,特有的数据放在自己的组件内部(state)
- 通过props可以传递一般数据和函数数据
- 一般数据—>父组件传递数据给子组件—>子组件读取数据
- 函数数据—>子组件传递数据给父组件—>子组件调用函数(回调函数)
11.2 使用消息订阅(subscribe)-发布(publish)机制
工具库PubSubJS
下载:
1
npm install pubsub-js --save
使用:
1
2
3import PubSub from 'pubsub-js' //引入
PubSub.subscribe(‘delete',function(data){}): //订阅
PubSub.publish('delete',data) //发布消息
11.3 context方式
context是一个全局变量,像是一个大容器,在任何地方都可以访问到,我们可以把要通信的信息放在context上,然后在其他组件中可以随意取到;
但是React官方不建议使用大量context,尽管他可以减少逐层传递,但是当组件结构复杂的时候,我们并不知道context是从哪里传过来的;而且context是一个全局变量,全局变量正是导致应用走向混乱的罪魁祸首.
层级关系:App.jsx->List.jsx->ListItem.jsx
App.jsx:
1 | import React, { Component } from 'react'; |
List.jsx:
1 | import ListItem from './ListItem'; |
ListItem.jsx:
1 | import React, { Component } from 'react'; |
11.4 redux
12 react-route
12.1 react-router的理解
- react的一个插件库
- 专门用来实现一个SPA应用
- 基于reacr的项目基本都会用到词库
12.2 SPA的理
- 单页Web应用(single page web application, SPA)
- 整个应用只有一个完整的页面
- 点击页面中的链接不会刷新页面,本身也不会像服务器发送请求
- 当点击链接是,只会做页面的局部刷新
- 数据都需要通过ajax/axios发送网络请求,并在前端异步展现
- 目前SPA应用非常流行,Vue-Router也对Vue能够实现SPA提供支持
12.3 路由的理解
什么是路由
- 一个路由就对应一个映射关系(key:value)
- key为路由路径,value可能是function/component
路由分类
- 后台路由:node服务器端路由,value是function,用来处理客户端的亲贵并返回一个响应数据
- 前端路由:浏览器端路由,value是一个component,当请求的是路由path时,浏览器端没有发送http请求,而是显示对应的组件
后台路由
- 注册路由:
router.get(path,function(req,res))
- 当node接收到一个请求时,根据请求路径找到匹配的路由,调用路由中的函数来处理请求,返回响应数据
- 注册路由:
前端路由
注册路由:
1
<Route path="/about" commponent={About}>
当浏览器的hash变为#about时,当前路由组件就会变为About组件
12.4 前端路由的实现
- histoy库
- 网址:history
- 管理浏览器绘画历史(history)的工具库
- 包装的是原生BOM中window.history和window.loaction.hash
13. react-router相关API
13.1 组件
1 | <BrowserRouter> |
13.2 其他
- history 对象
13.3 react-router-dom简单使用
示例:
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import React from 'react';
import {render} from 'react-dom';
import App from './components/app';
import reportWebVitals from './reportWebVitals';
import './index.scss'
import {BrowserRouter} from 'react-router-dom'
render(
<BrowserRouter>
<App/>
</BrowserRouter>
,
document.getElementById('root')
)
;
reportWebVitals();app.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45import React, {Component} from "react";
import {Switch, Route, Redirect} from 'react-router-dom'
import About from "../views/about";
import Home from "../views/home";
import MyNavLink from "./MyNavLink";
class App extends Component {
state = {};
static propTypes = {};
render() {
return (
<div className="container">
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<div className="page-header">
<h2>React Router Demo</h2>
</div>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
<MyNavLink className='list-group-item' to='/about'/>
<MyNavLink className='list-group-item' to='/home'/>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
<Switch>
<Route path='/about' component={About} className="list-group-item "/>
<Route path='/home' component={Home} className="list-group-item "/>
<Redirect to='/about'/>
</Switch>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default App;MyNavLink.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14import React, {Component} from "react";
import {NavLink} from "react-router-dom";
class MyNavLink extends Component {
/*
{...this.props} 将外部传递的所有属性传递给组件
*/
render() {
return (
<NavLink {...this.props} activeClassName='active-class'>About</NavLink>
)
}
}
export default MyNavLink;about.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import React, {Component} from "react";
class About extends Component {
state = {};
static propTypes = {};
render() {
return (
<div>
<h2>About</h2>
</div>
)
}
}
export default About;home.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import React, {Component} from "react";
class Home extends Component {
state = {};
static propTypes = {};
render() {
return (
<div>
<h2>Home</h2>
</div>
)
}
}
export default Home;
项目源码
13.4 嵌套路由的使用
13.4.1 如何编写路由效果
- 编写路由组件
- 在父路由组件中指定
- 路由链接:
<NavLink>
- 路由:
<Route>
- 路由链接:
14. 最流行的开源React UI组件库
14.1 meterial-ui(国外)
14.2 ant-design(国内蚂蚁金服)
- PC官网:https://ant.design/index-cn
- 移动官网:https://mobile.ant.design/index-cn
- github:https://github.com/ant-design/ant-design/
- github:https://github.com/ant-design/ant-design-mobile/
14.2.1 按需打包
下载包
npm i react-app-rewired@2.0.2-next.0 -D
出现了这个错误,
The "injectBabelPlugin" helper has been deprecated as of v2.0. You can use customize-cra plugins in replacement - https://github.com/arackaf/customize-cra#available-plugins
- 问题的分析:
react-scripts
升级到2.1.2
以后破坏了react-app-rewired
。然后react-app-rewired
升级到2.x
以后直接干掉了所有helpers
。 - 问题的解决: 把
react-app-rewired
进行降级后可以了, 可以通过npm install react-app-rewired@2.0.2-next.0
命令进行降级。 在安装低版本的react-app-rewired
以后,重新npm install
,最后再npm start
启动项目就可以解决问题了。
- 问题的分析:
npm i babel-plugin-import -D
修改默认配置
package.json
1
2
3
4
5"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
}config-overrides.js
1
2
3
4
5const {injectBabelPlugin} = require('react-app-rewired');
module.exports = function override(config, env) {
config = injectBabelPlugin(['import', {libraryName: 'antd-mobile', style: 'css'}], config);
return config;
};
15. redux
15.1 学习文档
- 中文文档: https://www.redux.org.cn/
- Github: https://github.com/reduxjs/redux
15.2 redux是什么
- redux是一个独立专门用于做状态管理的js库(不是react插件库)
- 它可以用react,angular,vue(vuex)等项目中,但基本与react配合使用
- 作用:管理react应用中多个组件共享状态
15.3 什么情况下需要使用redux
- 总体原则:能不用就不用,如果不用比较吃力才考虑使用
- 某个组建的状态,需要共享
- 某个状态需要在任何地方都可以拿到
- 一个组件需要改变全局状态
- 一个组件需要改变另一个组件状态
15.4 redux的核心API
15.4.1 createStore
作用:
创建包含指定reducer的store对象
编码:
1
2
3import {createStore} from 'redux'
import counter from './reducers/counter'
const store = createStore(counter)
15.4.2 store对象
作用:
redux库最核心的管理d对象
它内部维护着:
state
reducer
核心方法
getState() 读取状态
dispatch(action) 提交行为
subscribe(listener) 订阅监听用于监听state的状态改变
编码:
1
2store.getState()
store.dispatch({type:'INCREMENT',number})
15.5 redux的三个核心概念
15.5.1 action
标识要执行行为的对象
包含2个方面的属性
type:标识属性,值为字符串,唯一,必要熟悉 可以理解为key
xxx: 数据属性,值为任意类型,可选属性
例子:
1
2
3
4const action = {
type:'INCREMENT',
number:2
}Action Creator(创建Action函数)
1
const increment = (data)=>({type:'INCREMENT',data})
15.5.2 reducer
根据旧的state和action,产生新的state的纯函数(优点类似Vuex中mutation)
样例
1
2
3
4
5
6
7
8
9
10export defailt function counter(state=0,cation){
swotch(action.type){
case 'INCREMENT':
retturn state +action.data
case:'DECREMENT':
return state-action.data
default:
return state
}
}注意:
返回一个新的状态,不要修改原来的状态
15.5.3 store
将state,action与reducer联系在一起的对象
如何得到此对象
1
2
3import {createStore} from 'redux'
import reducer from './reducers'
const store =createStore(reducer)此对象的功能
getstate():得到对象
dispatch(action):分发action,触发reducer调用,产生新的state
subscribe(listener):注册监听,当产生新的state时,自动调用
15.6 react-redux
中文文档:https://www.redux.org.cn/docs/react-redux/
15.6.1 理解
- 一个react插件库
- 专门用来简化react应用中使用redux
15.6.2 React-Redux将所有组件分成两类
UI组件
只负责UI的呈现,不带有任何业务逻辑通过props接收数据(一般数据和函数),不适用任何Redux的API一般保存在components文件下
容器组件
负责管理数据和业务逻辑,不负责呈现UI,使用Redux的API,一般保存在containers文件下
15.6.3 React-Redux 相关API
Provider
让所有组件都可以得到state数据
1
2
3<Provider store={store}>
<App/>
</Provider>connect()
用于包装UI组件生产容器组件
1
2
3
4
5import {connect} from 'react-redux';
export default connect(
state=>({count:state}), // 父函数会将这两个对象一一结构交给子函数
{increase,reduce}
)(Counter);mapStateToprops()
将外部的数据(即state对象)转换为UI组件的标签属性
1
2
3
4const mapStateToprops = function(state){
return {
value:state}
}
mapDispatchToProps()
将分发action函数转换为UI组件的标签属性
简洁语法可以直接指定为actions对象或包含多个action方法的对象
1
2
3function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(actionCreators, dispatch) }
}
15.7 Redux异步编程
15.7.1 问题
- redux默认是不能进行异步处理的
- 应用中又需要再redux中执行异步任务(ajax,定时器)
15.7.2 下载redux插件(异步中间件)
1 | npm install --save redux-thunk |
15.7.3 index.js
1 | import {createStore,applyMiddleware} from "redux"; |
15.8 使用redux调试工具
15.8.1 浏览器下载Redux Devtools
扩展
15.8.2 下载工具依赖包
1 | npm install --save-dev redux-devtools-extension |
15.8.3 编码
1 | import {createStore, applyMiddleware} from "redux"; |
16 . code hub
17 函数柯里化
维基百科上说道:柯里化,英语:Currying(果然是满满的英译中的既视感),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。实际上就是把add函数的x,y两个参数变成了先用一个函数接收x然后返回一个函数去处理y参数。现在思路应该就比较清晰了,就是只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
1 | // 普通的add函数 |