zoukankan      html  css  js  c++  java
  • 组件化和 React

    一,对组件化的理解

    1,组件的封装

    -视图

    -数据

    -变化逻辑(数据驱动视图变化)

    例:

    import React, {
        Component
    } from 'react';
    import List from './list/index.js';
    import Input from './input/index.js';
    
    class Todo extends Component {
        constructor(props) {
            // 数据
            super(props)
            this.state = {//保存当前组件的变量
                list:[]
            }
        }
        render() {
            return (
                // 视图
                <div>
                    <Input addTitle={this.addTitle.bind(this)}/>
                    <List data={this.state.list} />
                    <List data={[1,2,3]} />
                </div>
            )
        }
        // 变化逻辑
        addTitle(title) {
            const currentList = this.state.list;
            this.setState({
                list:currentList.concat(title)
            })
        }
    }
    
    export default Todo

    2,组件的复用

    -props传递

    -复用

    例:

    import React, {
        Component
    } from 'react';
    
    class List extends Component {
        constructor(props) {
            super(props)
        }
        render(){
            const list = this.props.data; //props传递
            return (
                <ul>
                    {
                        list.map((item,index) => {
                            return <li key={index}>{item}</li>
                        })
                    }
                </ul>
            )
        }
    }
    
    export default List

    二,JSX本质

    1,JSX语法:

    例:

      // html形式
      // 引入JS变量和表达式
      // if...else...
      // 循环
      // style和className
      // 事件
      render() {
        const name = '12';
        const show = true;
        const list = [1,2,3,4,5];
        const styleConfig = {
          fontSize:'40px',
          color:'blue'
        }
        return (
          <div className='container'>
            {/* <Todo /> */}
            <p>{name}</p>
            <p>{name ? 1 : 0}</p>
            <p>{(name === '12').toString()}</p>
            <p>{name || 'lisi'}</p>
            {show ? <img src='./logo.svg' /> : ''}
            <ul>
              {list.map(function(item,index) {
                return <li key={index}>{item}</li>
              })}
            </ul>
            <p style={styleConfig}>this is a p</p>
            <p style={{fontSize:'40px',color:'blue'}}>this is a p</p>
          </div>
          
        );

    2,JSX解析成JS:

    JSX语法根本无法被浏览器所解析,那么它如何在浏览器运行?-转换成JS运行

    思考:为何react组件中,React 没有显示地使用,但却必须要引入?(去掉的话会报错)  'React' must be in scope when using JSX 

    import React, {
      Component
    } from 'react';

    看个例子:

        // JSX代码
        var profile = <div>
                        <img src="avatar.png" className="profile" />
                        <h3>{[user.firstName,user.lastName].join('')}</h3>
                      </div>;

    解析结果:

        // 解析结果 有没有跟vdom的h函数、vue的_c函数很像?yes!
        var profile = React.createElement("div",null,
                      React.createElement("img",{src:"avatar.png",className:"profile"}),
                      React.createElement("h3",null,[user.firstName,user.lastName].join('')),
        );

     从上面得知,我们所写的JSX代码,都是需要通过 React.createElement 函数来解析

    React.createElement 参数说明
     ReactElement createElement(
          string/ReactClass type,
          [object props],
          [children ...]
        )

    (类似 vdom 的 h 函数、vue 的 _c 函数)例:

        React.createElement('div',{id:'div1'},child1,child2,child3)
        React.createElement('div',{id:'div1'},[...])

    再看:

    render(){
            const list = this.props.data;
            return (
                <ul>
                    {
                        list.map((item,index) => {
                            return <li key={index}>{item}</li>
                        })
                    }
                </ul>
            )
        }

    解析如下:

       function render() {
            const list = this.props.data;
            return React.createElement(
                'ul',
                null,
                list.map((item,index) => {
                    return React.createElement(
                        'li',
                        {key:index},
                        item
                    )
                }) 
            )
        }

    JSX的写法大大降低了学习成本和编码工作量

    同时,JSX也会增加debug成本

    3,独立的标准

    JSX 是  React 引入的,但不是 React 独有的,已经是一个标准

    React 已经将它作为一个独立标准开放,其他项目也可以使用

    React.createElement 是可以自定义修改的 

    说明:本身功能已经完备;和其他标准兼容和扩展性没问题

    使用插件编译jsx代码,测试:

    新建文件夹 jsx-test

    mkdir jsx-test

    进入文件夹

    cd jsx-test

    初始化:

    npm init

    新建文件 demo.jsx

    touch demo.jsx

    全局安装babel

    sudo npm i babel -g

    安装依赖插件

    npm install --sav-dev babel-plugin-transform-react-jsx

    创建.babelrc文件,配置:

    {
        "plugins":["transform-react-jsx"]
    }

    babel编译命令:

    babel --plugins transform-react-jsx demo.jsx

    demo.jsx代码如下:

    class Input extends Component {
        constructor(props) {
            super(props)
            this.state = {
                title:''
            }
        }
        render(){
            return (
                <div>
                    <input value={this.state.title} onChange={this.changeHandle.bind(this)} />
                    <button onClick={this.clickHandle.bind(this)}>submit</button>
                </div>
            )
        }
        changeHandle(event){
            this.setState({
                title:event.target.value
            })
        }
    }

    编译结果:

    class Input extends Component {
        constructor(props) {
            super(props);
            this.state = {
                title: ''
            };
        }
        render() {
            return React.createElement(
                'div',
                null,
                React.createElement('input', { value: this.state.title, onChange: this.changeHandle.bind(this) }),
                React.createElement(
                    'button',
                    { onClick: this.clickHandle.bind(this) },
                    'submit'
                )
            );
        }
        changeHandle(event) {
            this.setState({
                title: event.target.value
            });
        }
    }

    尝试把 React.createElement 改成 h   在文件顶部加入

    /* @jsx h */

    运行编译如下:

    class Input extends Component {
        constructor(props) {
            super(props);
            this.state = {
                title: ''
            };
        }
        render() {
            return h(
                'div',
                null,
                h('input', { value: this.state.title, onChange: this.changeHandle.bind(this) }),
                h(
                    'button',
                    { onClick: this.clickHandle.bind(this) },
                    'submit'
                )
            );
        }
        changeHandle(event) {
            this.setState({
                title: event.target.value
            });
        }
    }

    总结:

    js语法(标签,js表达式,判断,循环,事件绑定)

    JSX其实是语法糖(微创新),需被解析成JS才能运行,React.createElement 

    已经成为独立的标准

    三,JSX 和 vdom 关系

    1,分析:为何需要vdom

    vdom是 React 初次推广开来的,结合 JSX

    JSX 就是模版,最终要渲染成 html

    初次渲染 + 修改 state 后的 re-render

    正好符合 vdom 的应用场景

    2,React.createElement 和 h(本质一样)

    所有的JSX都要解析成JS,执行最终返回vnode

    3,何时 patch ?

    (1),初次渲染 ReactDOM.render(<App />, container),会触发 patch(container,vnode)

    例:

    ReactDOM.render(<App />, document.getElementById('root'));

    (2),re-render - setState,会触发 patch(vnode,newVnode)

            // patch(vnode,newVnode)
            this.setState({
                list:currentList.concat(title)
            })

    4,自定义组件的解析

    'div',直接渲染<div>即可,vdom可以做到

    Input和List等自定义组件(class),vdom默认不认识

    因此Input和List定义的时候必须声明render函数

    根据props初始化实例,然后执行实例的render函数

    render函数返回的还是vnode对象

            var list = new List({ data: this.state.list });
            var vnode = list.render();

    例(demo.jsx)

    import List from './list/index.js';
    import Input from './input/index.js';
    
    function render() {
            return (
                // 视图
                <div>
                    <p> this is demo </p>
                    <Input addTitle={this.addTitle.bind(this)}/>
                    <List data={this.state.list} />
                    <List data={[1,2,3]} />
                </div>
            )
        } 

    解析如下:

    import List from './list/index.js';
    import Input from './input/index.js';
    
    function render() {
        return (
            // 视图
            React.createElement(
                'div',
                null,
                React.createElement(
                    'p',
                    null,
                    ' this is demo '
                ),
                React.createElement(Input, { addTitle: this.addTitle.bind(this) }),
                React.createElement(List, { data: this.state.list }),
                React.createElement(List, { data: [1, 2, 3] })
            )
        );
    }

    总结:

    为何需要vdom:JSX需要渲染成html,数据驱动视图

    React.createElement 和 h,都生成vnode (React.createElement 既可以创建 html 默认的标签-字符串形式,可以创建自定义组件名称)

    何时patch:ReactDOM.render(...) 和 setState()

    自定义组件的解析:初始化实例,然后执行render

    四,说下 setState 的过程 ( setState 核心函数)

    1,setState 的异步

       addTitle(title) {
            const currentList = this.state.list;
            // patch(vnode,newVnode)
            console.log(this.state.list); //[1,2]
            this.setState({
                list: currentList.concat(title) //3  
            })
            console.log(this.state.list); //[1,2]  因为上面的setState是异步操作,所以这里仍旧是[1,2]
        }

    原因:

    01,可能会一次执行多次 setState

    02,你无法规定、限制用户如何使用 setState

    03,没必要每次 setState 都重新渲染,考虑性能

    04,即便是每次重新渲染,用户也看不到中间的效果(js执行和DOM渲染是单线程的)

    05,只看到最后的结果即可

    addTitle(title) {
            const currentList = this.state.list;
            // 初次想增加 title        
            this.setState({
                list: currentList.concat(title) 
            })
    
            // 改变注意,想增加 title + 1        
            this.setState({
                list: currentList.concat(title + 1) 
            })
    
            // 又改变注意,想增加 title + 2        
            this.setState({
                list: currentList.concat(title + 2) 
            })
        }

    用户操作后,只会看到最后的结果,即  list: currentList.concat(title + 2)

    2,vue 修改属性也是异步

    01,效果、原因和 setState 一样

    02,对比记忆,印象深刻

    03,复习下vue 的实现流程

    第一步:解析模版成render函数

    第二步:响应式开始监听

    第三步:首次渲染,显示页面,且绑定依赖

    第四步:data 属性变化,触发 rerender ( set中执行updateComponent 是异步的)

    3,setState 的过程

    01,每个组件实例,都有 renderComponent 方法 (在Component 组件中定义的)

    02,执行 renderComponent 会重新执行实例的 render )

    03,render 函数返回 newVnode,然后拿到 preVnode (上次执行的 newVnode)

    04,执行 patch(preVnode,newVnode)

    addTitle(title) {
            const currentList = this.state.list;
            // patch(vnode,newVnode)
            console.log(this.state.list); //[1,2]
            this.setState({
                list: currentList.concat(title) //3  
            },() => {
                console.log(this.state.list); //这里能拿到实时结果 [1,2,3]
                // this.renderComponent()
            })
            console.log(this.state.list); //[1,2]  因为上面的setState是异步操作,所以这里仍旧是[1,2]
        }

    模拟 Component

    class Component {
        constructor(props) {
    
        }
        renderComponent() {
            const preVnode = this._vnode;
            const newVnode = this.render();
            patch(preVnode,newVnode)
            this._vnode = newVnode;
        
        }
    }

    五,对 React 和 vue 的认识

    国内使用,首推vue,文档更易读、易学,社区够大

    如果团队水平较高,推荐使用React,组件化和JSX做的更好

    1,两者的本质区别

    vue-本质是MVVM框架,由MVC发展而来

    React-本质是前端组件化框架,由后端组件化发展而来

    但这并不妨碍它们两者都能实现相同的功能

    2,模版的区别:

    vue-使用模版(最初由 angular 提出)

    React-使用JSX

    模版语法上,更倾向于JSX

    模版分离上,更倾向于Vue

    3,组件化的区别:

    React本身就是组件化,做的更彻底

    vue也支持组件化,不过是在MVVM上的扩展

    4,两者共同点

    都支持组件化

    都是数据驱动视图

    六,总结:

    组件化的理解:

    1,组件的封装:封装视图,数据,变化逻辑

    2,组件的复用:props传递,复用

    JSX本质:

    1,语法

    2,语法糖,需被解析成JS才能运行

    3,JSX是独立的标准,可被其他项目使用

    JSX和vdom的关系

    1,为何需要vdom:JSX需要渲染成html,还有rerender

    2,React.createElement 和 h,都生成vnode

    3,何时patch:ReactDOM.render() 和 setState

    4,自定义组件的解析:初始化实例,然后执行render

    setState过程:

    异步:效果、原因 (vue修改属性也是异步,效果、原因)

    setState的过程:最终走到 patch(preVnode,newVnode)

    附:用 React 实现 todo-list

    创建 React 开发环境

    全局安装

    sudo npm i create-react-app -g --registry=https://registry.npm.taobao.org

    运行

    create-react-app react-rest

    成功之后:

    Inside that directory, you can run several commands:
    
      yarn start
        Starts the development server.
    
      yarn build
        Bundles the app into static files for production.
    
      yarn test
        Starts the test runner.
    
      yarn eject
        Removes this tool and copies build dependencies, configuration files
        and scripts into the app directory. If you do this, you can’t go back!
    
    We suggest that you begin by typing:
    
      cd react-rest
      yarn start
    
    Happy hacking!

    进入文件夹

    cd react-rest

    运行

    yarn start

    实现todo组件

    import React, {
        Component
    } from 'react';
    import List from './list/index.js';
    import Input from './input/index.js';
    
    class Todo extends Component {
        constructor(props) {
            super(props)
            this.state = {//保存当前组件的变量
                list:[]
            }
        }
        render() {
            return (
                <div>
                    <Input addTitle={this.addTitle.bind(this)}/>
                    <List data={this.state.list} />
                </div>
            )
        }
        addTitle(title) {
            const currentList = this.state.list;
            this.setState({
                list:currentList.concat(title)
            })
        }
    }
    
    export default Todo

     todo中使用的Input组件:

    import React, {
        Component
    } from 'react';
    
    class Input extends Component {
        constructor(props) {
            super(props)
            this.state = {
                title:''
            }
        }
        render(){
            return (
                <div>
                    <input value={this.state.title} onChange={this.changeHandle.bind(this)} />
                    <button onClick={this.clickHandle.bind(this)}>submit</button>
                </div>
            )
        }
        changeHandle(event){
            this.setState({
                title:event.target.value
            })
        }
        clickHandle(){
            const title = this.state.title;
            // 把title添加进列表
            const addTitle = this.props.addTitle;
            addTitle(title);//重点
            this.setState({
                title:''
            })
    
        }
    }
    
    export default Input

     todo中使用的List组件

    import React, {
        Component
    } from 'react';
    
    class List extends Component {
        constructor(props) {
            super(props)
        }
        render(){
            const list = this.props.data;//list组建期望别人传入的列表
            return (
                <ul>
                    {
                        list.map((item,index) => {
                            return <li key={index}>{item}</li>
                        })
                    }
                </ul>
            )
        }
    }
    
    export default List

     最后,修改App.js

    import React, {
      Component
    } from 'react';
    import logo from './logo.svg';
    import './App.css';
    import Todo from './components/todo/index.js'
    
    class App extends Component {
      render() {
        return (
          <div>
            <Todo />
          </div>
        );
      }
    }
    
    export default App;

    附:

    react 父子组件互相通信

    1,父组件向子组件传递 在引用子组件的时候传递,相当于一个属性

                    <Input addTitle={this.addTitle.bind(this)} />
                    <List data={this.state.list} />

    2,子组件向父组件传递 子组件通过 调用父组件传递到子组件的方法 向父组件传递消息的。

    class Input extends Component {
        constructor(props) {
            super(props)
            this.state = {
                title:''
            }
            // console.log(this.props)
        }
        render(){
            return (
                <div>
                    <input value={this.state.title} onChange={this.changeHandle.bind(this)} />
                    <button onClick={this.clickHandle.bind(this)}>submit</button>
                </div>
            )
        }
        changeHandle(event){
            this.setState({
                title:event.target.value
            })
        }
        clickHandle(){
            const title = this.state.title;
            // 把title添加进列表
            const addTitle = this.props.addTitle;
            addTitle(title);//重点 调用父组件方法
            this.setState({
                title:''
            })
    
        }
    }

    附:

    React:Rethinking Web App Development at Facebook  只负责view层,不是完整的MVVM框架

    React 特点:轻,组件化开发,高度可重用

    React应用场景:

    复杂场景下的高性能

    重用组件库,组件组合

    React组件的生命周期和事件处理

    React Components Lifecycle

    Mounted: React Components 被render解析,生成对应的DOM节点,并被插入浏览器的DOM结构的一个过程( React.renderComponent() )

    Update:一个 mounted 的 React Components 被重新render 的过程 (setState() 或者 setProps()  ----> render)

    Unmounted:一个mounted的 React Components 对应的 DOM 节点被从DOM结构中移除的过程

    每一个状态 React 都封装了对应的 hook 函数

    未完待续...

  • 相关阅读:
    LAMP LNMP 和 LNMPA
    nginx版本如何选择?
    如何看apache的版本号
    CLR Via CSharp读书笔记(12):泛型
    要先怀疑外部代码的错误,再检测是不是自己代码的问题
    redis的那些事
    An Illustrated Guide to SSH Agent Forwarding
    Using sshagent with ssh
    dtach
    [原创]bind DNS IP列表的精确获取
  • 原文地址:https://www.cnblogs.com/iceflorence/p/8953892.html
Copyright © 2011-2022 走看看