react源碼(一):實現createElement和render函數

由於react依賴JSX語法,因此最開始須要對JSX的執行過程作大體介紹。至於JSX的相關基礎知識,請參照JSX官網css

JSX的執行過程

  • 1.寫出代碼
<h1>Hello</h1>複製代碼
  • 2.用打包工具如webpack,調用babel-loader把JSX語法轉換成JavaScript函數,createElement
  • 3.運行代碼時,瀏覽器執行createElement,獲得虛擬DOM,也就是react元素。其實react元素就是一個純粹的JavaScript對象,描述了會在頁面上顯示的DOM的屬性。
  • 4.把虛擬DOM(react元素)給render函數,render就會把虛擬DOM轉換成真實的DOM,並插入到頁面中去。

react中的虛擬DOM

上面說道react元素就是虛擬DOM,接着來看一下react元素有哪些屬性html

// 首先是調用React.createElement生成react元素,並在控制檯輸出let element = React.createElement("h1", {className: 'title',style: {color: 'red'}
}, React.createElement('span', null, 'hello'), ' world');console.log(JSON.stringify(element,null,2));

ReactDOM.render(
    element, document.getElementById('root')
);// 下面展現的屬性並非徹底的react元素的屬性,爲了簡化,把一些當前階段用不到的屬性進行刪除/**
{
     "type": "h1",
     "props": {
         "className": "title",
         "style": {
             "color": "red"
         },
         "children": [
         	{
                 "type": "span",
                 "props": {
                     "children": "hello"
                 }
             },
             " world"
         ]
     }s
 }
 */複製代碼

主要思路

  • 1.createElement函數的主要任務就是建立一個JavaScript對象
  • 2.render函數的主要任務就是建立文本節點和各類元素,將其插入到頁面中去

代碼實現

  • 1.createElement函數
/**
 * 
 * @param {*} type 元素類型
 * @param {*} config 配置對象,通常來講就是屬性對象
 * @param {*} children 第一個兒子
 */function createElement(type,config,children){if(config){delete config._owner;delete config._store;
    }let props = {...config};if(arguments.length > 3){
        children = Array.prototype.slice.call(arguments,2);
    }// children多是數組(多於一個兒子),也多是一個字符串或數字、也多是一個null 或 react元素props.children = children;return {
        type,props
    }
}複製代碼
  • 2.render函數
/**
 * 虛擬DOM轉換成真實DOM 並插入到容器
 * @param {*} vdom 虛擬DOM
 * @param {*} container 插入到哪一個容器
 */function render(vdom,container){const dom = createDOM(vdom);
    container.appendChild(dom);
}/**
 * 把虛擬DOM變成真實DOM
 * @param {} vdom null 數字 字符串 React元素 數組 暫時不支持數組
 */function createDOM(vdom){// null就什麼都不作// 若是vdom是一個字符串或者數字的話,建立一個文本的DOM節點返回if(typeof vdom  === 'string' || typeof  vdom ===  'number'){return document.createTextNode(vdom);
    }// 若是不存在返回空串 undefined nullif(!vdom){return '';
    }// 不然就是一個react元素let {type,props} = vdom;let dom = document.createElement(type); //span divupdateProps(dom,props); // 把虛擬DOM上的屬性設置到真實DOM上// 處理子節點 若是子節點就是一個單節點 而且是字符串或數字if (typeof props.children === 'string' || typeof props.children === 'number') {
        dom.textContent = props.children; // dom.textContent = 'hello';// 說明是一個單React元素節點} else if (typeof props.children === 'object' && props.children.type) {
        render(props.children,dom);// 若是兒子是一個數組,說明有多個子節點}else if(Array.isArray(props.children)){
        reconcileChildren(props.children,dom);
    }else{ // 若是出現了其餘的意外狀況dom.textContent = props.children ? props.children.toString():'';
    }return dom;
}/**
 * 把子節點從虛擬DOM所有轉換成真實DOM 並插入到父節點中去
 * @param {*} childrenVdom 子節點的虛擬DOM數組
 * @param {*} parentDOM 父節點真實DOM
 */function reconcileChildren(childrenVdom,parentDOM){
    childrenVdom.forEach(childVdom => {
        render(childVdom,parentDOM);
    });
}/**
 * 把屬性對象中的屬性設置到DOM元素上
 * @param {*} dom 
 * @param {*} props 
 */function updateProps(dom,props){for(let key in props){if(key === 'children') continue;if(key === 'style'){let styleObj = props[key];for(let key in styleObj){
                dom.style[key] = styleObj[key]; // dom.style.color = 'red';}
        }else{
            dom[key] = props[key]; // dom.className = 'title';}
    }
}複製代碼
相關文章
相關標籤/搜索