本质上来说JSX是React.createElement(component, props, ...children)方法的语法糖。

所以我们如果使用了JSX,我们其实就是在使用React,所以我们就需要引入React

前言

React是前端最受欢迎的框架之一,解读其源码的文章非常多,但是我想从另一个角度去解读React:从零开始实现一个React,从API层面实现React的大部分功能,在这个过程中去探索为什么有虚拟DOM、diff、为什么setState这样设计等问题。

提起React,总是免不了和Vue做一番对比

Vue的API设计非常简洁,但是其实现方式却让人感觉是“魔法”,开发者虽然能马上上手,但其原理却很难说清楚。

相比之下React的设计哲学非常简单,虽然有很多需要自己处理的细节问题,但它没有引入任何新的概念,相对更加的干净和简单。

关于jsx

在开始之前,我们有必要搞清楚一些概念。

我们来看一下这样一段代码:

const title = <h1 className="title">Hello, world!</h1>;

这段代码并不是合法的js代码,它是一种被称为jsx的语法扩展,通过它我们就可以很方便的在js代码中书写html片段。

本质上,jsx是语法糖,上面这段代码会被babel转换成如下代码:

const title = React.createElement(
'h1',
{ className: 'title' },
'Hello, world!'
);
  

React.createElement和虚拟DOM

前文提到,jsx片段会被转译成用React.createElement方法包裹的代码。所以第一步,我们来实现这个React.createElement方法

从jsx转译结果来看,createElement方法的参数是这样:

createElement( tag, attrs, child1, child2, child3 );

第一个参数是DOM节点的标签名,它的值可能是div,h1,span等等

第二个参数是一个对象,里面包含了所有的属性,可能包含了className,id等等

从第三个参数开始,就是它的子节点

我们对createElement的实现非常简单,只需要返回一个对象来保存它的信息就行了。

function createElement( tag, attrs, ...children ) {
return {
tag,
attrs,
children
}
}
 

函数的参数 ...children使用了ES6的rest参数,它的作用是将后面child1,child2等参数合并成一个数组children。

现在我们来试试调用它

// 将上文定义的createElement方法放到对象React中
const React = {
createElement
} const element = (
<div>
hello<span>world!</span>
</div>
);
console.log( element );
  打开调试工具,我们可以看到输出的对象和我们预想的一致

我们的createElement方法返回的对象记录了这个DOM节点所有的信息,换言之,通过它我们就可以生成真正的DOM,这个记录信息的对象我们称之为虚拟DOM。

ReactDOM.render
接下来是ReactDOM.render方法,我们再来看这段代码
ReactDOM.render(
<h1>Hello, world!</h1>
document.getElementById('root')
);

  以上经过转换,这段代码变成了这样

ReactDOM.render(
React.createElement( 'h1', null, 'Hello, world!' ),
document.getElementById('root')
)
```  所以render的第一个参数实际上接受的是createElement返回的对象,也就是虚拟DOM
而第二个参数则是挂载的目标DOM 总而言之,render方法的作用就是将虚拟DOM渲染成真实的DOM,下面是它的实现:
```function setAttribute( dom, name, value ) {
// 如果属性名是className,则改回class
if ( name === 'className' ) name = 'class'; // 如果属性名是onXXX,则是一个事件监听方法
if ( /on\w+/.test( name ) ) {
name = name.toLowerCase();
dom[ name ] = value || '';
// 如果属性名是style,则更新style对象
} else if ( name === 'style' ) {
if ( !value || typeof value === 'string' ) {
dom.style.cssText = value || '';
} else if ( value && typeof value === 'object' ) {
for ( let name in value ) {
// 可以通过style={ width: 20 }这种形式来设置样式,可以省略掉单位px
dom.style[ name ] = typeof value[ name ] === 'number' ? value[ name ] + 'px' : value[ name ];
}
}
// 普通属性则直接更新属性
} else {
if ( name in dom ) {
dom[ name ] = value || '';
}
if ( value ) {
dom.setAttribute( name, value );
} else {
dom.removeAttribute( name );
}
}
}
  这里其实还有个小问题:当多次调用render函数时,不会清除原来的内容。所以我们将其附加到ReactDOM对象上时,先清除一下挂载目标DOM的内容:
const ReactDOM = {
render: ( vnode, container ) => {
container.innerHTML = '';
return render( vnode, container );
}
}

  

渲染和更新

到这里我们已经实现了React最为基础的功能,可以用它来做一些事了。

我们先在index.html中添加一个根节点

  我们先来试试官方文档中的Hello,World
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('root')
);``` 运行以后 效果基本就出来了

React 引入import React 原理的更多相关文章

  1. 为什么import React from 'react',React首字母必须大写?

    很奇怪的是,明明没有用到 React,但是我不得不 import React.这是为什么? import React from 'react'; export default function (pr ...

  2. react实战系列 —— React 中的表单和路由的原理

    其他章节请看: react实战 系列 React 中的表单和路由的原理 React 中的表单是否简单好用,受控组件和非受控是指什么? React 中的路由原理是什么,如何更好的理解 React 应用的 ...

  3. 10分钟了解 react 引入的 Hooks

    "大家好,我是谷阿莫,今天要将的是一个...",哈哈哈,看到这个题我就想到这个开头.最近react 官方在 2018 ReactConf 大会上宣布 React v16.7.0-a ...

  4. 深入理解React:事件机制原理

    目录 序言 DOM事件流 事件捕获阶段.处于目标阶段.事件冒泡阶段 addEventListener 方法 React 事件概述 事件注册 document 上注册 回调函数存储 事件分发 小结 参考 ...

  5. React Native 入门到原理(详解)

    抛砖引玉(帮你更好的去理解怎么产生的 能做什么) 砖一.动态配置 由于 AppStore 审核周期的限制,如何动态的更改 app 成为了永恒的话题.无论采用何种方式,我们的流程总是可以归结为以下三部曲 ...

  6. React动态import()

    React动态import() react-router@v4代码分离,推荐的import().这里分享webpack配置和使用方法. 首先安装两个必须的包 cnpm i react-loadable ...

  7. [React] Create and import React components with Markdown using MDXC

    In this lesson I demonstrate how to use the library MDXC to create and import React components with ...

  8. React引入,运行

    1.引入 <script src="https://cdn.bootcss.com/react/15.5.4/react.min.js"></script> ...

  9. react引入ggEditor流程图

    遇到的问题 1.propsAPI获取不到内容:withPropsAPI包裹的组件必须是GGEditor的子组件. 2.自定义组件如何使用:正确的办法是通过config配置,参照上面的代码(之前我在在内 ...

随机推荐

  1. vue nexttick的理解和使用场景

    应用场景 需要在视图更新之后,基于新的视图进行操作 文档说明 在下次 DOM 更新循环结束之后执行延迟回调.在修改数据之后立即使用这个方法,获取更新后的 DOM nextTick原理 1.异步说明 V ...

  2. go 学习笔记---chan

    如果说 goroutine 是 Go语言程序的并发体的话,那么 channels 就是它们之间的通信机制.一个 channels 是一个通信机制,它可以让一个 goroutine 通过它给另一个 go ...

  3. 什么是k8s

    •Kubernetes介绍 1.背景介绍 云计算飞速发展 - IaaS - PaaS - SaaS Docker技术突飞猛进 - 一次构建,到处运行 - 容器的快速轻量 - 完整的生态环境 2.什么是 ...

  4. jq动画插件,自制基于vue的圆形时钟

    首先附上jq插件库,里面的东西太炫了,建议学前端的可以看看学习下:http://www.jq22.com/ 里面有个“超个性动画版本的个人简历”,通过屏幕上不断打印内容,改变相应样式来实现动画简历,我 ...

  5. ffmpeg命令参数详解

    ffmpeg命令参数详解 http://linux.51yip.com/search/ffmpeg ffmpeg图片加滤镜效果 参考:https://cloud.tencent.com/develop ...

  6. Windows 搭建 nginx rtmp服务器

    1.环境开发环境:windows开发工具:ffmpeg.nginx.nginx-rmtp-module.vlc media player播放器 2.准备文件官方ffmpeg下载地址:http://ww ...

  7. pytest之mark功能

    pytest系列(一)中给大家介绍了pytest的特性,以及它的编写用例的简单至极. 那么在实际工作当中呢,我们要写的自动化用例会比较多,不会都放在一个py文件里. 如下图所示,我们编写的用例存放在不 ...

  8. 第一阶段:Java基础 1.JAVA开发介绍---6. Java基本数据类型

    Java 的两大数据类型: 内置数据类型(基本数据类型) 引用数据类型 本数据类型: Java语言提供了八种基本类型.六种数字类型,一种字符类型,还有一种布尔型. byte,short,int,lon ...

  9. Java自学-操作符 逻辑操作符

    Java的逻辑运算符 逻辑运算符 示例 1 : 长路与 和 短路与 无论长路与还是短路与 两边的运算单元都是布尔值 都为真时,才为真 任意为假,就为假 区别: 长路与 两侧,都会被运算 短路与 只要第 ...

  10. Jest单元测试进阶

    Jest 命令行窗口中的指令 在学习Jest单元测试入门的时候,给Jest命令提供了一个参数 --watchAll, 让它监听测试文件或测试文件引入的文件的变化,从而时时进行测试.但这样做也带来一个问 ...