各位小夥伴,春節假期已過大半,這個春節應該是歷年來過的最安靜的一個春節了吧,每天家裏蹲,吃了睡,睡了吃,沒想到不出門居然成了對社會最大的貢獻。廢話很少說,開始說正題。css
相信不少人都用過react開發項目,也有不少人好奇頁面中明明沒有直接用到React,可是頁面卻必須引入React,不然就會報錯。初學者必定有這個疑問,不要着急,今天我就爲你解答這個疑問。html
jsx其實就是個語法糖,用寫html的方式來寫js,一個很像xml的js擴展。React使用jsx來替代常規的js。在線體驗node
寫js不香嗎?爲何要引入一個新概念jsx來迷惑你們?歸納來說jsx來說有如下幾個好處:react
嘗試過上面的在線體驗後,就會發現實際上babel-loader會把jsx預編譯爲React.createElement(xxx)。
jsx預處理前:webpack
jsx預處理後git
接下來咱們就本身動手來實現這三個API吧!github
做用:將傳入的節點轉化成vdom。
(1)建立./simple-react/component.js。實現class組件必備條件。web
export class Component{
static isReactComponent={};
constructor(props){
this.props=props;
this.state={}
}
}
複製代碼
(2)建立./simple-react/index.js文件數組
import {Component} from "./component"
function createElement(type,props,...children){
props.children=children;
// console.log(type);
// 判斷組件類型
let vtype;
if(typeof type==="string"){
// 原生標籤
vtype=1;
}else if(typeof type === "function"){
// 類組件,函數式組件
vtype=type.isReactComponent ? 3 : 2;
}
return {
vtype,
type,
props
}
}
const React={
createElement,
Component
}
export default React;
複製代碼
上面我只是簡單使用了一、二、3來分別標示來標籤類型,你也可使用別的方式來處理。createElement被調用時會傳入標籤類型type,標籤屬性props及若⼲子元素children。安全
做用:渲染vdom,掛載到真實dom樹上。
建立./simple-react/ReactDOM.js文件,包含render函數。
function render(vnode,container){
// vnode->node
mount(vnode,container); // 待實現
}
const ReactDOM={
render
}
export default ReactDOM;
複製代碼
這樣就實現了代碼中的常見的ReactDOM.render(jsx,container)。接下來重點實現mount函數來處理vnode掛載。
建立./simple-react/virtual-dom.js文件。
export function mount(vnode,container){
const {vtype}=vnode;
if(!vtype){
// 純文本節點
mountText(vnode,container);
}
if(vtype===1){
// 原生節點
mountHtml(vnode,container);
}
if(vtype===2){
// 建立函數式節點
mountFunc(vnode,container)
}
if(vtype===3){
// 建立class類型組件
mountClass(vnode,container);
}
}
function mountText(vnode,container){
let textNode=document.createTextNode(vnode);
container.appendChild(textNode)
}
function mountHtml(vnode,container){
const {type,props}=vnode;
let htmlNode=document.createElement(type);
const {children,...rest}=props;
Object.keys(rest).map(item=> {
if(item==="className"){
htmlNode.setAttribute("class",rest[item]);
}
if(item.slice(0,2)==="on"){
// 簡單處理click事件,實際狀況很複雜,須要考慮多種狀況
htmlNode.addEventListener("click",rest[item])
}
})
children.map(item=>{
if(Array.isArray(item)){
item.map(i=>mount(i,htmlNode))
}else{
mount(item,htmlNode)
}
})
container.appendChild(htmlNode)
}
function mountFunc(vnode,container){
const {type,props}=vnode;
let node=type(props);
mount(node,container);
}
function mountClass(vnode,container){
const {type,props}=vnode;
let cmp=new type(props);
let node=cmp.render();
mount(node,container);
}
複製代碼
mount函數建立好以後,完整的ReactDOM文件就能夠改寫爲以下:
import {mount} from "./virtual-dom";
function render(vnode,container){
// vnode->node
mount(vnode,container)
}
const ReactDOM={
render
}
export default ReactDOM;
複製代碼
相關文件建立好以後,在由creat-react-app建立好的項目中改寫index.js文件,引入本身寫好的簡版react文件,進行測試。
import React from "./simple-react";
import ReactDOM from "./simple-react/ReactDOM";
import './index.css'
function Funcomp(props){
return (
<div className="border">{props.name}</div>
)
}
class ClassComp extends React.Component {
constructor(props) {
super(props);
this.state = { }
}
handleClick=()=>{
alert("hello")
}
render() {
return (
<div className="border">
<h1>{this.props.name}</h1>
{
[1,2,3].map(item=>
(
<h1 key={item}>{item}</h1>
)
)
}
<button onClick={this.handleClick}>點我</button>
</div>
);
}
}
let jsx=(
<div>
<div className="border">我是內容</div>
<Funcomp name="我是函數組件內容" />
<ClassComp name="我是class組件內容" />
</div>
)
ReactDOM.render(jsx,document.getElementById("root"))
複製代碼
實際頁面效果若是和下面截圖同樣,表明着簡版react大功告成。
固然在實際react處理中,要處理的狀況遠比上面寫的複雜多得多,dom的更新、替換、刪除要通過diff的過程,打補丁,更新布丁等過程,v16.8以後的fiber更是相似於時間分片的方式,將任務拆分,將高優先級任務優先執行,提升頁面渲染的流暢度等等,有不少須要咱們掌握的東西。
應了那句話:路漫漫其修遠兮,吾將上下而求索。
歡迎點贊、留言、交流。