组件化和 React

一,对组件化的理解css

1,组件的封装html

-视图前端

-数据vue

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

例:react

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,组件的复用npm

-props传递后端

-复用浏览器

例:babel

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 函数

未完待续...

相关文章
相关标签/搜索