- React Fiber 16RC 易上手,难精通
RC=Release Candidate
,含义是"发布候选版
",它不是最终的版本,而是最终版(RTM=Release ToManufacture)之前的最后一个版本。广义上对测试有三个传统的称呼:alpha、beta、gamma
,用来标识测试的阶段和范围。alpha 是指内测
,即现在说的CB,指开发团队内部测试的版本或者有限用户体验测试版本。beta 是指公测
,即针对所有用户公开的测试版本。然后做过一些修改,成为正式发布的候选版本时叫做gamma,现在叫做RC(Release Candidate)
2-1React简介
- React JS
- React Native – 移动应用
- React VR
2-2React开发环境准备
2-3工程目录文件简介
-
package.json
node的包文件,一般不用动 -
gitignore
忽略上传到Git的文件 -
node_modules
依赖文件 -
public/favicon.icon
浏览器页面的icon
/index.html
首页
<noscript> 不支持js的时候显示</noscript>
-
mainfest.json
定义了PWA的快捷方式 -
src/index.js
程序运行的入口
/index.css
样式
/App.js
-
PWA
progressive web application 用写网页方式写手机应用
import registerServiceWorker form './registerServiceWorker';
registerServiceWorker();
手机通过https服务器上访问网页之后,即使下次访问断网了,也能访问进去 -
/App.test.js
自动化测试文件
2-4react中的组件
TodoList
- 参考完整代码:Code of TodoList
{/*这是个注释*/}
htmlFor
替代forclassName
替代classdangerouslySetInnerHTML={{__html:item}}
- 父组件传递值和方法给子组件
<TodoItem
item={item}
index={index}
deleteItem={this.handleDel.bind(this)}
/>
class TodoItem extends Component {
constructor(props) {
super(props);
this.handleDel = this.handleDel.bind(this);
}
render() {
return (
<div
index={this.props.index}
onClick={this.handleDel}
>
<li>{this.props.item}</li>
</div>
);
}
handleDel() {
this.props.deleteItem(this.props.index);
}
}
React衍生的思考
React
是声明式开发,面向数据编程React
可以与其他框架共存,他只操作id='root'
的DOMReact
组件化的开发,首字母大写是组件,父组件通过属性的props的方式传值给子组件;子组件和父组件首先需要传递一个方法,通过方法子组件可以操作父组件的DOM(面试题)React
单向数据流,子组件可以使用父组件的值,但是不要不能去修改!便于测试! 如果就想修改,就父组件向子组件传递方法,子组件调用父组件方法去修改,所以还是父组件进行操作的。React
因为React是一个视图层框架
,所以复杂的组件传值,需要用到数据层框架
。React
函数式编程能够更好的测试。
React高级功能
- 4-1
React Developer Tools
红色代表成功,黑色代表上线 - 4-2
PropTypes
和defaultProps
参数检测和默认值
import PropTypes from 'prop-types';
TodoItem.propTypes = {
content: PropTypes.arrayOf(PropTypes.number,PropTypes.string)
deleteItem: PropTypes.func,
index: PropTypes.number,
test: PropTypes.string.isRequired,
}
TodoItem.defaultTypes ={
test: 'Hello World!'
}
- 4-3
Props、State、render
父组件render
重新执行,子组件的render
也会重新执行;
当组件的State
或者Props
改变时,render
就会重新执行。
虚拟DOM△
-
4-4 React中的虚拟DOM
-
虚拟DOM△:
优点:性能提升;在跨端应用可以实现React Native;
-
React生成DOM的过程:(Vue也一样)
JSX >> createElement >> 虚拟DOM(JS对象) >> 真实的DOM
- 4-5 React中的虚拟DOM的Diff算法
找新虚拟DOM和旧虚拟DOM的不同,当props或者setState改变时触发。
-
setState是异步的,并且多个setState会自动变成一个,为了提升性能。
-
Diff算法:同层比对,发现一层不同就把下面的DOM重新生成
-
不用index作为key的原因:因为新的虚拟DOM会和原来的虚拟DOM对比,提高性能
4.7 ref的使用
-
ref = reference
-
e.target
可以获取DOM节点 -
ref={(inp)=>{this.inputRef = inp}} DOM元素上 this.inputRef.value 函数里面使用,替代e.target.value
尽量不要用ref,动画可能会用到
-
由于
setState
是异步函数,所以不会马上执行,如果需要输出console.log就要放到回调函数里面:
this.setState(
{ inputValue: e.target.value },
() => {console.log('这是setState回调函数')}
);
React生命周期函数
//在组件即将第一次被挂载的时候执行
componentWillMount(){
console.log('componentsWillMount!')
}
// 在组件第一次被挂载时候执行
componentDidMount(){
console.log('componentDidMount!')
}
// 组件被更新之前执行,需要返回一个布尔值(需要被更新吗?)如果false就不会更新
shouldComponentUpdate(){
console.log('shouldComponentUpdate!')
return true
}
// 组件被更新之前执行,但是是在shouldComponentUpdate返回true后运行,如果false不执行
componentWillUpdate(){
console.log('componentWillUpdate!')
}
// 数据变化,render执行后执行
componentDidUpdate(){
console.log('componentDidUpdate!')
}
// 当一个组件从父组件接收了参数,父组件的render函数被【重新】(第二次)执行后执行该函数
// 如果这个组件第一次存在于父组件中,不会执行
// 如果这个组件之前已经存在父组件中,才会执行
componentWillReceiveProps(){
console.log('componentWillReceiveProps!')
}
/***子组件****/
// 当一个组件从父组件接收了参数,父组件的render函数被【重新】(第二次)执行后执行该函数
// 如果这个组件第一次存在于父组件中,不会执行
// 如果这个组件之前已经存在父组件中,才会执行
componentWillReceiveProps(){
console.log('componentWillReceiveProps!')
}
// 组件即将在页面被剔除的时候执行
componentWillUnmount(){
console.log('componentWillUnmount!')
}
- 节省性能的方式
1.作用域绑定放在constructor{}
里面
2.setState异步函数,多次改变结合成一次执行
3.虚拟DOM的同层比对、key值。
4.shouldComponentUpdate{}
生命周期函数
接口数据模拟 Axios & Charles
ajax
请求放在componentDidMount{}
生命周期函数里面- axios — axios中文文档
基于promise用于浏览器和node.js的http客户端
npm add axios
import axios from 'axios';
- Charles 前端接口模拟
react-transition-group 实现动画
-
gitHub
搜索react-transition-group
-
npm add react-transition-group
-
参考中文文档:react-transition-group中文文档
-
Cartoon.js
import React, { Component, Fragment } from "react";
import { CSSTransition, TransitionGroup } from "react-transition-group";
class Cartoon extends Component {
constructor(props) {
super(props);
this.state = {
in: true,
value: "",
toggle: "Enter",
list: []
};
this.handleToggle = this.handleToggle.bind(this);
this.handleAdd = this.handleAdd.bind(this);
}
render() {
return (
<Fragment>
<div className={"cartoon"}>
<CSSTransition
in={this.state.in}
timeout={1500}
classNames="fade"
appear={true}
// unmountOnExit
onEnter={el => {
el.style.color = "orange";
console.log("onEnter");
this.setState({
value: "onEnter"
});
}}
onEntering={el => {
el.style.color = "black";
console.log("onEntering");
this.setState({
value: "onEntering"
});
}}
onEntered={el => {
el.style.color = "red";
console.log("onEntered");
this.setState({
value: "onEntered"
});
}}
onExit={el => {
el.style.color = "orange";
console.log("onExit");
this.setState({
value: "onExit"
});
}}
onExiting={el => {
el.style.color = "black";
console.log("onExiting");
this.setState({
value: "onExiting"
});
}}
onExited={el => {
el.style.color = "red";
console.log("onExited");
this.setState({
value: "onExited"
});
}}
>
<p>{this.state.value}</p>
</CSSTransition>
<button onClick={this.handleToggle}>{this.state.toggle}</button>
</div>
<TransitionGroup>
{this.state.list.map((item, index) => {
return (
<CSSTransition
timeout={1500}
classNames="item"
unmountOnExit
onEntered={el => {
el.style.color = "red";
}}
appear={true}
key={index}
>
<div>{item}</div>
</CSSTransition>
);
})}
<button onClick={this.handleAdd}>+Add</button>
</TransitionGroup>
</Fragment>
);
}
handleToggle() {
if (this.state.toggle === "Enter") {
this.setState({
toggle: "Exit"
});
} else {
this.setState({
toggle: "Enter"
});
}
this.setState({
in: this.state.in ? false : true
});
}
handleAdd() {
this.setState(prevState => {
return {
list: [...prevState.list, "Item"]
};
});
}
}
export default Cartoon;
- style.css
p {
font-size: 5rem;
font-weight: bold;
}
.fade-appear {
font-style: italic;
}
.fade-appear-active {
font-style: italic;
}
.fade-enter {
opacity: 0.5;
}
.fade-enter-active {
opacity: 1;
transition: opacity 1.5s ease-in;
}
.fade-enter-done {
opacity: 1;
}
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0.5;
transition: opacity 1.5s ease-in;
}
.fade-exit-done {
opacity: 0.5;
}
.item-enter , .item-appear {
opacity: 0;
}
.item-enter-active , .item-appear-active{
opacity: 1;
transition: opacity 1s ease-in;
}
.item-enter-done {
opacity: 1;
}
Redux入门
- 参考本博客另一文章:使用Redux的TodoList
- 5-1 Redux 概念简述
React : A JavaScript library for building user interfaces
Redux: Redux = Reducer + Flux
- 5-2 Redux 的工作流程
组件 > 表达式 > Store > Reducers查询Store > Store > 组件
- 5-3 Antdesign
- 5-4 Redux的 Stroe创建 & Action编写
npm add redux
- redux-devtools Chrome插件
redux-devtools-extension
:gitHubcreateStore
要加reducer
和window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
TodoList.js
组件
constructor(props) {
super(props);
//Store部分
this.state = store.getState();
//更新组件的数据:拿到store的数据给当前组件state
this.handleStoreChange = this.handleStoreChange.bind(this);
//更新组件的数据:store变化就执行()函数
store.subscribe(this.handleStoreChange);
}
//更新store的数据
handleStoreChange() {
this.setState(store.getState());
}
//action请求修改store的数据
handleAdd() {
const action = {
type: 'add_todo_item',
};
store.dispatch(action); // 发送action请求
}
handleChange(e) {
const action = {
type: 'change_input_inputValue',
value: e.target.value
}
store.dispatch(action)
}
handleDel(index) {
const action = {
type: 'del_data',
index
}
store.dispatch(action)
}
store/index.js
创建store
import { createStore } from 'redux'; // 创建store
import reducer from './reducer' // 引入reducer
const store = createStore(
reducer, // 使用reducer
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() // Chrome使用redux-devtools工具
);
export default store;
reducer.js
操作action
const defaultState = {
inputValue: '默认输入内容',
dataObj: ["默认内容"]
}
//reducer 可以接受state,但是绝对不能修改state
export default (state = defaultState, action) => {
// 对相应action进行操作
if (action.type === 'change_input_inputValue') {
// 创建一个newState
const newState = JSON.parse(JSON.stringify(state));
// 将action传递的值传给newState对应变量
newState.inputValue = action.value;
return newState;
}
if (action.type === 'add_todo_item') {
const newState = JSON.parse(JSON.stringify(state));
newState.dataObj.push(newState.inputValue);
newState.inputValue = '';
return newState;
}
if (action.type === 'del_data') {
const newState = JSON.parse(JSON.stringify(state));
newState.dataObj.splice(action.index,1);
return newState;
}
return state;
}
- 5-7
actionType.js
来定义action
名称
单独使用文件定义常量来避免拼写的错误
export const CHANGE_INPUT_INPUTVALUE = 'change_input_inputValue';
export const ADD_TODO_ITEM = 'add_todo_item';
export const DEL_DATA = 'del_data';
引入:import { CHANGE_INPUT_INPUTVALUE, ADD_TODO_ITEM, DEL_DATA } from './actionType'
- 5-8
actionCreators.js
创建action
方法
import { CHANGE_INPUT_INPUTVALUE, ADD_TODO_ITEM, DEL_DATA } from "./actionType";
export const getInputChangeAction = (value) => ({
type: CHANGE_INPUT_INPUTVALUE,
value
})
export const getAddTodoItem = () => ({
type: ADD_TODO_ITEM,
})
export const getDelData = (index) => ({
type: DEL_DATA,
index
})
引入:import { getInputChangeAction, getAddTodoItem, getDelData } from './store/actionCreators'
- 5-9 Redux知识点补充
-
三个基本原则:
- Store 是唯一的,只能有一个
- 只有 Store 能改变自己的内容
- Reducer 必须是*纯函数(给固定的输入,一定有固定的输出,不会有副作用)
-
Store >派发 >获取store数据 >订阅数据改变
-
React 进阶
- 6-1 UI组件与容器组件的拆分
- UI组件:傻瓜组件 > 负责渲染
- 容器组件:聪明组件 > 负责逻辑
// 传递值给子组件
<TodoListUI
inputValue={this.state.inputValue}
handleChange={this.handleChange}
handleAdd={this.handleAdd}
handleAjax={this.handleAjax}
handleClean={this.handleClean}
handleDel={this.handleDel}
dataObj={this.state.dataObj}
/>
// 接收父组件传递的值和函数,props;
// 使用箭头函数接收函数并传递数据index
renderItem={(item) => (
<List.Item
onClick={() => {this.props.handleDel(index)}}>
<ul>
<li>{item}</li>
</ul>
</List.Item>
)}
- 6-2 无状态组件
- 当一个组件只有一个
render
的时候,就可以用无状态组件。性能高,他是一个函数。没有多余的生命周期函数。定义UI组件只负责页面渲染时,用无状态组件最好。 - 这样以后所有的
this
可以去掉
const TodoListUI = (props) => {
return (
)
- 6-3 Redux 中发送异步请求获取数据
- 6-4 Redux 使用Redux-thunk中间件进行Ajax请求发送
引入thunk插件后,我们可以在
actionCreators内部编写逻辑
,处理请求结果。而不只是单纯的返回一个action对象。
thunk的原理,可以在actionCreators里通过返回一个函数,然后就可以在函数里编写某些异步操作了,待异步操作结束,最后通过传入的store.dispatch,发出action通知给Store要进行状态更新。
npm add redux-thunk
- gitHub中搜索
redux-devtools-extension
1.2目录处复制使用中间件 - 使用异步请求的中间件
store/index.js
使用 thunk
import { createStore, applyMiddleware, compose} from "redux";
import reducer from "./reducer";
import thunk from "redux-thunk";
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
: compose;
const enhancer = composeEnhancers(
applyMiddleware(thunk)
);
const store = createStore(reducer, enhancer);
export default store;
- actionCreators.js
export const getTodoList = () => {
return () => {
axios.get("../../todoList.json")
.then(res => {
const data = res.data;
const action = getAjax(data);
store.dispatch(action);
})
}
}
- TodoList.js
const action = getTodoList ();
store.dispatch(action)
- 6-5 什么是Redux的中间件
- 用Redux Thunk 对Dispatch进行升级,这样就不仅可以使用对象,还能使用函数。基本没什么API,简单
- 6-6 Redux-saga 中间件的作用
- GitHub :
Redux-saga
非常大型项目更好用 npm add redux-saga
store/index.js
配置
import createSagaMiddleware from 'redux-saga';
import todoSagas from './saga';
// 创建 SagaMiddleware
const sagaMiddleware = createSagaMiddleware();
// 使用 SagaMiddleware
const enhancer = composeEnhancers(
applyMiddleware(thunk,sagaMiddleware)
);
// 创建sagaMiddleware 中间件
const store = createStore(reducer, enhancer);
// then run the saga
sagaMiddleware.run(todoSagas);
sagas.js
import { takeEvery , put} from 'redux-saga/effects';
import { GET_INIT_LIST } from './actionType'
import { initListAction } from './actionCreators';
import axios from 'axios';
// 一定要引入generrator 函数
function* todoSaga() {
// 捕捉action的类型 并 执行对应方法
yield takeEvery(GET_INIT_LIST, getInitListSaga);
}
// 执行的方法
function* getInitListSaga() {
try {
const res = yield axios.get('/todoList.json');
const action = initListAction(res.data);
// 传给store reducer进行处理
yield put(action);
}catch(e) {
console.log('list.json 404');
}
}