React是facebook开源的JS库,它可以把界面抽象成一个一个的组件,组件进行组合来得到功能丰富的页面。与Vue不同,React立足于MVC框架,是一个包括view和controller的库。首先我们来看一下什么是MVC。

  MVC(Model View Controller):M指的是业务模型,它有最多的处理任务,被模型返回的数据是中立的,模型与数据格式无关,这样一个模型可以为多个视图提供数据,由于应用于模型的代码只用写一次就可被多个视图进行重用,这样减少了代码的重复性;V指的是用户界面,如网页界面或者是软件的客户端界面;C是控制器,它可以接受用户输入并且调用模型和视图去完成用户的需求,它本身并不输入任何东西和做任务处理,它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。概括来说,用户的请求首先会到达Controller,由Controller从Model上获取数据,进而选择合适的View,把处理的结果呈现在View上。React它是用来创建用户界面的JS库,它和用户界面打交道可以把它看成是MVC中的 V层。

  一、JSX

  JSX它是JavaScript的语法扩展,即我们在js中可以编写html代码。它可以将html结构,数据,甚至是样式都聚合在一起。

  1、使用html标签

  先声明后使用,react中的html标签和vue的不太一样,react是插入而vue是替换。如下所示:把名为myElement的多个标签插入到root根元素中。其中要注意的是:html里的class在jsx中要写成className,for写成htmlFor,不能与js中的关键字重复。当代码中嵌套多个html标签,需要用一个div或者是空标签进行包裹,否则它会报错。

import React from 'react';
import ReactDOM from 'react-dom';

  let myElement =
  <>
      <div className="myDivElement">my-div-element</div>
      <h3>JSX</h3>
  </>

ReactDOM.render(
  // 将元素渲染到DOM中
  myElement,
  document.getElementById('root')
);

  2、使用js表达式

  我们可以在JSX中使用js表达式,表达式要写在花括号{}中。需要注的有以下几点:

  JSX中的注释是像js中块注释一样{/*注释内容*/};

  JSX中可以使用任意的变量,当我们需要把数组进行循环一般使用map,reduce,filter等方法;

  结构中的判断不能用else...if而是要使用三元运算符或者是可以使用&&,||操作。

import React from 'react';
import ReactDOM from 'react-dom';
let num = 10;
let name = 'hello JSX';
let isLogin = true;
let a = undefined;
let nul = null;
let col = 'lightpink';
let ary = [14, 32, 42, 31, 85, 79];
let href = "https://baidu.com";
let obj = { a: 'React', b: 'Vue', c: 'Javascript' };
let sty = { color: 'lightpink', background: 'lightblue', fontSize: '20px' };
ReactDOM.render(
    <>
        {/* 注意特殊的注释方式 */}

        {/* 1.运算 */}
        <h2>{10 * 10}</h2>

        {/* 2.嵌入数据 */}
        <h3>数据是:{num}</h3>
        <h3>姓名是:{name}</h3>
        <h3>{isLogin ? 'hello React' : ''}</h3>
        {/* undefined和null不显示,如果非要显示则需要转成字符串 */}
        <h3>{a}</h3>
        <h3>{nul}</h3>
        <h3>{nul + ''}</h3>

        {/* 3.数组 */}
        {/* 3.1列表展示 */}
        <h3>数字列表</h3>
        <ul>{ary.map(item => <li key={item}>{item}</li>)}</ul>
        {/* 3.2过滤 */}
        <ul>{ary.filter(item => item >= 50).map(item => <li key={item}>{item}</li>)}</ul>
        {/* 3.2截取 */}
        <ul>{ary.slice(0, 3).map(item => <li key={item}>{item}</li>)}</ul>

        {/* 4.对象:在结构中不能直接放入对象 */}
        <li>我现在学的是:{obj.a}</li>

        {/* 5.判断不能用else...if可以使用三元 */}
        <h4>{1 > 0 ? '1>0' : '1<=0'}</h4>

        {/* 6.样式  style={{color:col}}这不是小胡子,这是花括号包了一个对象,style的值有两层花括号*/}
        <h5 style={{ color: col, backgroundColor: 'lightblue' }}>样式</h5>
        <a href={href} target="_blank">点击跳转</a>
        {/* 当我们需要设置多个属性,或者是有时不知道属性名称时,可以使用扩展属性。 */}
        <li style={{ ...sty }}>hello davina</li>
    </>,
    document.querySelector('#root')
)

  JSX其实是一种语法糖,它并不是直接渲染到页面上,而是内部通过babel先转义成createElement形式,再进行渲染。React引入JSX主要是为了方便view层面上的组件化,它可以构建html结构化页面。React将JSX映射为虚拟元素,并且用虚拟元素来管理整个的虚拟DOM。如果我们用JSX语法声明某个元素它会被转化成React.createElement(type,props,...children)。当然如果元素是组件也是不例外的。如下所示:

import React from 'react';
import ReactDOM from 'react-dom';
let name = 'davina';
/* 
ReactDOM.render(
  <h2 className="myh2" style={{ color: 'lightblue' }}>hello
  <span className="myspan">{name}</span>
  </h2>,
  document.querySelector('#root')
)
 */

// 它可以转化成如下:
ReactDOM.render(
  React.createElement("h2", {
  className: "myh2",
  style: {
    color: 'lightblue'
  }
}, "hello", 
  React.createElement("span", {
  className: "myspan"
}, name)) ,
  document.querySelector('#root')
)

  React元素它就是一个普通的js对象,它是构建应用的最小单位。当元素已经生成,它是不可变的,如果要进行更新它只会更新必要的部分。如下所示:

import React from 'react'
import ReactDom from 'react-dom'
setInterval(() => {
  let element = <div><span>当前时间:</span>{new Date().toLocaleTimeString()}</div>
  ReactDom.render(element, document.getElementById('root'))
}, 1000);

  二、实现虚拟DOM

  通过上面的应用我们可以看到React它是使用JSX编写虚拟的DOM对象,通过babel.js编译后生成真正的DOM,然后将真正的DOM插入到页面中。

//index.js
import React from './react';
import ReactDOM from './react-dom';

let element = React.createElement("h4", {
  className: "title",
  style: {
    color: 'lightpink',
    fontSize: '20px'
  }
}, 'hello', React.createElement("span", null, "davina"));

ReactDOM.render(element, document.getElementById('root'));

  在生成虚拟DOM时,使用createElement方法,用来模拟真实的DOM。根据真实的createElement我们可以看到它里面有三个参数:参数一:type类型,它有可能是DOM的元素标签,也有可能是组件名;参数二:props:属性,如下所示:className: "myh2",style: {color: 'lightblue'};参数三:chidlren子元素。

//react.js
//
创建一个createElement函数,并把type,props传给ReactElement函数 function createElement(type, config = {}, children) { //声明名字和props let propName; let props = {}; //把config上的所有属性都拷贝到props上,这样原来的就不发生改变 for (propName in config) { props[propName] = config[propName]; } //儿子的长度 const childrenLength = arguments.length - 2; //当只有一个儿子时 if (childrenLength == 1) { props.children = children; //当多个儿子时 } else if (childrenLength > 1) { //把类数组转成数组并进行截取,得到所有儿子 props.children = Array.from(arguments).slice(2); } return ReactElement(type, props); } //声明一个ReactElement函数,那createElement返回的就是这个element function ReactElement(type, props) { //声明一个type,props属性的元素并返回 const element = { type, props }; return Element; }
export default { createElement }

  这样一个createElement函数就创建成功,通过这个函数,我们可以把JSX格式的代码进行转化,创建一个虚拟的DOM。但单单这个方法是不足以达渲染要求,所以还需要render方法。它可以将虚拟的DOM解析成真实的DOM插入到父节点中最后并渲染到页面上

//react-dom.js
function render(element, parentNode) {
    // 如果element是字符串或者是数字那可以直接进行添加
    if (typeof element === 'string'||typeof element === 'number') {
        //创建一个文本节点然后添加到parentNode中
        return parentNode.appendChild(document.createTextNode(element));
    }
    //如果是element,element
    let type, props;
    type = element.type;
    props = element.props;
    //如果element是dom元素时,要处理特殊的属性,如className,fontSize......
    let domElement = document.createElement(type);//div,h1....
    for (let propName in props) {
        switch (propName) {
            //className要特殊处理
            case 'className':
                domElement.className = props[propName];
                break;
            //style要特殊处理
            case 'style':
                let styleObj = props[propName];  //styleObj => {color: 'lightpink',fontSize: '20px'}
                //用字符串拼接
                //['color','fontSize']=>['color:lightpink,'font-size:20px']=>'color:lightpink,'font-size:20px'
                let cssText = Object.keys(styleObj).map(attr => {
                    //propName=>${attr}     props[propName]=>${styleObj[attr]}
                    //处理驼峰转-
                    return `${attr.replace(/A-Z/g, function () { return "-" + arguments[1].toLowerCase() })}:${styleObj[attr]}`;
                }).join(';');
                domElement.style.cssText = 'color:lightpink;font-size:20px';
                break;
            //children要特殊处理
            case 'children':
                //转成数组
                let children = Array.isArray(props.children) ? props.children : [props.children];
                //进行循环,并且挂载在自己身上
                children.forEach(child => render(child, domElement));
                break;
            default:
                // element.setAttribute(propName, props[propName])=>div.setAttribute('id','box')
                //普通属性时直接进行添加,如id
                domElement.setAttribute(propName, props[propName]);
        }
        parentNode.appendChild(domElement);
    }
}

export default { render }

  但element不一定是元素或者是字符串,数字,还有可能是组件,所以我们还要考虑到element是组件的情况,组件又分为函数组件和类组件,这二者要分开考虑。

// index.js
// import ReactDOM from './react-dom'
// import React from './react'

//
函数组件 // function Welcome(props) { // return React.createElement('h4',{id:'welcome'},props.name,props.age) // } // let element = React.createElement(Welcome, { name: 'davina', age: 20 }); // ReactDOM.render(element, document.getElementById('root')); // 类组件 class Welcome1 extends React.Component{ render(){ return React.createElement('h4',{id:'class_welcome'},this.props.name,this.props.age) } } let element = React.createElement(Welcome1, { name: 'davina', age: 20 }); ReactDOM.render( element, document.getElementById('root'));

  当element为类组件时,因为类组件要继承React的Component所以:

//react.js
// Component
class Component{
    //加一个静态属性isReactComponent,后面的所有子类都是可以继承它的静态属性
    static isReactComponent = true;
    constructor(props){
        this.props = props;
    }
}
export default { Component}
//render函数内部
//如果是element,element
    let type, props;
    type = element.type;
    props = element.props;
    
    //判断是否是类组件
    if (type.isReactComponent) {
        // returnedElement接收返回值
        let returnedElement = new type(props).render();
        type = returnedElement.type;
        props = returnedElement.props;
        //判断是否是函数
    } else if (typeof type == 'function') {
        let returnedElement = type(props);
        type = returnedElement.type;
        props = returnedElement.props;
    }

   总结来说,jsx它仅仅是一个语法糖,并不是直接渲染到页面上,在react内部它要通过先转义成React.createElement(type,props,...children)形式再进行调用。利用React.createElement函数形成了一个js对象树,即虚拟DOM,形成虚拟DOM后,如果状态或者是数据发生改变,新旧虚拟DOM通过一系列复杂的算法进行比较,再重新渲染,形成真实DOM。即react的渲染是从jsx到虚拟DOM再到真实DOM的过程。

 

 

 

  

 

posted on 2020-09-05 12:00  人称小小贩  阅读(410)  评论(0编辑  收藏  举报