zoukankan      html  css  js  c++  java
  • React 初识

    React

    We built React to solve one problem: building large applications with data that changes over time.

    • 声明式的,用于构建用户界面的 JavaScript 库
    • 组合模型,using composition instead of inheritanc
    • 单向响应的数据流
    • JSX,语法糖,类型检查,执行速度快(尽可能减少与DOM直接操作的次数)

    核心

    • 组件
    • 虚拟DOM:解决jQuery操作真实DOM慢的问题
    • 响应式UI

    普通的现代构建管道通常包括

    • 包管理器(package manager):如npm或Yarn。它可以利用大量的第三方软件包生态系统,并轻松安装或更新它们
    • 打包工具(bundler):如webpack或Browserify。它允许编写模块化代码并将他们打包成为一个小包,以实现加载性能的优化,节省加载时间
    • 编译器(compiler):如Babel。它可以在编写现代JavaScript代码的同时兼容旧版本浏览器

    常用库概览

    react.js:React的核心库
    react-dom.js:提供与DOM相关的操作功能
    Browser.js:将JSX语法转为JavaScript语法(耗时)
    

    元素

    Elements are the smallest building blocks of React apps.

    组件

    Small and Isolated pieces of code,模版即组件,组件即HTML自定义标签,是包含了模板代码的一种特殊的HTML标签类型。

    • 函数式组件
    • 类组件

    当组件第一次渲染到DOM时,在React中称为挂载(mounting);当组件产生的DOM被销毁时,在React中称为卸载(unmounting)。

    所有React组件都必须是纯函数,并禁止修改其自身props 。在JSX回调中必须注意this的指向,提供3种方法:

    • 在构造函数中显式绑定:.bind(this)
    • 使用箭头函数:onClick={(e) => this.handleClick(e)}
    • 保持使用ES5风格:createReactClass

    推荐第1种。箭头函数每次渲染时都创建一个不同的回调。多数情况下没问题,然而如果这个回调被作为prop(属性)传递给下级组件,这些组件可能需要额外的重复渲染。

    setState

    • (可能是)异步执行
    • 构造函数是唯一可以初始化 this.state 的地方
    // ok,当前值不依赖上一次的值
    this.setState({name: 'Hello'});
    
    // 提供callback方式1
    this.setState((prevState, props) => { 
      return { ...prevState, name: props.name }; 
    });
    // 或
    this.setState((prevState, props) => ({
      counter: prevState.counter + props.increment
    }));
    // 或
    this.setState(function(prevState, props) {
      return {
        counter: prevState.counter + props.increment
      };
    });
    
    // 提供callback方式2
    this.setState({
      name: 'qwer'
      }, ()=>{
          console.log(this.state.name); //qwer
    });
    

    在状态更新、渲染完成后,会触发对回调函数的执行。有关信息参见:react - setState

    状态提升(Lifting State Up)

    state创建有2种:

    1. 当组件以class类创建,state可以是class的属性值,也可以在构造函数通过this.state赋值来创建
    2. 当组件以函数创建,state需通过函数的getIntialState函数的返回值来创建

    在React中,共享state(状态)是通过将其移动到需要它的组件的最接近的共同祖先组件来实现,使React的state成为 “单一数据源原则” 。
    在一个React应用中,对于任何可变的数据都应该循序“单一数据源”原则,依赖从上向下的数据流向。
    对于UI中的错误,使用React开发者工具来检查props,向上遍历树,直到找到负责更新状态的组件,跟踪到bug的源头。
    具体参见:React Developer Tools

    将参数传递给事件处理程序

    • arrow functions:箭头函数方式,参数e作为React事件对象作为第二个参数进行显式传递
    • Function.prototype.bind:bind方式,事件对象以及更多的参数将会被隐式传递
    <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
    <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
    

    对于第2种方法,方法定义的形式

    deleteRow(id, event) {...}  

    当需要从子组件中更新父组件的state时,要在父组件创建事件句柄 (handleChange) ,并作为prop (updateStateProp) 传递到子组件。

    var Content = React.createClass({
      render: function() {
        return  <div>
                    <button onClick = {this.props.updateStateProp}>点我</button>
                    <h4>{this.props.myDataProp}</h4>
                  </div>
      }
    });
    var HelloMessage = React.createClass({
      getInitialState: function() {
        return {value: 'Hello Runoob!'};
      },
      handleChange: function(event) {
        this.setState({value: '菜鸟教程'})
      },
      render: function() {
        var value = this.state.value;
        return <div>
                  <Content myDataProp = {value} 
                     updateStateProp = {this.handleChange}></Content>
                  </div>;
      }
    });
    ReactDOM.render(
      <HelloMessage />,
      document.getElementById('example')
    );
    

    综上,对于显式bind方式,通式如下

    handleclick(要传的参数,event){...} 
    onClick = {this.handleclick.bind(this,要传的参数)}

    组件钩子函数生命周期

    。。。

    高阶组件

    高阶组件是一个函数,接受一个组件并返回一个新的组件

    const EnhancedComponent = higherOrderComponent(WrappedComponent);
    
    • 高阶组件既不会修改输入组件,也不会通过继承来复制行为。
    • 通过包裹的形式,高阶组件将原先的组件组合在容器组件中。
    • 高阶组件是纯函数,没有副作用。
    • 高阶组件最好是通过将输入组件包裹在容器组件的方式来使用组合。

    错误边界

    Error Boundaries 是React组件,它可以在子组件树的任何位置捕获JavaScript错误、记录这些错误,并显示一个备用UI,而不是使整个组件树崩溃。但是,仅可以捕获其子组件的错误,无法捕获其自身的错误。对以下情况无能为力:

    • 事件处理:事件处理器内部的错误,采用try{}catch(){}捕获即可
    • 异步代码(例如 setTimeout 或 requestAnimationFrame 回调函数)
    • 服务端渲染
    • 错误边界自身抛出来的错误(而不是其子组件)

    钩子函数:componentDidCatch(error, info)
    类似JS的catch(){}方法,如果一个错误边界无法渲染错误信息,则错误会向上冒泡至最接近的错误边界。

    class ErrorBoundary extends React.Component {
      constructor(props) {
        super(props);
        this.state = { error: null, errorInfo: null };
      }
      
      componentDidCatch(error, errorInfo) {
        // Catch errors in any components below and re-render with error message
        this.setState({
          error: error,
          errorInfo: errorInfo
        })
        // You can also log error messages to an error reporting service here
      }
      
      render() {
        if (this.state.errorInfo) {
          // Error path
          return (
            <div>
              <h2>Something went wrong.</h2>
              <details style={{ whiteSpace: 'pre-wrap' }}>
                {this.state.error && this.state.error.toString()}
                <br />
                {this.state.errorInfo.componentStack}
              </details>
            </div>
          );
        }
        // Normally, just render children
        return this.props.children;
      }  
    }   

    ref属性

    用来绑定到render()输出的任何组件上,允许引用render()返回的相应的支撑实例(Backing Instance)。

    简言之,用于从组件获取真实的DOM结点。

    • 处理focus、文本选择或者媒体播放
    • 触发强制动画
    • 集成第三方DOM库
    • 访问<input type="file">表单要提交处理的文件

    组件并不是真实的DOM节点,而是存在于内存之中的一种数据结构,叫做虚拟DOM (virtual DOM)。只有当它插入文档以后,才会变成真实的DOM 。根据 React 的设计,所有的DOM变动,都先在虚拟DOM上发生,然后再将实际发生变动的部分,反映在真实DOM上,这种算法叫做 DOM diff,可以极大提高网页的性能表现。

    场景:获取组件new出来的实例,绕过父子组件通信的约束,直接操作组件new出来的实例

    • 通过 ref 属性获取真实的React元素实例
    • 通过 ReactDOM.findDOMNode(组件真实实例) 返回真实的DOM结构

    通过ref属性获取到React组件new出来的真实实例,前置条件:

    • 自定义组件
    • 通过class来定义

    真实DOM结点结构 = ReactDOM.findDOMNode(组件真实实例)

    this.props.children属性

    表示组件的所有子结点,其值有三种可能:

    • undefined:当前组件没有子节点
    • object:有且仅有有一个子节点
    • array:有多个子节点

    React提供工具方法:React.Children,智能处理(容错) this.props.children。

    React.Children.map(children, function[(thisArg)])
    React.Children.forEach(children, function[(thisArg)])
    React.Children.count(children)
    React.Children.only(children)
    React.Children.toArray(children)
    

    条件渲染组件

    To do this return null instead of its render output:从组件的render方法返回null不会影响组件生命周期方法的触发。

    function WarningBanner(props) {
      if (!props.warnsInfoMsg) {
        return null;
      }
    
      return (
        <div className="warning">
          Warning! {this.props.warnsInfoMsg}
        </div>
      );
    }
    

    模块加载机制

    CommonJS规范

    • fs:文件系统模块,负责读写文件,支持stream和pipe(自动流式读写)
    • http:web服务器模块,提供request和response对象分别封装http请求和响应  
    • cypto:加密解密模块,哈希算法,数字证书
    // 模块对外提供变量或函数方法
    module.exports = {
        key: value
        ...
    };
    // 引入模块
    const module = require('./Module.js');
    

    context

    上下文,提供通过组件树传递数据的方法,在组件间共享数据,避免通过中间元素传递 props。

    • 使用props参数传递方式
    class App extends React.Component {
      render() {
        return <Toolbar theme="dark" />;
      }
    }
    
    function Toolbar(props) {
      // The Toolbar component must take an extra "theme" prop
      // and pass it to the ThemedButton. This can become painful
      // if every single button in the app needs to know the theme
      // because it would have to be passed through all components.
      return (
        <div>
          <ThemedButton theme={props.theme} />
        </div>
      );
    }
    
    function ThemedButton(props) {
      return <Button theme={props.theme} />;
    }
    
    • 使用context:Stick to cases where the same data needs to be accessed in many components at multiple levels.
    // Context lets us pass a value deep into the component tree without explicitly threading it through every component.
    
    // Create a context for the current theme (with "light" as the default).
    const ThemeContext = React.createContext('light');
    
    class App extends React.Component {
      render() {
        // Use a Provider to pass the current theme to the tree below.
        // Any component can read it, no matter how deep it is.
        // In this example, passing "dark" as the current value.
        return (
          <ThemeContext.Provider value="dark">
            <Toolbar />
          </ThemeContext.Provider>
        );
      }
    }
    
    // A component in the middle doesn't have to pass the theme down explicitly anymore.
    function Toolbar(props) {
      return (
        <div>
          <ThemedButton />
        </div>
      );
    }
    
    function ThemedButton(props) {
      // Use a Consumer to read the current theme context.
      // React will find the closest theme Provider above and use its value.
      // In this example, the current theme is "dark".
      return (
        <ThemeContext.Consumer>
          {theme => <Button {...props} theme={theme} />}
        </ThemeContext.Consumer>
      );
    }
    

    其中,ThemeContext 是 {Provider, Consumer} 对象

    const {Provider, Consumer} = React.createContext(defaultValue);
    
    <Provider value={/* some value */}>
    
    <Consumer>
      {value => /* render something based on the context value */}
    </Consumer>  

    仔细体会,该方式类似发布订阅模式,provider生产数据,Consumer消费数据。 

    具体参见:Context - React

    编程思想

    页面设计流程

    1. 组件拆解:自下而上,由内而外,单一职责原则
    2. 组件组合:各组件组合成静态页面框架
    3. 确定UI状态(state)的最小但完整集合表示
    4. 确定state位置:状态提升,公共父级组件
    5. 组件通信交互:回调函数(反向数据流)
      • 父组件将方法名作为参数传递给子组件
      • 在子组件上触发的事件调用父组件的方法以更新父组件的状态

    注:不要在子组件上直接调用父组件方法。子组件应该封装一个事件句柄,在句柄内调用回调函数。

    关于确定 UI state

    • 是否通过props(属性)从父级传入? 若是,它可能不是state(状态)。
    • 是否永远不会发生变化? 若是,它可能不是state(状态)。
    • 是否可以由组件中其他的state(状态)或props(属性)计算得出?若是,则它不是state(状态)。

    具体参见:React 编程思想

    学习了下React 核心开发者出品的 React 设计思想 ,简单总结如下:

    • 变换(Transformation):纯函数

    • 抽象(Abstraction):函数/组件调用

    • 组合(Composition):组合思想,而非继承

    • 状态(State):不可变性,setState()

    • Memoization:记忆缓存

    • 列表(Lists)

    • 连续性(Continuations)

    • 代数效应(Algebraic Effects):context

    经验避坑

    关于React注释

    • 在标签外的的注释不能使用花括号
    • 在标签内部的注释需要花括号
    ReactDOM.render(
        /*注释 */
        <h1>xxxx {/*注释*/}</h1>,
        document.getElementById('root')
    );
    

    关于Html与React

    • 在React的.js文件中,标签中属性和事件必须驼峰格式
    • 为组件添加属性时,原生Html中的关键字class和for必须写成className和htmlFor 

    环境判断

    根据浏览器和Node环境提供的全局变量名称来判断当前环境:

    if (typeof(window) === 'undefined') {
        console.log('node.js');
    } else {
        console.log('browser');
    }
    

    关于规范

    • 添加属性Object.assign() 
    • 扩展运算符(...)拷贝数组  
    • Array.from():将类数组对象转为数组  
    • 所有配置项都应该集中在1个对象中,作为最后1个参数,布尔值不可以直接作为参数。  
    • 使用帕斯卡式命名构造函数或类 

    具体参见:Airbnb React/JSX Style Guide;  

    参考

    React 官网 - EngReact 中文文档

    React 入门实例教程 -阮一峰

  • 相关阅读:
    js_阶乘
    python 最新方案-解决编码错误问题:UnicodeEncodeError: 'ascii' codec can't encode characters in position
    RabbitMQ Python端发送消息给Java端 接受消息全数字问题
    【Hadoop】第六天 Hive
    【Hadoop 】第五天 Hadoop-HA 搭建
    Redis面试常问点
    python md5 salt 摘要算法
    【Hadoop】第四天 Shuffle MapReduce组件全貌
    【Hadoop】第三天 mapreduce的原理和编程
    【Hadoop 】第二天 hdfs的原理和使用操作、编程
  • 原文地址:https://www.cnblogs.com/wjcx-sqh/p/9195851.html
Copyright © 2011-2022 走看看