React全家桶-基础入门

846 阅读7分钟

React是什么?

React 介绍:

React 是一个用于构建用户界面的 JAVASCRIPT 库。

React 主要用于构建UI,很多人认为 React 是 MVC 中的 V(视图)。

React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。

React 特点:

  • 1.声明式设计 −React采用声明范式,可以轻松描述应用。

  • 2.高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。

    • React将DOM抽象为虚拟DOM,虚拟DOM其实就是用一个对象来描述DOM,通过对比前后两个对象的差异,最终只把变化的部分重新渲染,提高渲染的效率;

    • 当DOM发生更改时需要遍历DOM对象的属性, 而原生DOM可遍历属性多达200多个, 而且大部分属性与渲染无关, 导致更新页面代价太大;

    • 用 JS对象结构表示 DOM 树的结构,然后用这个树构建一个真正的 DOM 树,插到文档当中;

    • 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异;

    • 把记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。

  • 3.灵活 −React可以与已知的库或框架很好地配合。

  • 4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。

  • 5.组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。

  • 6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。

React初体验

<body>
    <div id="root"></div>

    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/babel.js"></script>
    <script type="text/babel">
        //1.获取页面容器元素
        const box = document.getElementById('root');
        //2.使用JSX语法
        const vDom = <h1>hello yaobinggt!</h1>
        //3.将虚拟DOM渲染到容器元素上去
        ReactDOM.render(vDom, box);//render(要渲染的内容,存放虚拟DOM的容器)
    </script>
</body>

然后我们简约一下:

<body>
    <div id="root"></div>

    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/babel.js"></script>
    <script type="text/babel">
        const vDom = <h1>hello yaobinggt!</h1>
        ReactDOM.render(vDom, document.getElementById('root'));//render(要渲染的内容,存放虚拟DOM的容器)
    </script>
</body>

JSX语法

什么是JSX

React 使用 JSX 来替代常规的 JavaScript。JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。

优点:

  • JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
  • 它是类型安全的,在编译过程中就能发现错误。
  • 使用 JSX 编写模板更加简单快速。

JSX初体验、原生js对比

我们来往容器中插入一个p标签:<p class="text">hello yaobinggt!</p>

原生js:(死板不灵活,臃肿)

<body>

    <div id="root"></div>

    <script type="text/javascript">
        const root = document.getElementById('root');//获取父级元素
        const p = document.createElement('p');//创建子元素
        p.innerHTML = "hello yaobinggttttttt!";//往创建元素里面写入内容
        root.appendChild(p);//往父级元素里面插入子元素
    </script>
</body>

典型js+react:(零活,臃肿)

<body>
    <div id="root"></div>

    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/babel.js"></script>
    <script type="text/javascript">
        let myClassName = 'text',
            myContent = 'hello yaobinggt';
        const vDOM = React.createElement('p', { className: myClassName }, myContent);
        ReactDOM.render(vDOM, document.getElementById('root'))
    </script>

jsx:(完美)

<body>
    <div id="root"></div>

    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/babel.js"></script>
    <script type="text/babel">
        let myClassName = 'text',
            myContent = 'hello yaobinggt';
        const textP = <p className={myClassName}>{myContent}</p>;
        ReactDOM.render(textP, document.getElementById('root'))
    </script>
</body>

总结:

  • JSX只是高级语法糖, 最终执行时还是会被转成原生js, 通过babel等方式
  • 更加语义化, 更加直观, 代码可读性更高
  • 性能相对原生方式更加好一些

JSX常见的界面操作方式

多重标签嵌套、内联样式的引用、js变量的引用:

<body>
    <div id="root"></div>

    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/babel.js"></script>

    <script type="text/babel">
        //js中的变量,表达式要写在{}内
        const cuba = "CUBA青年队"
        //内联样式通过对象方式引入类似插槽语法
        const myStyle = {
            width: '200px',
            height: '300px',
            backgroundColor: 'orange',
            fontSize: '30px'
        }
        //多重标签嵌套
        ReactDOM.render(
            <div style={myStyle}>
                <h1>{cuba}-集训大名单</h1>
                <p>明天我们就要出国征战了</p>
                <span style={{ color: 'orange', backgroundColor: 'red' }}>请为我们祝福</span>
            </div>
            , root);
    </script>
</body>

jsx数组遍历

<body>
    <div id="root"></div>
    
    <script type="text/babel">
        //数组遍历
        const dataArr = [
            { name: 'yaoming', numb: 11 },
            { name: 'maidi', numb: 1 },
            { name: 'koben', numb: 24 },
            { name: 'jordan', numb: 23 }
        ];
        //创建虚拟DOM
        const vDOM = (
            <ul>
                {
                    dataArr.map((data, index) => {
                        return <li>ID:{index},姓名:{data.name},球衣号:{data.numb}</li>
                    })
                }
            </ul>
        )
        //渲染虚拟DOM
        ReactDOM.render(vDOM, root)
    </script>
</body>

总结:

  • JSX中添加属性时,使用 className 代替 class , 像label中的for属性,使用htmlFor代替;
  • JSX创建DOM的时候,所有的节点,必须有唯一的根元素进行包裹 ;
  • JSX语法中,标签必须成对出现,如果是单标签,则必须自闭合;
  • 在 JSX 中可以直接使用 JS代码,直接在 JSX 中通过 {} 中间写 JS代码即可;
  • 在 JSX 中只能使用表达式,不能出现语句;
  • 在 JSX 中注释语法:{/* 中间是注释的内容 */}

组件/模块, 组件化/模块化

基本介绍

组件:

  • 一个应用/版块/页面中用于实现某个局部的功能(包括html, js, css等);
  • 把这些局部功能组装到一起就形成了完整的一个大的功能;目的:复用代码, 提高项目运行效率。

组件化:如果一个应用是用多组件的方式进行综合开发的, 那么这个应用就是一个组件化应用。

模块:多个组件形成模块, 或者是一个提供特定功能的js文件, 主要特点在于耦合性低, 可移植性高, 执行效率好。

模块化:如果一个应用都是用模块的形式来构建的,那么这个应用就是模块化应用。

组件的概念:虚拟DOM对象的集合,将一组虚拟DOM对象, 封装在一起(函数、类),就构成了一个组件;组件内部, 可以处理数据+业务逻辑;

组件的注意:组件名称首字母必须大写,只有大写才会被识别成组件;虚拟DOM 必须有且只有一个根元素;

构造函数创建组件

<body>
    <div id="root"></div>
    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/babel.js"></script>
    <script type="text/babel">
        function Yaobing(props) {
            return (
                <div>
                    <h2>My name is {props.name}</h2>
                    <p>I come from {props.address},I'm {props.age} years old! I have {props.fruit}!</p>
                </div>
            )
        }
        ReactDOM.render(<Yaobing name="yaobing" address="China" age="28" fruit={["Apple", "Orange", "Banner"]} />, root)
    </script>
</body>

构造函数多组件

<div id="root"></div>

    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/babel.js"></script>
    <script type="text/babel">
        function Header(props) {
            return (
                <header style={{ color: "#ff6600" }}>我是文章头部标题内容</header>
            )
        }
        function Content(props) {
            return (
                <section style={{ color: "#242746" }}>我是文章内容部分</section>
            )
        }
        function Footer(props) {
            return (
                <footer style={{ color: "#a000db" }}>我是文章底部内容</footer>
            )
        }
        function Article() {
            return (
                <div>
                    <Header />
                    <Content />
                    <Footer />
                </div>
            )
        }
        ReactDOM.render(<Article />, root)
   </script>
</body>

向组件传值-props

组件内部, 通过props属性值来获取,通过组件类的 defaultProps 属性为 props 设置默认值

<body>
    <div id="root"></div>
    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/prop-types.js"></script>
    <script src="./js/babel.js"></script>
    <script type="text/babel">
        //设置组件
        function Person(props) {
            return (
                <div>
                    <a href={props.link}>{props.name}</a>
                </div>
            )
        };
        //设置默认属性
        //约定(PropTypes)属性类型,必须(isRequired)
        Person.propTypes = {
            name: PropTypes.string.isRequired,
            link: PropTypes.string
        };
        //设置属性默认值
        Person.defaultProps = {
            name: "yaouu",
            link: "www.yaouu.uu"
        }
        ReactDOM.render(<Person />, root)
    </script>
</body>
<body>
    <div id="root"></div>

    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/babel.js"></script>
    <script type="text/babel">
        class Person extends React.Component {//继承(extends)React.Component类(它里面有定义组件一系列的实现形式)
            render() {
                let style = {
                    backgroundColor: 'red',
                    width: '400px',
                    height: '400px'
                }
                return (
                    <div style={style}>
                        <h2>姓名:{this.props.name}</h2>
                        <h2>年龄:{this.props.age}</h2>
                        <h2>爱好:</h2>
                        <ul>
                            {this.props.like.map((item, index) => (
                                <li key={index}>{item}</li>
                            ))}
                        </ul>
                    </div>
                )
            }
        }
        ReactDOM.render(<Person name="yaobing001" age="28" like={["打篮球", "打乒乓球", "踢足球"]} />, root)
    </script>
</body>

PropTypes不同验证器的例子:

import PropTypes from 'prop-types';

MyComponent.propTypes = {
  // 你可以将属性声明为 JS 原生类型,默认情况下
  // 这些属性都是可选的。
  optionalArray: PropTypes.array,
  optionalBool: PropTypes.bool,
  optionalFunc: PropTypes.func,
  optionalNumber: PropTypes.number,
  optionalObject: PropTypes.object,
  optionalString: PropTypes.string,
  optionalSymbol: PropTypes.symbol,

  // 任何可被渲染的元素(包括数字、字符串、元素或数组)
  // (或 Fragment) 也包含这些类型。
  optionalNode: PropTypes.node,

  // 一个 React 元素。
  optionalElement: PropTypes.element,

  // 一个 React 元素类型(即,MyComponent)。
  optionalElementType: PropTypes.elementType,

  // 你也可以声明 prop 为类的实例,这里使用
  // JS 的 instanceof 操作符。
  optionalMessage: PropTypes.instanceOf(Message),

  // 你可以让你的 prop 只能是特定的值,指定它为
  // 枚举类型。
  optionalEnum: PropTypes.oneOf(['News', 'Photos']),

  // 一个对象可以是几种类型中的任意一个类型
  optionalUnion: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message)
  ]),

  // 可以指定一个数组由某一类型的元素组成
  optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

  // 可以指定一个对象由某一类型的值组成
  optionalObjectOf: PropTypes.objectOf(PropTypes.number),

  // 可以指定一个对象由特定的类型值组成
  optionalObjectWithShape: PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
  }),
  
  // An object with warnings on extra properties
  optionalObjectWithStrictShape: PropTypes.exact({
    name: PropTypes.string,
    quantity: PropTypes.number
  }),   

  // 你可以在任何 PropTypes 属性后面加上 `isRequired` ,确保
  // 这个 prop 没有被提供时,会打印警告信息。
  requiredFunc: PropTypes.func.isRequired,

  // 任意类型的数据
  requiredAny: PropTypes.any.isRequired,

  // 你可以指定一个自定义验证器。它在验证失败时应返回一个 Error 对象。
  // 请不要使用 `console.warn` 或抛出异常,因为这在 `onOfType` 中不会起作用。
  customProp: function(props, propName, componentName) {
    if (!/matchme/.test(props[propName])) {
      return new Error(
        'Invalid prop `' + propName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  },

  // 你也可以提供一个自定义的 `arrayOf` 或 `objectOf` 验证器。
  // 它应该在验证失败时返回一个 Error 对象。
  // 验证器将验证数组或对象中的每个值。验证器的前两个参数
  // 第一个是数组或对象本身
  // 第二个是他们当前的键。
  customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
    if (!/matchme/.test(propValue[key])) {
      return new Error(
        'Invalid prop `' + propFullName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  })
};

组件内的状态机-state

React 把组件看成是一个状态机(State Machines), 通过状态 (State) 去操作状态机。

在开发中, 通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。

在React 中,只需更新组件的 state,然后根据新的 state 重新渲染用户界(不要操作 DOM)。

根据state状态来渲染界面,后期, 只要我们修改state, 那么界面就会自动重新渲染,再次执行render。

注意:

  • 不能直接修改state, 需要通过setState方法,此方法内部给出的对象, 会增量更新到原state,并不会替换原state。
  • 状态数据封装在组件内部, 不要在外界访问
  • 多次state数据"同时修改", 会被合并后, 更新一次
<body>
    <div id="root"></div>
    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/prop-types.js"></script>
    <script src="./js/babel.js"></script>
    <script type="text/babel">
        class Person extends React.Component {
            constructor(props) {//constructor 是一种用于创建和初始化class创建的对象的特殊方法。
                super(props);
                //状态机
                this.state = {
                    name: "乔丹",
                    age: 53,
                    sex: "男",
                    score: 63
                }
            }
            render() {
                return (
                    <div>
                        <h2>NBA明星简介</h2>
                        <p>姓名:{this.state.name},年龄:{this.state.age},性别:{this.state.sex},最高得分:{this.state.score}</p>
                        <p>-----------------------------------</p>
                        <button onClick={() => this._dealBtnClick()}>换一个人</button>
                    </div>
                )
            }
            _dealBtnClick() {
                //更新状态机
                this.setState({
                    name: "姚明",
                    age: 42,
                    sex: "男",
                    score: 43
                })
            }
        }
        ReactDOM.render(<Person />, root)
    </script>
</body>

state和props区别

  • props是指组件间传递的一种方式;
  • 组件内部的this.props属性是只读的不可修改。
  • state是组件内部的状态(数据);
  • 不能够直接修改,必须要通过setState来改变值的状态。
<body>
    <div id="root"></div>
    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/prop-types.js"></script>
    <script src="./js/babel.js"></script>
    <script type="text/babel">
        class Person extends React.Component {
            static propTypes = {
                personObj: PropTypes.object.isRequired
            }
            constructor(props) {//constructor 是一种用于创建和初始化class创建的对象的特殊方法。
                super(props);
                //状态机
                const { p_name, p_age, p_sex, p_score } = this.props.personObj;
                this.state = {
                    name: p_name,
                    age: p_age,
                    sex: p_sex,
                    score: p_score
                }
            }
            render() {
                const { name, age, sex, score } = this.state;
                const { p_name, p_age, p_sex, p_score } = this.props.personObj;
                return (
                    <div>
                        <h2>NBA明星简介</h2>
                        <p>姓名:{name},年龄:{age},性别:{sex},最高得分:{score}</p>
                        <p>-----------------------------------</p>
                        <p>姓名:{p_name},年龄:{p_age},性别:{p_sex},最高得分:{p_score}</p>
                        <p>-----------------------------------</p>
                        <button onClick={() => this._dealBtnClick()}>换一个人</button>
                    </div>
                )
            }
            _dealBtnClick() {
                //更新状态机
                this.setState({
                    name: "姚明",
                    age: 42,
                    sex: "男",
                    score: 43
                })
                this.setProps({
                    name: "姚明",
                    age: 42,
                    sex: "男",
                    score: 43
                })
            }
        }
        let p = {
            p_name: 'koben',
            p_age: 36,
            p_sex: '男',
            p_score: 81
        }
        ReactDOM.render(<Person personObj={p} />, root)
    </script>
</body>

组件的事件处理

  • react组件的事件处理 + React 事件绑定属性的命名采用驼峰式写法,而不是小写; + 如果使用 JSX 的语法你需要传入一个函数作为事件处理函数;例如:<button onClick={func}>点击</button>
  • react组件的事件阻止 + 不能使用返回 false 的方式阻止默认行为;明确的使用 preventDefault。
  • 事件方法中的this + 箭头函数中的this是在定义函数时绑定,且内部无this. 参照上下文确定this + 普通函数是在执行函数时绑定, 内部有this, 谁执行就是谁
  • 事件方法中传参 + 可以使用箭头函数, 二次包装
<body>
    <div id="root"></div>
    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/prop-types.js"></script>
    <script src="./js/babel.js"></script>
    <script type="text/babel">
        class Counter extends React.Component {
            static defaultProps = {
                step: 1
            }
            static propsTypes = {
                step: PropTypes.number
            }
            constructor(props) {//
                super(props); //super调用父类里面的构造函数

                //状态
                this.state = {
                    res: 0
                }
            }
            render() {
                const { step } = this.props;
                return (
                    <div>
                        <h3>结果:{this.state.res}</h3>
                        <a href="www.baidu.com" onClick={(e) => this._addNum(step, e)}>加{step}</a>
                        <p></p>
                    </div>
                )
            }
            _addNum(num, e) {
                //console.log(e);
                //阻止默认事件
                e.preventDefault();
                this.setState({
                    res: this.state.res + num
                })
            }
        }
        let vDOM = (
            <div>
                <Counter step={1} />
                <Counter step={2} />
                <Counter step={3} />
                <Counter step={4} />
                <Counter step={5} />
            </div>
        )
        ReactDOM.render(vDOM, root)
    </script>
</body>

表单事件

onChange

<script type="text/babel">
        class AssPanel extends React.Component {
            constructor(props) {
                super(props);
                this.state = {
                    content: "默认内容"
                }
            }
            _dealChange(e) {
                console.log(e.target.value);
                //更新状态机
                this.setState({
                    content: e.target.value
                });
            }
            _getDate() {
                alert(this.state.content);
            }
            render() {
                return (
                    <div>
                        <label>
                            <input type="text" value={this.state.content} onChange={(e) => this._dealChange(e)} />
                            <button onClick={() => this._getDate()}>获取输入框中的值</button>
                        </label>
                    </div>
                )
            }
        }
        ReactDOM.render(<AssPanel />, root);
    </script>

ref

<script type="text/babel">
    class AssPanel extends React.Component {
        constructor(props) {
            super(props);
            //绑定ref
            //this.myRef = React.createRef();
            this.myRef2 = React.createRef();
        }
        _dealFocus() {
            //console.log(this.myRef.current);
            //console.log(this.myRef);
            //this.myRef.current.focus();
            console.log(this.myRef2.current.defaultValue)
        }
        render() {
            return (
                <div>
                    <label>
                        <input ref={this.myRef} type="text" placeholder="请输入内容" />
                        <input ref={this.myRef2} type="button" value="获取焦点" onClick={() => this._dealFocus()} />
                    </label>
                </div>
            )
        }
    }
    ReactDOM.render(<AssPanel />, root);
</script>