Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

读书笔记-《深入浅出React和Redux》 #2

Open
ycshill opened this issue Jul 18, 2018 · 4 comments
Open

读书笔记-《深入浅出React和Redux》 #2

ycshill opened this issue Jul 18, 2018 · 4 comments

Comments

@ycshill
Copy link
Owner

ycshill commented Jul 18, 2018

第1章 React新的前端思维方式

  • React 不支持 IE8
  • 环境搭建:
    • npm install --global create-react-app
  • chuang 创建项目
    • create-react-app frist_react_app
  • JSX
    • React 判断一个元素是HTML元素还是React组件,原则上就是看第一个字母是否是大写
    • onClick ???????

      即使现在,我们还是要说在HTML中直接使用onclick很不专业,原因如下:
      □ onclick添加的事件处理函数是在全局环境下执行的,这污染了全局环境,很容易产生意料不到的后果;
      □ 给很多DOM元素添加onclick事件,可能会影响网页的性能,毕竟,网页需要的事件处理函数越多,性能就会越低;
      □ 对于使用onclick的DOM元素,如果要动态地从DOM树中删掉的话,需要把对应的时间处理器注销,假如忘了注销,就可能造成内存泄露,这样的bug很难被发现。
      上面说的这些问题,在JSX中都不存在。
      首先,onClick挂载的每个函数,都可以控制在组件范围内,不会污染全局空间。
      我们在JSX中看到一个组件使用了onClick,但并没有产生直接使用onclick(注意是onclick不是onClick)的HTML,而是使用了事件委托(event delegation)的方式处理点击事件,无论有多少个onClick出现,其实最后都只在DOM树上添加了一个事件处理函数,挂在最顶层的DOM节点上。所有的点击事件都被这个事件处理函数捕获,然后根据具体组件分配给特定函数,使用事件委托的性能当然要比为每个onClick都挂载一个事件处理函数要高。
      因为React控制了组件的生命周期,在unmount的时候自然能够清除相关的所有事件处理函数,内存泄露也不再是一个问题。

  • React 工作原理
    • 响应式编程: UI = render(data);
    • Virtual Dom ???????

第2章 设计高质量的React组件

  • 组件
    • 一个组件只做一件事情

    • prop

      • 组件对外的接口
      • propTypes ??????(需要扩展)

      开发者在代码中定义propTypes,在开发过程中避免犯错,但是在发布产品代码时,用一种自动的方式将propTypes去掉,这样最终部署到产品环境的代码就会更优。现有的babel-react-optimize就具有这个功能,可以通过npm安装,但是应该确保只在发布产品代码时使用它

    • state

      • 改变state的状态的时候用 this.setState,原因如下:

      直接修改this.state的值,虽然事实上改变了组件的内部状态,但只是野蛮地修改了state,却没有驱动组件进行重新渲染,既然组件没有重新渲染,当然不会反应this.state值的变化;而this.setState()函数所做的事情,首先是改变this.state的值,然后驱动组件经历更新过程,这样才有机会让this.state里新的值出现在界面上。

    • 组件不应该改变prop的值,而state的存在的目的就是让组件来改变的;

    • 组件的生命周期

      • 分为【装载过程】、【更新过程】、【卸载过程】

      • 装载过程

        • 执行的顺序:
          • constructor
          • getInitialState (es6中用不到)
          • getDefaultProps (es6中用不到)
          • componentWillMount
          • render
          • componentDidMount
        • constructor
          出现原因多是:
          - 初始化state
          - 绑定this
        • render
          - 是一个纯函数,不能改变state和props的状态
          - render函数不做实际的渲染动作,只是返回一个JSX的描述结果,最终的React来渲染;
        • componentWillMount和componentDidMount

        在装载过程中,componentWillMount会在调用render函数之前被调用,componentDidMount会在调用render函数之后被调用,这两个函数就像是render函数的前哨和后卫,一前一后,把render函数夹住,正好分别做render前后必要的工作。
        不过,我们通常不用定义componentWillMount函数,顾名思义,componentWillMount发生在“将要装载”的时候,这个时候没有任何渲染出来的结果,即使调用this.setState修改状态,也不会引发重新绘制,一切都迟了。换句话说,所有可以在这个componentWillMount中做的事情,都可以提前到constructor中间去做,可以认为这个函数存在的主要目的就是为了和componentDidMount对称。
        而componentWillMount的这个兄弟componentDidMount作用就大了。
        需要注意的是,render函数被调用完之后,componentDidMount函数并不是会被立刻调用,componentDidMount被调用的时候,render函数返回的东西已经引发了渲染,组件已经被“装载”到了DOM树上
        我们还是以ControlPanel为例,在ControlPanel中有三个Counter组件,我们稍微修改Counter的代码,让装载过程中所有生命周期函数都用console.log输出函数名和caption的值,比如,componentWillMount函数的内容如下:
        componentWillMount() {
 console.log('enter componentWillMount ' + this.props.caption);
}

上面修改并没有添加任何功能,只是通过console.log输出一些内容,然后我们刷新网页,在浏览器的console里我们能够看见:
enter constructor: First enter componentWillMount First enter render: First enter constructor: Second enter componentWillMount Second enter render: Second enter constructor: Third enter componentWillMount Third enter render: Third enter componentDidMount First enter componentDidMount Second enter componentDidMount Third
> 可以清楚地看到,虽然componentWillMount都是紧贴着自己组件的render函数之前被调用,componentDidMount可不是紧跟着render函数被调用,当所有三个组件的render函数都被调用之后,三个组件的componentDidMount才连在一起被调用。
之所以会有上面的现象,是因为render函数本身并不往DOM树上渲染或者装载内容,它只是返回一个JSX表示的对象,然后由React库来根据返回对象决定如何渲染。而React库肯定是要把所有组件返回的结果综合起来,才能知道该如何产生对应的DOM修改。所以,只有React库调用三个Counter组件的render函数之后,才有可能完成装载,这时候才会依次调用各个组件的componentDidMount函数作为装载过程的收尾。
componentWillMount和componentDidMount这对兄弟函数还有一个区别,就是componentWillMount可以在服务器端被调用,也可以在浏览器端被调用;而componentDidMount只能在浏览器端被调用,在服务器端使用React的时候不会被调用。
到目前为止,我们构造的React应用例子都只在浏览器端使用React,所以看不出区别,但后面第12章关于“同构”应用的介绍时,我们会探讨在服务器端使用React的情况。
至于为什么只有componentDidMount仅在浏览器端执行,这是一个实现上的决定,而不是设计时刻意为之。不过,如果非要有个解释的话,可以这么说,既然“装载”是一个创建组件并放到DOM树上的过程,那么,真正的“装载”是不可能在服务器端完成的,因为服务器端渲染并不会产生DOM树,通过React组件产生的只是一个纯粹的字符串而已。
不管怎样,componentDidMount只在浏览器端执行,倒是给了我们开发者一个很好的位置去做只有浏览器端才做的逻辑,比如通过AJAX获取数据来填充组件的内容
在componentDidMount被调用的时候,组件已经被装载到DOM树上了,可以放心获取渲染出来的任何DOM。
在实际开发过程中,可能会需要让React和其他UI库配合使用,比如,因为项目前期已经用jQuery开发了很多功能,需要继续使用这些基于jQuery的代码,有时候其他的UI库做某些功能比React更合适,比如d3.js已经支持了丰富的绘制图表的功能,在这些情况下,我们不得不考虑如何让React和其他UI库和平共处。
以和jQuery配合为例,我们知道,React是用来取代jQuery的,但如果真的要让React和jQuery配合,就需要是利用componentDidMount函数,当componentDidMount被执行时,React组件对应的DOM已经存在,所有的事件处理函数也已经设置好,这时候就可以调用jQuery的代码,让jQuery代码在已经绘制的DOM基础上增强新的功能。
在componentDidMount中调用jQuery代码只处理了装载过程,要和jQuery完全结合,又要考虑React的更新过程,就需要使用下面要讲的componentDidUpdate函数。
- 更新过程
- 执行顺序:
- componentWillReceiveProps
- shouldComponentUpdate
- render
- componentDidUpdate
- componentWillReceiveProps
只要父组件的render函数被调用,在render函数里面被渲染的子组件就会经历更新的过程,不管父组件传给子组件的props有没有改变;

第3章 从Flux到Redux

  • Redux

    • redux的三个基本原则: 唯一数据源;保持状态只读;数据改变只能通过纯函数完成;

    • 唯一数据源:
      只有一个Store,如何设计Store上的状态结构是要考虑的重点,原则是避免冗余的数据

    • 保持状态只读:
      修改状态,只能够通过action修改,但是这个改变不是去修改状态上的值,而是创建一个新的状态对象返回给Redux。

    • 数据改变只能通过纯函数
      reducer(state, action)
      例如:

      reducer(state, action) => {
        switch(action.type) {
          case ActionTypes.type:
            return {...state, count: count + 1}  
        }
      }
      

      说明,Redux的reducer只负责计算状态,不负责存储状态;

  • 聪明组件和傻瓜组件

    • 聪明组件:用来存储state和redux交互;
    • 傻瓜组件:通过props传入状态,只是用来渲染;
  • Context ??? (后期有时间了解一下)
    Context 这个功能提供了一个全局可以访问的对象,一般避免使用

  • React-Redux
    做了两个事情: 提供一个connect;提供一个Provider

    • connect:
      相当于聪明组件,用mapStateToProps 把聪明组件的状态传给傻瓜组件,通过mapDispatchToProps把用傻瓜组件的用户行为转化为派发给Store的动作。

第4章 模块化React和Redux应用

  • 模块化应用要点:
    • 代码文件的组织方式:
      • 按角色分类
        此处输入图片的描述
      • 按功能分类
        此处输入图片的描述
    • 模块的接口: 负责内部的联系和外部的输出
  • 工具:
    • redux-immutable-state-invariant ????

第5章 React组件性能优化

  • 性能优化
    • 使用react-redux 中的connect,原理是shouldUpadateComponent(),要诀就是避免传递给其他组件的prop值是一个不同的对象;
    • Key
      可以提高性能,要满足两点:
      • 唯一性
      • 稳定性
    • 使用reselect
      工作原理:只要相关的状态没有改变,那就使用上一次缓存的结果;
    • 范式化状态数设计
      要扁平化,范式化,所谓范式化,就是按照关系型数据库的设计原则,减少冗余数据;

第6章 React高级组件

  • 高级组件:???????
    • 定义:

      简单的说,一个高级组件就是一个函数,这个函数接受一个组件作为输入,然后返回一个新的组件作为结果,而且返回新的组件具有新的组件不具有的功能;

    • 使用意义
      • 重用代码
      • 不改变原有组件的情况下修改现有React组件的行为。
  • 以函数为子组件:???????

第7章 Redux和服务器通信

  • Redux和服务端通信:
    • fetch: 用fetch来访问服务器数据资源,fetch函数返回的结果是Promise对象,虽然被fetch广为接受,大有取代其他网络访问方式的架势,但是它有一个特性一直被人诟病,那就是fetch认为只要服务器返回一个合法的HTTP响应就算成功,就会调用then提供的回调函数,即使这个HTTP响应的状态码是表示出错了的400或者500。正因为fetch的这个特点,所以我们在then中,要做的第一件事就是检查传入参数response的status字段,只有status是代表成功的200的时候才继续,否则以错误处理。

      fetch(apiUrl).then((response) => {
      if (response.status !== 200) {
      throw new Error('Fail to get response with status ' + response.status);
      }
      
    • 访问服务器数据的时机和步骤

      • 访问服务器API是一个异步操作。因为JavaScript是单线程的语言,不可能让唯一的线程一直等待网络请求的结果,所以所有对服务器的数据请求必定是异步请求。
        但是,React组件的渲染又是同步的,当开始渲染过程之后,不可能让Weather组件一边渲染一边等待服务器的返回结果。
        总之,当Weather组件进入装载过程的时候,即使此时Weather立刻通过fetch函数发起对服务器的请求,也没法等待服务器的返回数据来进行渲染。因为React组件的装载过程和更新过程中生命周期函数是同步执行的,没有任何机会等待一个异步操作。
        所以,可行的方法只能是这样,分两个步骤完成:
        步骤1,在装载过程中,因为Weather组件并没有获得服务器结果,就不显示结果。或者显示一个“正在装载”之类的提示信息,但Weather组件这时候要发出对服务器的请求。
        步骤2,当对服务器的请求终于获得结果的时候,要引发Weather组件的一次更新过程,让Weather重新绘制自己的内容,这时候就可以根据API返回结果绘制天气信息了。
    • Redux 访问服务器

      • redux-thunk 中间件: 解决Redux访问服务器异步的问题;?????

第8章 单元测试

  • 单元测试 ????

第9章 扩展Redux

  • 中间件:
    • 中间件用来增强Redux Store的dispatch方法。

    • 中间件模板:每个中间件都是一个函数,返回一个接收next参数的函数,而接收next参数的函数又返回一个接收actions参数的函数。next参数本身也是一个函数,中间件调用这个next函数通知Redux自己的处理工作已经结束。例如以下的代码:

          function doNothingMiddleware({dispatch, getState}) {  //这两个参数是非必须的
            return function(next) {
              return function(action) {
                // 这里面可以做很多事情,比如调用dispatch派发出一个action对象;tedia调用getState获取当前的Redux Store上的状态;访问action对象上action上的所有数据;
                return next(action);
              }
            }
          }
      

      上面的代码就等同于:

       ({dispatch, getState}) => next => action => next(action)
      
    • 中间件的使用:
      以同时使用 redux-thunk 和 redux-devtools 增强功能为例:

       import {createStore, applyMiddleware, compose} from 'redux';
       import thunkMiddleware from 'redux-thunk';
       const win = window;
       const storeEnhancers = compose( //redux提供的compose函数,可以把多个store Enhancer c串联成为一个函数,因为createStore 只能够接受一个Store Enhancer 的参数;
         applyMiddleware(...middleware),  // **一定要作为第一个**
         (win && win.devToolsExrension) ? win.devToolsExension : f=> f;
      )
      // createStore 接受三个参数,第一个参数是reducer,第二个如果是对象,被认为是创建Store的初始化状态,第三个参数是增强器
      const store = createStore(reducer, storeEnhancers);
      
    • 中间件的开发?????????

第10章 动画

  • 动画
    • ReactCSSTransitionGroup
      • 主要用来解决装载过程和卸载过程中的动画;
      • transitionName
      • transitionAppear
    • React-Motion

第11章 多页面应用

  • 单页应用
    • 这种看起来是多页面但是其实只有一个页面的应用,就是单页应用;
  • React-Router
    • Link
    • react-router-redux
  • 代码分片?????
    对javascript进行分片打包,然后按需加载;

第12章 同构

  • 同构????????

以上画 ?号的都要再读一遍!!!!

@ycshill ycshill changed the title 《深入浅出React和Redux》 读书笔记 读书笔记 - 《深入浅出React和Redux》 Aug 8, 2018
@ycshill ycshill changed the title 读书笔记 - 《深入浅出React和Redux》 读书笔记-《深入浅出React和Redux》 Aug 8, 2018
@mqp0713
Copy link

mqp0713 commented Sep 3, 2018

componentDidMout少了一个n

@ycshill
Copy link
Owner Author

ycshill commented Sep 12, 2018

thank you, you are very careful! @mqp0713

@HSQCoollaughing
Copy link

是高阶组件吧,哈哈哈

@ycshill
Copy link
Owner Author

ycshill commented Feb 17, 2020

是高阶组件吧,哈哈哈

什么意思?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants