zoukankan      html  css  js  c++  java
  • React学习

    React

    https://www.cnblogs.com/strick/p/10015551.html

    120分钟React快速扫盲教程
      在教程开端先说些题外话,我喜欢在学习一门新技术或读过一本书后,写一篇教程或总结,既能帮助消化,也能加深印象和发现自己未注意的细节,写的过程其实仍然是一个学习的过程。有个记录的话,在未来需要用到相关知识时,也方便自己查阅。

      React既不是一个MVC框架,也不是一个模板引擎,而是Facebook在2013年推出的一个专注于视图层,用来构建用户界面的JavaScript库。它推崇组件式应用开发,而组件(component)是一段独立的、可重用的、用于完成某个功能的代码,包含了HTML、CSS和JavaScript三部分内容。React为了保持灵活性,只实现了核心功能,提供了少量的API,一些DOM方法都被剥离到了react-dom.js中。这么做虽然轻巧,但有时候要完成特定的业务场景,还是需要与其他库结合,例如Redux、Flux等。React不仅让函数式编程愈加流行,还引入了JSX语法(能把HTML嵌入进JavaScript中)和Virtual DOM技术,大大提升了更新页面时的性能。在React中,每个组件的呈现和行为都由特定的数据所决定,而数据的流动都是单向的,即单向数据流。在编写React时,推荐使用ES6语法,官方的文档也使用了ES6语法,因此,在学习React之前,建议对ES6有所了解,避免不必要的困惑。

      下面是一段较为完整的React代码,本文大部分的示例代码来源于此,阅读下面的代码可以对React有一个感性的认识。

    import React, { Component } from 'react';
    import ReactDOM from 'react-dom';
    import PropTypes from 'prop-types';
    class Search extends Component {
    //static defaultProps = {
    // url: "http://jane.com"
    //};
    constructor(props) {
    super(props);
    this.state = {
    txt: "请输入关键字"
    };
    }
    componentWillMount() {
    console.log("component will mount");
    }
    componentDidMount() {
    console.log("component did mount");
    this.refs.txt.addEventListener("blur", (e) => {
    this.getValue(e);
    });
    console.log(this.refs)
    }
    handle(keyword, e) {
    console.log(keyword);
    console.log(this);
    console.log(this.select.value);
    }
    getValue(e) {
    console.log(e.target.value);
    }
    refresh(e) {
    this.setState({
    type: e.target.value
    });
    }
    render() {
    console.log("render");
    let {type} = this.state;
    console.log(type);
    return (


    {this.props.children}

    <select defaultValue={2} ref={(select) => this.select = select}>



    <input placeholder={this.state.txt} ref="txt" defaultValue="教程" style={{marginLeft:10, textAlign:"center"}}/>
    <button className="btn" data-url={this.props.url} onClick={this.handle.bind(this, "REACT")}>{"<搜索>"}

    );
    }
    }
    Search.defaultProps = {
    url: "http://jane.com"
    };
    ReactDOM.render(

    React扫盲教程


    ,
    document.getElementById("container")
    );
    一、JSX语法
      JSX是对JavaScript语法的一种扩展,它看起来像HTML,同样拥有清晰的DOM树状结构和元素属性,如下代码所示。但与HTML不同的是,为了避免自动插入分号时出现问题,在最外层得用圆括号包裹,并且必须用一个元素包裹(例如下面的
    元素)其它元素,所有的元素还必须得闭合。

    (



    ) 1)元素

      JSX中的元素分为两种:DOM元素和组件元素(也叫React元素),DOM元素就是HTML文档映射的节点,首字母要小写;而组件元素的首字母要大写。无论是DOM元素还是组件元素,最终都会通过React.createElement()方法转换成JSON对象,如下所示,JSON对象是简化过的。

    //React.createElement()方法
    React.createElement("div", null, [
    React.createElement("input", { placeholder: ${this.state.txt} }, null),
    React.createElement("button", { className: "btn" }, "<搜索>")
    ]);
    //简化过的JSON对象
    {
    type: "div",
    props: {
    children: [
    {
    type: "input",
    props: {
    placeholder: ${this.state.txt}
    }
    },
    {
    type: "button",
    props: {
    className: "btn",
    children: "<搜索>"
    }
    }
    ]
    }
    }
      由于JSX中的元素能够被编译成对象,因此还可以把它们应用到条件、循环等语句中,或者作为一个值应用到变量和参数上。

    2)属性

      JSX中的属性要用驼峰的方式表示,例如maxlength要改成maxLength、readonly要改成readOnly。还有两个比较特殊的属性:class和for,由于这两个是JavaScript中的关键字,因此要改名,前者改成className,后者改成htmlFor。

      JSX中的属性值不仅可以是字符串,还可以是表达式。如果是表达式,那么就要用花括号(“{}”)包裹。而在JSX中,任何位置都可以使用表达式。

      有一点要注意,为了防止XSS的攻击,React会把所有要显示到DOM中的字符串进行转义,例如“<搜索>”转义成“<搜索>”。

    3)Virtual DOM

      众所周知,DOM操作会引起重绘和重排,而这是非常消耗性能的。React能把元素转换成对象,也就是说可以用对象来表示DOM树,而这个存在于内存中的对象正是Virtual DOM。Virtual DOM相当于一个缓存,当数据更新后,React会重新计算Virtual DOM,再与上一次的Virtual DOM通过diff算法做比对(如下图所示),最后只在页面中更新修改过的DOM。由于大部分的操作都在内存中进行,因此性能将会有很大的提升。

    二、组件
      组件的构建方式有3种:React.createClass()、ES6的类和函数。当用ES6的类来构建时,所有的组件都继承自抽象基础类React.Component,该抽象类声明了state、props、defaultProps和displayName等属性,定义了render()、setState()和forceUpdate()等方法。注意,在组件的构造函数constructor()中要调用super()函数,用于初始化this和执行抽象类的构造函数。

    import React, { Component } from 'react';
    class Search extends Component {
    constructor (props) {
    super(props);
    }
    render() {
    return ();
    }
    }
      组件中的render()方法是必须的,它会返回一个元素、数字或字符串等各种值。render()是一个纯函数,即输出(返回值)只依赖输入(参数),并且执行过程中没有副作用(不改变外部状态)。

      组件之间可以相互嵌套,而它们的数据流是自顶向下流动的(如下图所示),即父组件将数据传给子组件。此处传递的数据就是组件的配置参数,由props属性控制,而组件的内部状态保存在state属性中。

    1)props

      如果一个组件要做到可复用,那么它应该是可配置的。为此,React提供了props属性,它的使用如下所示。

    class Search extends Component {
    render() {
    return (




    );
    }
    }

      先给Search组件定义一个名为url的属性,然后在组件内部,可以通过引用props属性来获取url的值。有一点要注意,props是只读属性,因此在组件内部无法修改它。

      React为组件提供了默认的配置,可以调用它的静态属性defaultProps。总共有两种写法实现默认配置,如下代码所示,其中写法一用到了ES6中的static关键字。

    //写法一
    class Search extends Component {
    static defaultProps = {
    url: "http://jane.com"
    };
    }
    //写法二
    Search.defaultProps = {
    url: "http://jane.com"
    };

      此时,即使组件不定义url属性,在组件内部还是会有值。

      props还有一个特殊的属性:children,它的值是组件内的子元素,如下代码所示,children属性的值为“

    React扫盲教程

    ”。

    class Search extends Component {
    render() {
    return (


    {this.props.children}

    );
    }
    }

    React扫盲教程

    2)state

      组件的呈现会随着内部状态和外部配置而改变,通常会在组件的构造函数中初始化需要的内部状态,如下代码所示,为文本框添加默认提示。

    class Search extends Component {
    constructor (props) {
    super(props);
    this.state = {
    txt: "请输入关键字"
    };
    }
    }
      React还提供了setState()方法,用于更新组件的状态。注意,不要通过直接为state赋值的方式来更新状态,因为setState()方法在更新状态后,还会调用render()方法,重新渲染组件。此外,React为了提升性能,会把多次setState()调用合并成一次,像下面这样写打印出的txt属性的值仍然是前一次的值,因此状态更新是异步的。

    this.setState({
    txt: "React"
    });
    console.log(this.state.txt); //"请输入关键字"
    3)生命周期

      组件的生命周期(life cycle)可简单的分为4个阶段:初始化(Initialization)、挂载(Mounting)、更新(Updation)和卸载(Unmounting),具体如下图所示。每个阶段都会对应几个方法,其中包含will的方法会在某个方法之前被调用,而包含did的方法会在某个方法之后被调用。

    1、在初始化阶段,会设置props、state等属性。

    2、在挂载阶段,两个挂载方法将以组件的render()为分界点。

    3、更新阶段发生在传递props或执行setState()的时候。

    4、当一个组件被移除时,就会调用componentWillUnmount()方法。

    当组件在页面中输出时,在控制台将依次输出“will mount”、“render”和“did mount”。

    class Search extends Component {
    componentWillMount() {
    console.log("will mount");
    }
    componentDidMount() {
    console.log("did mount");
    }
    render() {
    console.log("render");
    }
    }
    三、React和DOM
    1)ReactDOM

      如果要把组件添加到真实的DOM中,那么就需要使用ReactDOM中的render()方法,如下代码所示,其实在前面已经调用过几次这个方法了。

    ReactDOM.render(
    ,
    document.getElementById("container")
    );
      此方法可接收三个参数,第一个是要渲染(即添加)的元素,第二个是容器元素(即添加的位置),第三个是可选的回调函数,会在渲染或更新之后执行。

      ReactDOM还提供了另外两个方法:unmountComponentAtNode()和unmountComponentAtNode(),具体可参考官方文档。

    2)事件

      React实现了一种合成事件(SyntheticEvent),合成事件只有冒泡传播,并且它的注册方式、事件对象和事件处理程序中的this对象都与原生事件不同。

    1、合成事件会通过设置元素的属性来注册事件,但与原生事件不同的是,属性的命名要用驼峰的写法而不是全部小写,并且属性值可以是任意类型而不再仅是字符串,如下代码所示。React已经封装好了一系列的事件类型(原生事件类型的一个子集),并且已经处理好它们的兼容性,提供的事件类型可以参考官网。

    class Search extends Component {
    handle(e) {
    console.log("click");
    }
    render() {
    return (




    );
    }
    }
    2、合成事件中的事件对象(event object)是一个基于W3C标准的SyntheticEvent对象的实例,它不但与原生的事件对象拥有相同的属性和方法(例如cancelable、preventDefault()、stopPropagation()等),还完美解决了兼容性问题。

    3、React的事件处理程序中的this对象默认是undefined,因为注册的事件都是以普通函数的方式调用的。如果要让this指向当前组件,那么可以用bind()方法或ES6的箭头函数。

    class Search extends Component {
    //bind()方法
    handle1(e) {
    console.log(this);
    }
    //箭头函数
    handle2 = (e) => {
    console.log(this);
    };
    render() {
    return (





    );
    }
    }
    4、在向事件处理程序传递参数时,要把事件对象放在最后,如下代码所示。

    class Search extends Component {
    handle(keyword, e) {
    console.log(keyword);
    console.log(this);
    }
    render() {
    return (


    <button onClick={this.handle1.bind(this, "REACT")}>搜索

    );
    }
    }
    5、如果要为组件中某个元素注册原生事件,那么可以利用元素的ref属性和组件的refs对象实现。例如实现一个文本框在失去焦点时,打印输出它的值,如下代码所示。注意,原生事件的注册要在componentDidMount()方法内执行。

    class Search extends Component {
    componentDidMount() {
    this.refs.txt.addEventListener("blur", (e) => {
    this.getValue(e);
    });
    }
    getValue(e) {
    console.log(e.target.value);
    }
    render() {
    return (




    );
    }
    }
      在上面的代码中,ref属性的值被设为了“txt”,此时,在refs对象中就会出现一个名为“txt”的属性,关于这个它们的具体用法可以参考官网。

    3)表单

      HTML中的表单元素(例如元素)中的value属性表示选中项。

    class Search extends Component {
    refresh(e) {
    this.setState({
    type: e.target.value
    });
    }
    render() {
    let {type} = this.state;
    return (




    );
    }
    }
      非受控组件(uncontrolled component)的状态不受React组件控制,也不用为每个状态编写对应的事件处理程序,但可以通过元素的ref属性获取它的值,非受控组件的写法更像是传统的DOM操作。在使用非受控组件时,如果要为其设置默认值,可以使用属性defaultValue或defaultChecked,具体如下所示。

    class Search extends Component {
    handle(e) {
    console.log(this.select.value);
    }
    render() {
    return (


    <select defaultValue={2} ref={(select) => this.select = select}>





    );
    }
    }
    4)样式

      在React问世的初期,由于它推崇组件模式,因此会要求HTML、CSS和JavaScript混合在一起,这与过去的关注点分离正好相反。React已将HTML用JSX封装,而对CSS的封装,则抛出了CSS in JS的解决方案,即用JavaScript写CSS。

      在React中的元素都包含className和style属性,前者可设置CSS类,后者可定义内联样式。style的属性值是一个对象,其属性就是CSS属性,但属性名要用驼峰的方式命名,例如margin-left改成marginLeft,具体如下所示。

    class Search extends Component {
    render() {
    return (


    <input style={{marginLeft:10, textAlign:"center"}}/>

    );
    }
    }
      注意,属性名不会自动补全浏览器前缀,并且React会自动给需要单位的数字加上px。在MDN上给出了CSS属性用JavaScript命名的对应关系,可在此处参考。

      由于React处理CSS的功能并不强大,因此市面上出现了很多与CSS in JS相关第三方类库,例如classnames、polished.js等,有外国网友还专门搜集了40多种相关的类库。

      虽然这种方式能让组件更方便的模块化,但同时也彻底抛弃了CSS,既不能使用CSS的特性(例如选择器、媒体查询等),也无法再用CSS预处理器(例如SASS、LESS等)。为了解决上述问题,又有人提出了CSS Modules。

      如果要在React中制作动画,官方推荐使用React Transition Group和React Motion。不过,你也可以使用普通的动画库(例如animejs),只要在DOM渲染好以后调用即可。

    四、React进阶
    1)跨级通信

      React数据流动是单向的,组件之间通信最常见的方式是父组件通过props向子组件传递信息,但这种方式只能逐级传递,如果要跨级通信(即父组件与孙子组件通信),那么可以利用状态提升实现,但这样的话,代码会显得很不优雅并且很臃肿。好在React包含一个Context特性,可以满足刚刚的需求,不过官方不建议大量使用该特性,因为它不但会增加组件之间的耦合性,还会让应用变得混乱不堪,下图演示了两种数据传递的过程。在理解Context特性后,能更合理的使用状态管理容器Redux。

      当一个组件设置了Context后,它的子组件就能直接访问Context中的内容,Context相当于一个全局变量,但作用域仅限于它的子组件中。总共有两种Context的实现方式,都基于生产者消费者模式。首先来看第一种,具体代码如下所示。

    import PropTypes from 'prop-types';
    class Grandpa extends Component {
    getChildContext() {
    return { name: "strick" };
    }
    render() {
    return ();
    }
    }
    Grandpa.childContextTypes = {
    name: PropTypes.string
    };
    class Son extends Component {
    render() {
    return ();
    }
    }
    class Grandson extends Component {
    render() {
    let { name } = this.context;
    return (

    爷爷叫{name}
    );
    }
    }
    Grandson.contextTypes = {
    name: PropTypes.string
    };
      在上面的代码中,创建了三个组件,Grandpa是最上层的父组件(生产者),Son是中间的子组件,Grandson是最下层的孙子组件(消费者)。首先在Grandpa中,声明了一个静态属性childContextTypes和一个getChildContext()方法,这两个是必须的,否则无法实现数据传递。其中childContextTypes是一个对象,它的属性名就是要传递的变量名,而属性值则通过PropTypes指明了该变量的数据类型,getChildContext()方法返回的对象就是要传递的一组变量和它们的值。然后在Son中渲染Grandson组件。最后为Grandson声明一个静态属性contextTypes,同样是个对象,并且属性名和属性值与childContextTypes中的相同。

      第二种方式是在React 16.3的版本中引入的,比起第一种方式,写法更加简洁,并且Context的生产者和消费者都以组件的方式实现,如下所示。

    let NameContext = React.createContext({ name });
    class Grandpa extends Component {
    render() {
    return (
    <NameContext.Provider value={{name: "strick"}}>

    </NameContext.Provider>
    );
    }
    }
    class Son extends Component {
    render() {
    return ();
    }
    }
    class Grandson extends Component {
    render() {
    return (
    <NameContext.Consumer>
    {(context) => (

    爷爷叫{context.name}

    )}
    </NameContext.Consumer>
    );
    }
    }
      上面的代码依然创建了三个组件,名字也和第一种方式中的相同。除了中间组件Son之外,另外两个组件的内容发生了变化。首先,通过React.createContext()方法创建一个Context对象,此对象包含两个组件:Provider和Consumer,前者是生产者,后者是消费者。然后在Grandpa的render()方法中设置Provider组件的value属性,此属性相当于getChildContext()方法。最后在Grandson组件中调用Context对象,注意,Consumer组件的子节点只能是一个函数。

    2)高阶组件

      高阶组件(higher-order component,简称HOC)不是一个真的组件,而是一个函数,它的参数中包含组件,其返回值是一个功能增强的新组件。高阶组件是一个没有副作用的纯函数,它遵循了装饰者模式的设计思想,不会修改传递进来的原组件,而是对其进行包装和拓展,不仅增强了组件的复用性和灵活性,还保持了组件的易用性。下面演示了高阶组件是如何控制props和state的。

    class Button extends Component {
    render() {
    return (




    );
    }
    }
    //高阶组件
    function HOC(Mine) {
    class Wrapped extends Component {
    constructor() {
    super();
    this.state = {
    txt: "提交"
    };
    }
    render() {
    return <Mine {...this.state} />;
    }
    }
    return Wrapped;
    }
    let Wrapped = HOC(Button);
      高阶组件HOC()的函数体中创建了一个名为Wrapped的组件,在它的构造函数中初始化了state状态。然后在其render()方法中使用了{...this.state},这是JSX的一种语法,在state对象前添加扩展运算符,可把它解构成组件的一组属性。最后在Button组件中调用传递进来的属性。

      高阶组件还有迁移重复代码、劫持render()方法和引用refs等功能。

    五、后记
      就先整理这些了,如有错误,欢迎指正,后面还会陆续加入漏掉的知识点。

      最后,我想说下,其实自己也是一个React初学者,通过这样的梳理后,对React有了更为深刻的理解,在后续的学习中能容易的吸收新的知识点。

    源码下载:

    https://github.com/pwstrick/react

    参考资料:

    React中文文档

    《深入React技术栈》

    React.js 小书

    React WIKI

    《深入浅出React和Redux》

    React入门实例教程 阮一峰

    React入门教程

    全栈React: 第1天 什么是 React?

    深度剖析:如何实现一个 Virtual DOM 算法

    從零開始學 ReactJS

    react component lifecycle

    CSS Modules 入门及 React 中实践

    CSS in JS 简介

    聊一聊我对 React Context 的理解以及应用

  • 相关阅读:
    java.lang.NoSuchMethodError
    asm相关内容想下载(包括 jar 包)
    Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: org/objectweb/asm/Type
    用Navicat连接mysql报错:2003-Can't connect to MySql server on '10.100.0.109'(10039)
    The type java.lang.reflect.AnnotatedElement cannot be resolved. It is indirectly referenced from required .class files
    The type java.lang.CharSequence cannot be resolved. It is indirectly referenced from required .class files
    交通测速方式
    卡口和电子警察的区别
    Myeclipse连接Mysql数据库时报错:Error while performing database login with the pro driver:unable
    在window上安装mysql
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/10104215.html
Copyright © 2011-2022 走看看