React: 有状态组件生成真实DOM结点
上次我们分析了无状态组件生成 DOM 的过程,无状态组件其实就是纯函数,它不维护内部的状态,只是根据外部输入,输出一份视图数据。而今天我们介绍的有状态组件,它有内部的状态,因此在组件的内部,可以自行对状态进行更改,进而渲染出新的视图。下面我们就来分析有状态组件生成真实 DOM 结点的过程。
我们先来写的一个 Greeting 组件,每次点击问候按钮,文字部分会更新问候的次数:
class Greeting extends React.Component {
constructor() {
super();
this.state = {
count: 0,
};
}
componentDidMount() {
console.log('did mount');
}
greet = () => {
let {count} = this.state;
this.setState({
count: ++count,
});
};
render() {
let {name} = this.props;
return (
<div className="container">
<div>hello {name} {this.state.count} times</div>
<button onClick={this.greet}>greet</button>
</div>
)
}
}
const App = <Greeting name="scott"/>;
console.log(App);
ReactDOM.render(App, document.getElementById('root'));
编译之后的代码如下:
// 自执行函数变量 _createClass实际上是用来定义props的
var _createClass = function () {
// 定义属性 props是数组类型 [{key, val}]
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) {
descriptor.writable = true;
}
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
// 定义原型props
if (protoProps) {
defineProperties(Constructor.prototype, protoProps);
}
// 定义静态props
if (staticProps) {
defineProperties(Constructor, staticProps);
}
return Constructor;
};
}();
function _possibleConstructorReturn(self, call) {
return call && (typeof call === "object" || typeof call === "function") ? call : self;
}
// 继承
function _inherits(subClass, superClass) {
// 使用Object.create(prototype, {constructor})来实现继承
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) {
Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
}
// 自执行函数变量 表示用户自定义组件
var Greeting = function (_React$Component) {
// 自定义Greeting组件
function Greeting() {
var _this = _possibleConstructorReturn(this, (Greeting.__proto__ || Object.getPrototypeOf(Greeting)).call(this));
// 组件内部state
_this.state = {
count: 1
};
// 组件内部greet方法
_this.greet = function () {
var count = _this.state.count;
_this.setState({
count: ++count
});
};
return _this;
}
// 继承ReactComponent
_inherits(Greeting, _React$Component);
// 给Greeting定义生命周期方法
_createClass(Greeting, [
{
key: "componentDidMount",
value: function componentDidMount() {
console.log('did mount');
}
},
{
key: "render",
value: function render() {
var name = this.props.name;
return React.createElement(
"div",
{ className: "container" },
React.createElement(
"div",
null,
"hello ",
name,
" ",
this.state.count,
" times"
),
React.createElement(
"button",
{ onClick: this.greet },
"greet"
)
);
}
}
]);
return Greeting;
}(React.Component);
var App = React.createElement(Greeting, { name: "scott" });
console.log(App);
ReactDOM.render(App, document.getElementById('root'));
模拟组件渲染:
const React = {
// 创建DOM描述对象 即虚拟DOM
createElement(type, props, ...children) {
let propsChildren = children;
// 组件参数的props.children本身是数组
// 所以调用组件函数时这里需要特殊处理
if (Array.isArray(children[0])) {
propsChildren = children[0];
}
// 结点
let vnode = {
type,
props: {
...props,
children: propsChildren,
}
};
// 挂载组件函数体的虚拟DOM
if (typeof type === 'function') {
let componentProps = {
...props,
children,
};
// 有状态组件
if (type.prototype && type.prototype.render) {
let component = new type();
component.props = componentProps;
component.vnode = vnode;
vnode.body = component.render();
}
// 无状态组件
else {
vnode.body = type(componentProps);
}
}
return vnode;
}
};
// ReactComponent基类
function ReactComponent(props) {}
// 实现setState方法
ReactComponent.prototype.setState = function (partialSate) {
Object.assign(this.state, partialSate);
let oldDom = this.vnode.dom;
let newDom = ReactDOM.generateDOM(this.render());
this.vnode.dom = newDom;
// 替换DOM结点
oldDom.parentNode.replaceChild(newDom, oldDom);
}
// 模拟React.Component基类
React.Component = ReactComponent;
const ReactDOM = {
// 渲染真实DOM
render(vnode, container) {
container.appendChild(this.generateDOM(vnode));
},
// 获取真实DOM结点
generateDOM(vnode) {
if (typeof vnode.type === 'function') {
// 将组件函数体的虚拟DOM生成真实DOM
let elem = this.generateDOM(vnode.body);
vnode.dom = elem;
return elem;
}
let elem = document.createElement(vnode.type);
vnode.dom = elem;
// 特殊key值映射
let specialKeyMap = {
className: 'class',
fontSize: 'font-size',
};
let {props} = vnode;
// 设置DOM属性
props && Object.keys(props).forEach(key => {
if (key === 'children') {
// 处理子节点
props.children.forEach(child => {
if (['string', 'number'].includes(typeof child)) {
// 纯内容节点
elem.appendChild(document.createTextNode(child));
} else {
// DOM节点
elem.appendChild(this.generateDOM(child));
}
});
} else if (key === 'style') {
// 设置样式属性
let styleObj = props.style;
let styleItems = [];
Object.keys(styleObj).forEach(styleKey => {
styleItems.push(`${specialKeyMap[styleKey] || styleKey}:${styleObj[styleKey]}`);
});
elem.setAttribute('style', styleItems.join(';'));
} else if (['onClick'].includes(key)) {
let eventName = key.replace(/^on/, '').toLowerCase();
// 绑定事件
elem.addEventListener(eventName, function () {
props[key]();
});
} else {
// 设置其他属性
elem.setAttribute(specialKeyMap[key] || key, props[key]);
}
});
return elem;
}
};
React: 有状态组件生成真实DOM结点的更多相关文章
- React: 无状态组件生成真实DOM结点
在上一篇文章中,我们总结并模拟了 JSX 生成真实 DOM 结点的过程,今天接着来介绍一下无状态组件的生成过程. 先以下面一段简单的代码举例: const Greeting = function ({ ...
- React系列文章:无状态组件生成真实DOM结点
在上一篇文章中,我们总结并模拟了JSX生成真实DOM结点的过程,今天接着来介绍一下无状态组件的生成过程. 先以下面一段简单的代码举例: const Greeting = function ({name ...
- React系列文章:JSX生成真实DOM结点
在上一篇文章中,我们介绍了Babel是如何将JSX代码编译成可执行代码的,随后也实现了一个自己的解析器,模拟了Babel编译的过程. 现在我们再来回顾一下,假定有如下业务代码: const style ...
- React: JSX生成真实DOM结点
在上一篇文章中,我们介绍了 Babel 是如何将 JSX 代码编译成可执行代码的,随后也实现了一个自己的解析器,模拟了 Babel 编译的过程. 现在我们再来回顾一下,假定有如下业务代码: const ...
- react篇章-React State(状态)-组件都是真正隔离的
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title&g ...
- [react] 什么是虚拟dom?虚拟dom比操作原生dom要快吗?虚拟dom是如何转变成真实dom并渲染到页面的?
壹 ❀ 引 虚拟DOM(Virtual DOM)在前端领域也算是老生常谈的话题了,若你了解过vue或者react一定避不开这个话题,因此虚拟DOM也算是面试中常问的一个点,那么通过本文,你将了解到如下 ...
- React之父子组件传递和其它一些要点
react是R系技术栈中最基础同时也是最核心的一环,2年不到获取了62.5k star(截止到目前),足可见其给力程度.下面对一些react日常开发中的注意事项进行罗列. React的组件生命周期 r ...
- Vue视图渲染原理解析,从构建VNode到生成真实节点树
前言 在 Vue 核心中除了响应式原理外,视图渲染也是重中之重.我们都知道每次更新数据,都会走视图渲染的逻辑,而这当中牵扯的逻辑也是十分繁琐. 本文主要解析的是初始化视图渲染流程,你将会了解到从挂载组 ...
- 从DOM操作看Vue&React的前端组件化,顺带补齐React的demo
前言 接上文:谈谈我对前端组件化中“组件”的理解,顺带写个Vue与React的demo 上次写完博客后,有朋友反应第一内容有点深,看着迷迷糊糊:第二是感觉没什么使用场景,太过业务化,还不如直接写Vue ...
随机推荐
- [RN] React Native 使用 react-native-camera 过程中报错 Found react-native-camera 'mlkit' but wasn't required.`
详细报错如下: Could not resolve all task dependencies for configuration ':app:debugRuntimeClasspath'. Coul ...
- 谈谈你对arguments的了解
arguments:函数的实参副本,它是一个类数组. 使用场景:只能在函数内调用,严格模式下不能用. 函数的形参个数:fn.length 函数的实参个数:arguments.length argume ...
- 动态规划大合集II
1.前言 大合集总共14道题,出自江哥之手(这就没什么好戏了),做得让人花枝乱颤.虽说大部分是NOIP难度,也有简单的几道题目,但是还是做的很辛苦,有几道题几乎没思路,下面一道道边看边分析一下. 2. ...
- Http状态码梳理汇总
常见的状态代码为:200 - 服务器成功返回网页404 - 请求的网页不存在503 - 服务器暂时不可用 1xx(临时响应) 用于表示临时响应并需要请求者执行操作才能继续的状态代码.代码 说明 100 ...
- es6引用模块import后面加上花括号{}和不加花括号的区别
在使用import语法引用模块时,如何正确使用{} 例如:有两个文件,home.js.user.js 当需要在home.js中引入user.js的时候 //home.js import user fr ...
- concurrent(六)同步辅助器CyclicBarrier & 源码分析
参考文档:Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例:https://www.cnblogs.com/skywang12345/p/3533995.html简介Cy ...
- python3.5以上版本,typing模块提高代码健壮性
一 前言 Python是一门弱类型的语言,很多时候我们可能不清楚函数参数类型或者返回值类型,很有可能导致一些类型没有指定方法,在写完代码一段时间后回过头看代码,很可能忘记了自己写的函数需要传什么参数, ...
- 框架Ray
高性能最终一致性框架Ray之基本概念原理 一.Actor介绍 Actor是一种并发模型,是共享内存并发模型的替代方案. 共享内存模型的缺点: 共享内存模型使用各种各样的锁来解决状态竞争问题,性能低下且 ...
- Spring+Spring+Hibernate环境搭建
源码地址:https://gitee.com/kszsa/ssht.git 一.引入lib包 pom.xml,引入需要的jar包 <?xml version="1.0" en ...
- 网络基础 ----------- osi 与 一些协议
1.了解 OSI ISO IOS ISO(全称:International Organization for Standardization) 国际标准化组织, 成立于1947年2月23日,制定全世界 ...