2020 动手写个 react (2)

coding 我们写一段 jsx ,有关 jsx 语法这里暂不做过介绍,留下伏笔 const ele = ( const ele = (
hello zidea
) ) 然后将上面 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 = (
hello react
) console.log(ele) 图 实现渲染 我们都知道在 React 中调用 ReactDom 的 render 方法然后传入虚拟节点和要将虚拟节点添加容器 dom 元素就可以实现将虚拟节点渲染到页面。 ReactDOM.render(ele,document.querySelector("#root")) import React from './react' const ele = (
hello react
) // 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 = (
hello zidea
) ... 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) }

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):