美文网首页Web前端之路
2020 动手写个 react (2)

2020 动手写个 react (2)

作者: zidea | 来源:发表于2020-05-08 19:50 被阅读0次
coding

我们写一段 jsx ,有关 jsx 语法这里暂不做过介绍,留下伏笔

const ele = (
    const ele = (
    <div className="box" size="25">
    hello <span>zidea</span>
    </div>
)
)

然后将上面 jsx 通过 babel 转换为 js 如下

"use strict";

const ele = /*#__PURE__*/React.createElement("div", {
  className: "box",
  size: "25"
}, "hello ", /*#__PURE__*/React.createElement("span", null, "zidea"));

有了上面生成代码,我们简单地分析一下

React.createElement(tag,attrs,children)
  • tag 是要创建标签名称
  • attrs 是以对象形式组织属性对象,对象中以键值对形式保存属性名和值
  • children 是若干个子元素

实现 createElement 方法

  • 创建 React 对象
const React = {
    createElement
}

  • 实现 creatElement 方法

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

export default React;

  • 引入 React 对象,使用 React 的 createElement 方法
import React from './react'

const ele = (
    <div className='title' title='react source analysis'>
        hello <span>react</span>
    </div>
)

console.log(ele)

实现渲染

我们都知道在 React 中调用 ReactDom 的 render 方法然后传入虚拟节点和要将虚拟节点添加容器 dom 元素就可以实现将虚拟节点渲染到页面。

ReactDOM.render(ele,document.querySelector("#root"))
import React from './react'

const ele = (
    <div className='box' title='hello zidea'>
        hello <span>react</span>
    </div>
)

// console.log(ele)

ReactDOM.render(ele,document.querySelector("#root"))

接下依旧是创建文件夹 react-dom 然后在该文件夹下创建 index.js 文件实现 ReactDOM 的 render 方法

const ReactDOM = {
    render
}

function render(vnode,container){
    //TODO
}

export default ReactDOM;

有关常见一个 js 模块并且将 ReactDOM 对象暴露出来我就不做过多解释了。接下来我们就专注 render 方法的实现。

文本节点实现

function render(vnode,container){
    //TODO
    if(vnode === undefined ) return;
    // vnode is equal string
    if(typeof vnode === 'string'){
        //create textNode
        const textNode = document.createTextNode(vnode)
        return container.appendChild(textNode)
    }
}
  • 参数和 react 没有什么区别
    • vnode 虚拟节点
    • container 容器
  • 首选判断 vnode 是否为空,为空则直接返回
  • 然后判断 vnode 是否为字符串,如果为字符串这添加文本节点

看效果

ReactDOM.render('react',document.querySelector("#root"))

虚拟节点

ReactDOM.render(ele,document.querySelector("#root"));
console.log(vnode)
function render(vnode,container){
    ...

    // deconstruct vnode
    const {tag} = vnode;

    //create dom object
    const dom = document.createElement(tag)

    container.appendChild(dom)
}
  • 解构 vnode 对象
  • 根据 tag 创建 dom 对象
  • 将 dom 对象添加到容器中

获取属性

function render(vnode,container){

    ... 
    // deconstruct vnode
    const {tag,attrs} = vnode;

    //create dom object
    const dom = document.createElement(tag)

    if(attrs){
        // property key: className box
        Object.keys(attrs).forEach(key=>{
            const val = attrs[key]
        })
    }
    ...
}
  • 从虚拟节点(vnode)中解构出 attrs
  • 然后遍历 attrs 获取属性值

设置属性

function setAttribute(dom,key,value){

}

实现 setAttribute 方法

function setAttribute(dom,key,value){

    // convert className to class
    // 1. event 2.class 3.style etc 

    // class case
    if(key === 'className'){
        key = 'class'
    }

    // event case
    if(/on\w+/.test(key)){
        //to lower case
        key = key.toLowerCase();
        dom[key] = value || ''
    }else if(key === 'style'){
        if(!value || typeof value === 'string'){
            dom.style.cssText = value || '';
        }else if(value && typeof value === 'object'){
            //{width:16}
            for(let k in value){
                if(typeof value[k] === 'number'){
                    dom.style[k] = value[k] + 'px'
                }else{
                    dom.style[k] = value[k]
                }
            }
        }
    }else{
        if(key in dom){
            dom[key] = value || ''
        }
        if(value){
            dom.setAttribute(key,value)
        }else{
            dom.removeAttribute(key)
        }
    }

}

在 dom 上有许多属性,可以通过属性添加样式,添加事件,保存数据等。我们这里通过判断 attrs 中属性键和值类型来一一进行判断

class 属性

如果键为 className 时,我们将键修改 class 虽然添加值

if(key === 'className'){
    key = 'class'
}
事件属性
if(/on\w+/.test(key)){
    //to lower case
    key = key.toLowerCase();
    dom[key] = value || ''
}
样式属性
else if(key === 'style'){
    if(!value || typeof value === 'string'){
        dom.style.cssText = value || '';
    }else if(value && typeof value === 'object'){
        //{width:16}
        for(let k in value){
            if(typeof value[k] === 'number'){
                dom.style[k] = value[k] + 'px'
            }else{
                dom.style[k] = value[k]
            }
        }
    }
}

样式情况相对于其他属性要复杂一些,分两种情况处理可能是字符串或者一个对象

  • 字符串情况直接赋值即可
  • 对象情况
    在开始处理对象情况,我们先看一看下面 jsx 对象编译为 javascript 对象后默认
const ele = (
    <div className="box" size="25" style={{width:16}}>
    hello <span>zidea</span>
    </div>
)
...
style: {
    width: 16
  }
...

这里 style 对象中嵌套一个对象,我们通过解析对象来获取每一个样式的名称和样式值。样式值可能是数值也可能是字符串。

for(let k in value){
    if(typeof value[k] === 'number'){
        dom.style[k] = value[k] + 'px'
    }else{
        dom.style[k] = value[k]
    }
}
if(attrs){
    // property key: className box
    Object.keys(attrs).forEach(key=>{
        const val = attrs[key]
        setAttribute(dom,key,val)
    })
}

container.appendChild(dom)

渲染子节点

通过递归形式来实现渲染子节点

function render(vnode,container){
    
    ...

    vnode.children.forEach(child=>render(child,dom))

    container.appendChild(dom)
}

相关文章

  • 2020 动手写个 react (2)

    我们写一段 jsx ,有关 jsx 语法这里暂不做过介绍,留下伏笔 然后将上面 jsx 通过 babel 转换为 ...

  • 2020 动手写个 react (1)

    百度和阿里内部都是在使用 react,包括今日头条也在使用 react。似乎 vue 最近比较火,但是对于一些大型...

  • 2020 动手写个 react (3)

    组件 组件可能是函数还可能是类,我们知道只要继承 Component 类就是 react 的组件。 创建一个函数,...

  • 官宣:ReactNative导航库重大更新

    2020年2月6日React Navigation通过其博客正式宣布了React Navigation第5个版本,...

  • 2020-05-07

    #2020/5/4到 2020/5/10 1、完成TS基础语法的学习 2、学会TS在react中如何使用(vue暂...

  • Dva最不佳实践

    前言 由于之前写了几个dva的项目,近期没怎么用有些遗忘了,写个小结记录一下。 dva是基于react、react...

  • 原生实现一个react-redux的代码示例

    自己动手实现一个react-redux之前试过自己动手实现一个redux,这篇blog主要记录动手实现一个reac...

  • 原生实现一个react-redux

    自己动手实现一个react-redux 之前试过自己动手实现一个redux,这篇blog主要记录动手实现一个rea...

  • 动手写个小程序-TodoList

    前言 前两天写过一篇关于我对小程序的看法和对未来前端发展趋势,接触小程序开发也有两周左右了,对于一个之前从来没有正...

  • 动手写个简易的Retrofit

    Retrofit简易版本 为啥要写这个? 之前一直使用OKHttp,之前修改过鸿洋的OKhttpUtils增加了缓...

网友评论

    本文标题:2020 动手写个 react (2)

    本文链接:https://www.haomeiwen.com/subject/gnkqnhtx.html