《React.js开发简书项目》开发前笔记(TodoList)

  • 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 替代for
  • className 替代class
  • dangerouslySetInnerHTML={{__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'的DOM
  • React 组件化的开发,首字母大写是组件,父组件通过属性的props的方式传值给子组件;子组件和父组件首先需要传递一个方法,通过方法子组件可以操作父组件的DOM(面试题)
  • React 单向数据流,子组件可以使用父组件的值,但是不要不能去修改!便于测试! 如果就想修改,就父组件向子组件传递方法,子组件调用父组件方法去修改,所以还是父组件进行操作的。
  • React 因为React是一个视图层框架,所以复杂的组件传值,需要用到数据层框架
  • React 函数式编程能够更好的测试。

React高级功能

  • 4-1 React Developer Tools
    红色代表成功,黑色代表上线
  • 4-2 PropTypesdefaultProps
    参数检测和默认值
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{}生命周期函数里面
  • axiosaxios中文文档

基于promise用于浏览器和node.js的http客户端
npm add axios
import axios from 'axios';

  • Charles 前端接口模拟

react-transition-group 实现动画

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入门

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-extensiongitHub
    • createStore要加reducerwindow.__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');
    }
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值