手写react核心原理,再也不怕面试官问我react原理

}

也就是说,这是所有的 种类都有的属性,所以我们可以这样实现 Unit

class Unit{

constructor(element){

this._currentElement = element

}

getMarkUp(){

throw Error(“此方法应该被重写,不能直接被使用”)

}

}

为什么getMarkUp 要throw Error("此方法应该被重写,不能直接被使用")呢?

学过 java或其他语言的同学应该秒懂,这是因为getMarkUp希望是被子类重写的方法,因为每个子类执行这个方法返回的结果是不一样的。

7. 实现TextUnit


到这一步,我们只要重写getMarkUp方法就好了,不过不要忘记,给每一个元素添加一个 reactid,至于为什么,已经在上面说过了,也放了一张大图了哈。

class TextUnit extends Unit{

getMarkUp(reactid){

this._reactid = reactid

return <span data-reactid=${reactid}>${this._currentElement}</span>

}

}

好了,到这里先看下完整的Unit.js长什么样子吧

// Unit.js

class Unit{

constructor(element){

this._currentElement = element

}

getMarkUp(){

throw Error(“此方法应该被重写,不能直接被使用”)

}

}

class TextUnit extends Unit{

getMarkUp(reactid){

this._reactid = reactid

return <span data-reactid=${reactid}>${this._currentElement}</span>

}

}

function createUnit(element){

if(typeof element === ‘string’ || typeof element === “number”){

return new TextUnit(element)

}

}

export {

createUnit

}

我们在index.js引入 unit测试下

// index.js

import React from ‘./react’;

import ReactDOM from ‘./react-dom’;

ReactDOM.render(

“sunny”,

document.getElementById(‘root’)

);

// react-dom.js

import {createUnit} from ‘./unit’

import $ from “jquery”

let ReactDOM = {

render,

rootIndex:0

}

function render(element,container){

let unit = createUnit(element)

let markUp = unit.getMarkUp(ReactDOM.rootIndex);// 用来返回HTML标记

$(container).html(markUp)

}

export default ReactDOM

在这里插入图片描述

意料之内的成功!哈哈哈啊

8. 理解React.creacteElement方法


在第一次学习react的时候,我总会带着许多疑问。比如看到下面的代码就会想:为什么我们只是引入了React,但是并没有明显的看到我们在其他地方用,这时我就会想着既然没有用到,那如果删除之后会不会受到影响呢?答案当然是不行的。

import React from ‘react’;

import ReactDOM from ‘react-dom’;

let element = (

hello

world

)

console.log({type: element.type, props:element.props})

ReactDOM.render(element,document.getElementById(‘root’));

当我们带着这个问题去研究的时候会发现其实在渲染element的时候调了React.createElement(),所以上面的问题就在这里找到了答案。

如下面代码所示,这就是从jsx语法到React.createElement的转化

hello

world

//上面的这段代码很简单,但是我们都知道react是所谓的虚拟dom,当然不可能就是我们看到的这样。当我们将上面的代码经过babel转译后,我们再看看

React.createElement(“h1”, {

id: “title”,

className: “bg”,

style: {

color: ‘red’

}

}, “hello”, React.createElement(“span”, null, “world”));

document有createElement()方法,React也有createElement()方法,下面就来介绍React的createElement()方法。

var reactElement = ReactElement.createElement(

… // 标签名称字符串/ReactClass,

… // [元素的属性值对对象],

… // [元素的子节点]

)

1、参数:

1)第一个参数:可以是一个html标签名称字符串,也可以是一个ReactClass(必须);

2)第二个参数:元素的属性值对对象(可选),这些属性可以通过this.props.*来调用;

3)第三个参数开始:元素的子节点(可选)。

2、返回值:

一个给定类型的ReactElement元素

我们可以改下我们的index.js

// index.js

import React from ‘./react’;

import ReactDOM from ‘./react-dom’;

var li1 = React.createElement(‘li’, {onClick:()=>{alert(“click”)}}, ‘First’);

var li2 = React.createElement(‘li’, {}, ‘Second’);

var li3 = React.createElement(‘li’, {}, ‘Third’);

var ul = React.createElement(‘ul’, {className: ‘list’}, li1, li2, li3);

console.log(ul);

ReactDOM.render(ul,document.getElementById(‘root’))

可以就看下 ul 最终的打印 期待结果

在这里插入图片描述

由此 ,我们只知道了,ReactElement.createElement方法将生产一个给定类型的ReactElement元素,然后这个对象被传入 render方法,然后进行了上面讲到的 createUnit和getMarkUp操作。

9. 实现React.createElement方法


经过上面的讲解,我们大概已经知道React.createElement方法的作用了,现在就来看看是怎么实现的

在这里插入图片描述

我们创建了一个新的文件element.js

// element.js

class Element {

constructor(type,props){

this.type = type

this.props = props

}

}

function createElement(type,props={},…children){

props.children = children || [];

return new Element(type,props)

}

export {

Element,

createElement

}

我们 定义了一个 Element 类 ,然后在createElement方法里创建了这个类的对象,

并且return出去了

没错,这个对象就是上面所说的给定类型的ReactElement元素,也就是下面这张图所显示的

在这里插入图片描述

我们应当是这样React.createElement()调用这个方法的,所以我们要把这个方法挂载到react身上。

我们前面还没有实现react.js

其实,很简单,就是返回一个React对象,这个对象有createElement方法

// react.js

import {createElement} from “./element”

const React = {

createElement

}

export default React

10. 实现NativeUnit


上面实现了 createElement返回 给定类型的ReactElement元素 后,就将改元素传入,render方法,因此 就会经过 createUnit方法, createUnit方法判断是属于什么类型的 元素,如下面代码

// Unit.js

import {Element} from “./element” // 新增代码

class Unit{

constructor(element){

this._currentElement = element

}

getMarkUp(){

throw Error(“此方法应该被重写,不能直接被使用”)

}

}

class TextUnit extends Unit{

getMarkUp(reactid){

this._reactid = reactid

return <span data-reactid=${reactid}>${this._currentElement}</span>

}

}

function createUnit(element){

if(typeof element === ‘string’ || typeof element === “number”){

return new TextUnit(element)

}

// 新增代码

if(element instanceof Element && typeof element.type === “string”){

return new NativeUnit(element)

}

}

export {

createUnit

}

好了,现在我们来实现NativeUnit类,其实主要就是实现NativeUnit的getMarkUp方法

class NativeUnit extends Unit{

getMarkUp(reactid){

this._reactid = reactid

let {type,props} = this._currentElement;

}

}

要明确的一点是,NativeUnit 的getMarkUp方法,是要把

在这里插入图片描述

这样一个element 对象转化为 真实的dom的

因此,我们可以这样完善getMarkUp方法

class NativeUnit extends Unit{

getMarkUp(reactid){

this._reactid = reactid

let {type,props} = this._currentElement;

let tagStart = <${type}

let childString = ‘’

let tagEnd = </${type}>

for(let propName in props){

if(/^on[A-Z]/.test(propName)){ // 添加绑定事件

}else if(propName === ‘style’){ // 如果是一个样式对象

}else if(propName === ‘className’){ // 如果是一个类名

}else if(propName === ‘children’){ // 如果是子元素

}else { // 其他 自定义的属性 例如 reactid

tagStart += (${propName}=${props[propName]})

}

}

return tagStart+‘>’ + childString +tagEnd

}

}

这只是 大体上的 一个实现 ,其实就是 把标签 和属性 以及 子元素 拼接成 字符串,然后返回出去。

我们测试下,现在有没有 把ul 渲染出来

// index.js

import React from ‘./react’;

import ReactDOM from ‘./react-dom’;

var li1 = React.createElement(‘li’, {}, ‘First’);

var li2 = React.createElement(‘li’, {}, ‘Second’);

var li3 = React.createElement(‘li’, {}, ‘Third’);

var ul = React.createElement(‘ul’, {className: ‘list’}, li1, li2, li3);

console.log(ul);

ReactDOM.render(ul,document.getElementById(‘root’))

在这里插入图片描述

发现确实成功渲染出来了,但是 属性和 子元素还没有,这是因为我们 还没实现 具体 的功能。

现在我们来实现事件绑定 功能

class NativeUnit extends Unit{

getMarkUp(reactid){

this._reactid = reactid

let {type,props} = this._currentElement;

let tagStart = <${type} data-reactid="${this._reactid}"

let childString = ‘’

let tagEnd = </${type}>

for(let propName in props){

// 新增代码

if(/^on[A-Z]/.test(propName)){ // 添加绑定事件

let eventName = propName.slice(2).toLowerCase(); // 获取click

( d o c u m e n t ) . d e l e g a t e ( ‘ [ d a t a − r e a c t i d = " (document).delegate(`[data-reactid=" (document).delegate([datareactid="{this._reactid}"], e v e n t N a m e . {eventName}. eventName.{this._reactid}`,props[propName])

}else if(propName === ‘style’){ // 如果是一个样式对象

}else if(propName === ‘className’){ // 如果是一个类名

}else if(propName === ‘children’){ // 如果是子元素

}else { // 其他 自定义的属性 例如 reactid

}

}

return tagStart+‘>’ + childString +tagEnd

}

}

在这里,我们是用了事件代理的模式,之所以用事件代理,是因为这些标签元素还没被渲染到页面上,但我们又必须提前绑定事件,所以需要用到事件代理

接下来,实现 样式对象的绑定

class NativeUnit extends Unit{

getMarkUp(reactid){

this._reactid = reactid

let {type,props} = this._currentElement;

let tagStart = <${type} data-reactid="${this._reactid}"

let childString = ‘’

let tagEnd = </${type}>

for(let propName in props){

if(/^on[A-Z]/.test(propName)){ // 添加绑定事件

}else if(propName === ‘style’){ // 如果是一个样式对象

let styleObj = props[propName]

let styles = Object.entries(styleObj).map(([attr, value]) => {

return ${attr.replace(/[A-Z]/g, m => -KaTeX parse error: Expected 'EOF', got '}' at position 20: …oLowerCase()}`)}̲:{value}`;

}).join(‘;’)

tagStart += (style="${styles}")

}else if(propName === ‘className’){ // 如果是一个类名

}else if(propName === ‘children’){ // 如果是子元素

}else { // 其他 自定义的属性 例如 reactid

}

}

return tagStart+‘>’ + childString +tagEnd

}

}

这里 其实就是把

{style:{backgroundColor:“red”}}

对象中的 style这个对象 属性拿出来,

然后把backgroundColor 通过正则 变化成background-color

然后再拼接到tagStart中。

接下来再实现className,发现这个也太简单了吧

class NativeUnit extends Unit {

getMarkUp(reactid) {

this._reactid = reactid

let { type, props } = this._currentElement;

let tagStart = <${type} data-reactid="${this._reactid}"

let childString = ‘’

let tagEnd = </${type}>

for (let propName in props) {

if (/^on[A-Z]/.test(propName)) { // 添加绑定事件

} else if (propName === ‘style’) { // 如果是一个样式对象

} else if (propName === ‘className’) { // 如果是一个类名

tagStart += ( class="${props[propName]}")

} else if (propName === ‘children’) { // 如果是子元素

} else { // 其他 自定义的属性 例如 reactid

}

}

return tagStart + ‘>’ + childString + tagEnd

}

}

为什么这么简单呢? 因为只需要把

className: ‘list’

中的className变化成 class就可以了。OMG!!

接下来,是时候实现子元素的拼接了哈

class NativeUnit extends Unit {

getMarkUp(reactid) {

this._reactid = reactid

let { type, props } = this._currentElement;

let tagStart = <${type} data-reactid="${this._reactid}"

let childString = ‘’

let tagEnd = </${type}>

for (let propName in props) {

if (/^on[A-Z]/.test(propName)) { // 添加绑定事件

} else if (propName === ‘style’) { // 如果是一个样式对象

} else if (propName === ‘className’) { // 如果是一个类名

} else if (propName === ‘children’) { // 如果是子元素

let children = props[propName];

children.forEach((child, index) => {

let childUnit = createUnit(child); // 可能是字符串 ,也可能是原生标签,也可能是自定义属性

let childMarkUp = childUnit.getMarkUp(${this._reactid}.${index})

childString += childMarkUp;

})

} else { // 其他 自定义的属性 例如 reactid

}

}

return tagStart + ‘>’ + childString + tagEnd

}

}

发现子元素 ,其实只要进行递归操作,也就是将子元素传进createUnit,把返回的childUnit 通过childMarkUp 方法变成 真实动,再拼接到childString 就好了。 其实想想也挺简单,就类似深拷贝的操作。

好了,接下来就是 其他属性了

class NativeUnit extends Unit {

getMarkUp(reactid) {

this._reactid = reactid

let { type, props } = this._currentElement;

let tagStart = <${type} data-reactid="${this._reactid}"

let childString = ‘’

let tagEnd = </${type}>

for (let propName in props) {

if (/^on[A-Z]/.test(propName)) { // 添加绑定事件

} else if (propName === ‘style’) { // 如果是一个样式对象

} else if (propName === ‘className’) { // 如果是一个类名

} else if (propName === ‘children’) { // 如果是子元素

} else { // 其他 自定义的属性 例如 reactid

tagStart += (${propName}=${props[propName]})

}

}

return tagStart + ‘>’ + childString + tagEnd

}

}

其他属性直接就拼上去就好了哈哈哈

好了。现在我们已经完成了NativeUini的getMarkUp方法。我们来测试一下是否成功了没有吧!

在这里插入图片描述

害,不出所料地成功了。

11. 完成React.Component


接下来我们看看自定义组件是怎么被渲染的,例如下面的Counter组件

// index.js

class Counter extends React.Component{

constructor(props){

super(props)

this.state = {number:0};

}

render(){

let p = React.createElement(‘p’,{style:{color:‘red’}},this.state.number);

let button = React.createElement(‘button’,{},“+”)

return React.createElement(‘div’,{id:‘counter’},p,button)

}

}

let element = React.createElement(Counter,{name:“计时器”})

ReactDOM.render(element,document.getElementById(‘root’))

我们发现自定义组件好像需要继承React.Component。这是为什么呢?

我之前一直误认为所有的生命周期都是从Component继承过来的,也许有很多小伙伴都和我一样有这样的误解,直到我看了Component源码才恍然大悟,原来我们用的setState和forceUpdate方法是来源于这里

知道这个原因后,我们就可以先简单地实现React.Component了

// component.js

class Component{

constructor(props){

this.props = props

}

}

export {

Component

}

然后再引入react中即可

// react.js

import {createElement} from “./element”

import {Component} from “./component”

const React = {

createElement,

Component

}

export default React

跟 处理NativeUnit一样,先通过createUnit判断element是属于什么类型,如果是自定义组件就 return CompositeUnit

// Unit.js

import { Element } from “./element” // 新增代码

import $ from “jquery”

class Unit {

constructor(element) {

this._currentElement = element

}

getMarkUp() {

throw Error(“此方法应该被重写,不能直接被使用”)

}

}

class TextUnit extends Unit {

}

class NativeUnit extends Unit {

}

function createUnit(element) {

if (typeof element === ‘string’ || typeof element === “number”) {

return new TextUnit(element)

}

if (element instanceof Element && typeof element.type === “string”) {

return new NativeUnit(element)

}

// 新增代码

if(element instanceof Element && typeof element.type === ‘function’){

return new CompositeUnit(element)

}

}

export {

createUnit

}

为什么是用 typeof element.type === 'function'来判断 呢? 因为Counter是 一个类,而类在js中的本质就是function

好了,接下来实现一下CompositeUnit类

class CompositeUnit extends Unit{

getMarkUp(reactid){

this._reactid = reactid

let {type:Component,props} = this._currentElement // 实际上,在例子中type === Counter

let componentInstance = new Component(props);

let renderElement = componentInstance.render();

let renderUnit = createUnit(renderElement);

return renderUnit.getMarkUp(this._reactid)

}

}

咦,好简短 啊,不过 没那么 简单,但是让 我的三寸不烂之舌来讲解一下,包懂

此时的_currentElement 是:

{

type:Counter,

props:{}

}

let {type:Component,props} = this._currentElement// 实际上,在例子中type就是Counter

new Component(props);其实就是new Counter

也就是我们上面例子中写的

class Counter extends React.Component{

constructor(props){

super(props)

this.state = {number:0};

}

render(){

let p = React.createElement(‘p’,{style:{color:‘red’}},this.state.number);

let button = React.createElement(‘button’,{},“+”)

return React.createElement(‘div’,{id:‘counter’},p,button)

}

}

let element = React.createElement(Counter,{name:“计时器”})

ReactDOM.render(element,document.getElementById(‘root’))

可想而知 ,通过new Counter就获得了Counter的实例

也就是componentInstance ,而每一个Counter的实例都会有render方法,所以执行componentInstance.render()

就获得一个给定类型的ReactElement元素(好熟悉的一句话,对,我们在上面讲到过)。

然后就把这个ReactElement元素对象传给createUnit,获得一个具有getMarkUp的renderUnit 对象,

然后就可以执行renderUnit.getMarkUp(this._reactid)获得真实dom,就可以返回了。

其实,仔细想想,就会发现,在

let renderUnit = createUnit(renderElement);

之前,我们是在处理自定义组件Counter。

而到了

let renderUnit = createUnit(renderElement);

这一步,其实就是在处理NativeUnit。(细思极恐。。)

好了,测试一下在这里插入图片描述

发现确实成功了。

12. 实现 componentWillMount


我们在之前的例子上添加个componentWillMount 生命周期函数吧

// index.js

import React from ‘./react’;

import ReactDOM from ‘./react-dom’;

class Counter extends React.Component{

constructor(props){

super(props)

this.state = {number:0};

}

componentWillMount(){

console.log(“阳光你好,我是componentWillMount”);

}

render(){

let p = React.createElement(‘p’,{style:{color:‘red’}},this.state.number);

let button = React.createElement(‘button’,{},“+”)

return React.createElement(‘div’,{id:‘counter’},p,button)

}

}

let element = React.createElement(Counter,{name:“计时器”})

ReactDOM.render(element,document.getElementById(‘root’))

我们知道componentWillMount 实在组件渲染前执行的,所以我们可以在render之前执行这个生命周期函数

class CompositeUnit extends Unit{

getMarkUp(reactid){

this._reactid = reactid

let {type:Component,props} = this._currentElement // 实际上,在例子中type === Counter

let componentInstance = new Component(props);

componentInstance.componentWillMount && componentInstance.componentWillMount() // 添加生命周期函数

let renderElement = componentInstance.render();

let renderUnit = createUnit(renderElement);

return renderUnit.getMarkUp(this._reactid)

}

}

可能聪明的小伙伴会问,不是说componentWillMount是在组件重新渲染前执行的吗?那组件没挂到页面上应该都是渲染前,所以componentWillMount也可以在return renderUnit.getMarkUp(this._reactid)前执行啊。

其实要回答这个问题,倒不如回答另一个问题:

父组件的componentWillMount和子组件的componentWillMount哪个先执行。

答案是父组件先执行。

这是因为在父组件中会先执行 父组件的componentWillMount ,然后执行componentInstance.render();的时候,会解析子组件,然后又进入子组件的getMarkUp。又执行子组件的componentWillMount 。

若要回答 为什么componentWillMount 要在 render函数执行前执行,只能说,react就是这么设计的哈哈哈

13. 实现componentDidMount


众所周知,componentDidMount是在组件渲染,也就是挂载到页面后才执行的。

所以,我们可以在返回组件的真实dom之前 就监听 一个mounted事件,这个事件执行componentDidMount方法。

class CompositeUnit extends Unit{

getMarkUp(reactid){

this._reactid = reactid

let {type:Component,props} = this._currentElement // 实际上,在例子中type === Counter

let componentInstance = new Component(props);

componentInstance.componentWillMount && componentInstance.componentWillMount()

let renderElement = componentInstance.render();

let renderUnit = createUnit(renderElement);

$(document).on(“mounted”,()=>{

componentInstance.componentDidMount && componentInstance.componentDidMount()

})

return renderUnit.getMarkUp(this._reactid)

}

}

然后 再在 把组件的dom挂载到 页面上后再触发这个 mounted事件

// react-dom.js

import {createUnit} from ‘./unit’

import $ from “jquery”

let ReactDOM = {

render,

rootIndex:0

}

function render(element,container){

let unit = createUnit(element)

let markUp = unit.getMarkUp(ReactDOM.rootIndex);// 用来返回HTML标记

$(container).html(markUp)

$(document).trigger(“mounted”)

}

export default ReactDOM

由此依赖,就实现了,componentDidMount 生命周期函数,哈哈哈。

测试一下,成功了没有哈

在这里插入图片描述

啊,一如既往的成功,可能好奇的你问我为什么每次测试都成功,那是因为,不成功也被我调试到成功了。

为了下面 实现 setState 功能,我们 修改一下 CompositeUnit 的getMarkUp方法。

class CompositeUnit extends Unit{

getMarkUp(reactid){

this._reactid = reactid

let {type:Component,props} = this._currentElement // 实际上,在例子中type === Counter

let componentInstance = this._componentInstance = new Component(props); // 把 实例对象 保存到这个 当前的 unit

componentInstance._currentUnit = this // 把 unit 挂到 实例componentInstance

componentInstance.componentWillMount && componentInstance.componentWillMount()

let renderElement = componentInstance.render();

let renderUnit = this._renderUnit = createUnit(renderElement); // 把渲染内容对象也挂载到当前 unit

$(document).on(“mounted”,()=>{

componentInstance.componentDidMount && componentInstance.componentDidMount()

})

return renderUnit.getMarkUp(this._reactid)

}

}

我们为这个 CompositeUnit 的实例添加了

  1. _componentInstance :用了表示 当前组件的实例 (我们所写的Counter组件)

  2. _renderUnit: 当前组件的render方法返回的react元素对应的unit._currentElement

另外,我们也通过

componentInstance._currentUnit = this // 把 unit 挂到 实例componentInstance

把当前 的unit 挂载到了 组件实例componentInstance身上。

可见 组件的实例保存了 当前 unit,当前的unit也保存了组件实例

14. 实现setState


我们看下面的例子,每隔一秒钟就number+1

// index.js

import React from ‘./react’;

import ReactDOM from ‘./react-dom’;

import $ from ‘jquery’

class Counter extends React.Component{

constructor(props){

super(props)

this.state = {number:0};

}

componentWillMount(){

console.log(“阳光你好,我是componentWillMount”);

$(document).on(“mounted”,()=>{

console.log(456);

})

}

componentDidMount(){

setInterval(()=>{

this.setState({number:this.state.number+1})

},1000)

}

render(){

return this.state.number

}

}

let element = React.createElement(Counter,{name:“计时器”})

ReactDOM.render(element,document.getElementById(‘root’))

前面说到,setState方法是从Component组件继承过来的。所以我们给Component组件添加setState方法

// component.js

class Component{

constructor(props){

this.props = props

}

setState(partialState){

// 第一个参数是新的元素,第二个参数是新的状态

this._currentUnit.update(null,partialState)

}

}

export {

Component

}

我们发现原来是在setState方法里调用了当前实例的对应的unit的update方法,它传进去了 部分state的值。

看到这里,我们就知道了,我们需要回到 CompositeUnit类添加一个update方法。

class CompositeUnit extends Unit{

update(nextElement,partialState){

// 有传新元素的话就更新currentElement为新的元素

this._currentElement = nextElement || this._currentElement;

// 获取新的状态,并且更新组件的state

let nextState = this._componentInstance.state = Object.assign(this._componentInstance.state,partialState);

// 新的属性对象

let nextProps = this._currentElement.props

}

getMarkUp(reactid){

}

}

我们首先 更换了_currentElement的值,这里为什么会有 有或者没有nextElement的情况呢?

(主要就是因为,如果 _currentElement 是 字符串或者数字的话,那么它就需要 传nextElement 来替换掉旧的 _currentElement 。而如果不是字符串或者数字的话,是不需要传的。而CompositeUnit 必定是组件的,所以不用传nextElement )。

接着,我们 通过下面这句代码获取了最新的state,并且更新了组件的state

let nextState = this._componentInstance.state = Object.assign(this._componentInstance.state,partialState);

获取 最新的 props跟获取state的方式不一样,props是跟_currentElement 绑定在一起的,所以获取最新的props是通过

let nextProps = this._currentElement.props

接下来,我们要先获取新旧的渲染元素,然后拿来比较,怎么获取呢?

class CompositeUnit extends Unit{

update(nextElement,partialState){

// 有传新元素的话就更新currentElement为新的元素

this._currentElement = nextElement || this._currentElement;

// 获取新的状态,并且更新组件的state

let nextState = this._componentInstance.state = Object.assign(this._componentInstance.state,partialState);

// 新的属性对象

let nextProps = this._currentElement.props

// 下面要进行比较更新

// 先得到上次渲染的unit

let preRenderedUnitInstance = this._renderUnit;

// 通过上次渲染的unit得到上次渲染的元素

let preRenderElement = preRenderedUnitInstance._currentElement

// 得到最新的渲染元素

let nextRenderElement = this._componentInstance.render()

}

getMarkUp(reactid){

}

}

我们先得到上次渲染的unit,再通过上次渲染的unit得到上次渲染的元素preRenderElement ,

再通过this._componentInstance.render()得到下次渲染的元素nextRenderElement 。

接下来就可以进行比较这两个元素了

我们首先会判断要不要进行深度比较。

如果不是进行深度比较就非常简单

直接获取新的渲染unit,然后通过getMarkUp获得要渲染的dom,接着就把当前的组件里的dom元素替换掉

class CompositeUnit extends Unit{

update(nextElement,partialState){

// 有传新元素的话就更新currentElement为新的元素

this._currentElement = nextElement || this._currentElement;

// 获取新的状态,并且更新组件的state

let nextState = this._componentInstance.state = Object.assign(this._componentInstance.state,partialState);

// 新的属性对象

let nextProps = this._currentElement.props

// 下面要进行比较更新

// 先得到上次渲染的unit

let preRenderedUnitInstance = this._renderUnit;

// 通过上次渲染的unit得到上次渲染的元素

let preRenderElement = preRenderedUnitInstance._currentElement

// 得到最新的渲染元素

let nextRenderElement = this._componentInstance.render()

// 如果新旧两个元素类型一样,则可以进行深度比较,如果不一样,直接干掉老的元素,新建新的

if(shouldDeepCompare(preRenderElement,nextRenderElement)){

}else{

this._renderUnit = createUnit(nextRenderElement)

let nextMarkUp = this._renderUnit.getMarkUp(this._reactid)

( ‘ [ d a t a − r e a c t i d = " (`[data-reactid=" ([datareactid="{this._reactid}"]`).replaceWith(nextMarkUp)

}

}

getMarkUp(reactid){

}

}

我们先简单地写一下shouldDeepCompare方法,直接return false,来测试一下 非深度比较,是否能够正确执行

function shouldDeepCompare(){

return false

}

class CompositeUnit extends Unit{

update(nextElement,partialState){

// 有传新元素的话就更新currentElement为新的元素

this._currentElement = nextElement || this._currentElement;

// 获取新的状态,并且更新组件的state

let nextState = this._componentInstance.state = Object.assign(this._componentInstance.state,partialState);

// 新的属性对象

let nextProps = this._currentElement.props

// 下面要进行比较更新

// 先得到上次渲染的unit

let preRenderedUnitInstance = this._renderUnit;

// 通过上次渲染的unit得到上次渲染的元素

let preRenderElement = preRenderedUnitInstance._currentElement

// 得到最新的渲染元素

let nextRenderElement = this._componentInstance.render()

// 如果新旧两个元素类型一样,则可以进行深度比较,如果不一样,直接干掉老的元素,新建新的

if(shouldDeepCompare(preRenderElement,nextRenderElement)){

}else{

this._renderUnit = createUnit(nextRenderElement)

let nextMarkUp = this._renderUnit.getMarkUp(this._reactid)

( ‘ [ d a t a − r e a c t i d = " (`[data-reactid=" ([datareactid="{this._reactid}"]`).replaceWith(nextMarkUp)

}

}

getMarkUp(reactid){

}

}

在这里插入图片描述

发现确实成功了。

如果可以进行深度比较呢?

class CompositeUnit extends Unit{

update(nextElement,partialState){

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(资料价值较高,非无偿)

核心竞争力,怎么才能提高呢?

成年人想要改变生活,逆转状态?那就开始学习吧~

万事开头难,但是程序员这一条路坚持几年后发展空间还是非常大的,一切重在坚持。

为了帮助大家更好更高效的准备面试,特别整理了《前端工程师面试手册》电子稿文件。

前端面试题汇总

JavaScript

性能

linux

前端资料汇总

完整版PDF资料免费分享,只需你点赞支持,动动手指点击此处即可领取了

前端工程师岗位缺口一直很大,符合岗位要求的人越来越少,所以学习前端的小伙伴要注意了,一定要把技能学到扎实,做有含金量的项目,这样在找工作的时候无论遇到什么情况,问题都不会大。

._renderUnit = createUnit(nextRenderElement)

let nextMarkUp = this._renderUnit.getMarkUp(this._reactid)

( ‘ [ d a t a − r e a c t i d = " (`[data-reactid=" ([datareactid="{this._reactid}"]`).replaceWith(nextMarkUp)

}

}

getMarkUp(reactid){

}

}

在这里插入图片描述

发现确实成功了。

如果可以进行深度比较呢?

class CompositeUnit extends Unit{

update(nextElement,partialState){

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-NLcudYKu-1711781576595)]

[外链图片转存中…(img-DHS2Mn1e-1711781576595)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

[外链图片转存中…(img-OvzWW3Mc-1711781576596)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(资料价值较高,非无偿)

核心竞争力,怎么才能提高呢?

成年人想要改变生活,逆转状态?那就开始学习吧~

万事开头难,但是程序员这一条路坚持几年后发展空间还是非常大的,一切重在坚持。

为了帮助大家更好更高效的准备面试,特别整理了《前端工程师面试手册》电子稿文件。

前端面试题汇总

JavaScript

性能

linux

前端资料汇总

完整版PDF资料免费分享,只需你点赞支持,动动手指点击此处即可领取了

前端工程师岗位缺口一直很大,符合岗位要求的人越来越少,所以学习前端的小伙伴要注意了,一定要把技能学到扎实,做有含金量的项目,这样在找工作的时候无论遇到什么情况,问题都不会大。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值