React的簡單實現(xiàn)(一)JSX和虛擬Dom

createElement函數(shù)

在react里面JSX會被轉(zhuǎn)譯成用React.createElement方法包裹的代碼赂弓,比如

const title = <h1 className="title">Hello, world!</h1>

會經(jīng)過一個語法分析的庫(babel里面)參考:http://www.reibang.com/p/7e872afeae42
沼沈,最終被轉(zhuǎn)化為:

const title = React.createElement(
    'h1',
    { className: 'title' },
    'Hello, world!'
);

不管JSX梁呈,先簡單實現(xiàn)一下createElement這個函數(shù)啤咽,從jsx轉(zhuǎn)譯結(jié)果來看巷疼,createElement方法的參數(shù)是這樣:

createElement( tag, attrs, child1, child2, child3 );

第一個參數(shù)是DOM的標(biāo)簽名珊膜,比如div宣脉,h1,span等等
第二個參數(shù)是一個對象竹祷,里面包含了所有的屬性羊苟,可能包含了className塑陵,id等等
第三個參數(shù)開始践险,就是它的子節(jié)點

最簡單實現(xiàn),返回一個對象進(jìn)行包裹:

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

試著進(jìn)行封裝和調(diào)用:

const React = {
    createElement
}

const element = (
    <div>
        hello<span>world!</span>
    </div>
);
console.log( element );
image.png

這時候會遇到上面的錯誤,因為目前我們的語法是JSX占遥,所以需要引入babel的支持,參考:https://babeljs.io/docs/en/芬萍,這里我們采用一種更簡單的方式,單純對JSX進(jìn)行轉(zhuǎn)換

1柬祠,新建一個.babelrc文件,指定transform-react-jsx用的方法就是上述的createElement:

{
  "presets": ["env"],
  "plugins": [
    ["transform-react-jsx", {
      "pragma": "React.createElement"
    }]
  ]
}

2嗜愈, 使用parcel打包工具莽龟,對jsx語法進(jìn)行詞法分析,然后用上述的createElement包裹起來毯盈,先進(jìn)行安裝:

npm install -g parcel-bundler

3,調(diào)整代碼結(jié)構(gòu)赘阀,新建index.html和index.js厂镇,js內(nèi)容就是上面的


image.png

4,使用 ‘parcel index.html’ 命令進(jìn)行打包操作
5酌媒,打包完成后得到dist的輸出


image.png

運(yùn)行index.html可以得到下述結(jié)果(可能需要更改下js的路徑)


image.png
ReactDOM.render方法

參考react里面的寫法

ReactDOM.render(
    <h1>Hello, world!</h1>,
    document.getElementById('root')
);

實際上是JSX進(jìn)過轉(zhuǎn)換是這樣的

ReactDOM.render(
    React.createElement( 'h1', null, 'Hello, world!' ),
    document.getElementById('root')
);

render方法的作用就是將對象轉(zhuǎn)換成虛擬DOM秒咨,再最終渲染成真實的DOM

function render( vnode, container ) {
    
    // 當(dāng)vnode為字符串時掌挚,渲染結(jié)果是一段文本
    if ( typeof vnode === 'string' ) {
        const textNode = document.createTextNode( vnode );
        return container.appendChild( textNode );
    }

    const dom = document.createElement( vnode.tag );

    if ( vnode.attrs ) {
        Object.keys( vnode.attrs ).forEach( key => {
            const value = vnode.attrs[ key ];
             setAttribute( dom, key, value );    // 設(shè)置屬性
        } );
    }

    vnode.children.forEach( child => render( child, dom ) );    // 遞歸渲染子節(jié)點

    return container.appendChild( dom );    // 將渲染結(jié)果掛載到真正的DOM上
}

還有setAttribute方法:

function setAttribute( dom, name, value ) {
    // 如果屬性名是className,則改回class
    if ( name === 'className' ) name = 'class';

    // 如果屬性名是onXXX陡厘,則是一個事件監(jiān)聽方法
    if ( /on\w+/.test( name ) ) {
        name = name.toLowerCase();
        dom[ name ] = value || '';
    // 如果屬性名是style特占,則更新style對象
    } else if ( name === 'style' ) {
        if ( !value || typeof value === 'string' ) {
            dom.style.cssText = value || '';
        } else if ( value && typeof value === 'object' ) {
            for ( let name in value ) {
                // 可以通過style={ width: 20 }這種形式來設(shè)置樣式,可以省略掉單位px
                dom.style[ name ] = typeof value[ name ] === 'number' ? value[ name ] + 'px' : value[ name ];
            }
        }
    // 普通屬性則直接更新屬性
    } else {
        if ( name in dom ) {
            dom[ name ] = value || '';
        }
        if ( value ) {
            dom.setAttribute( name, value );
        } else {
            dom.removeAttribute( name );
        }
    }
}

每次render前是目,清理一下之前的內(nèi)容:

const ReactDOM = {
    render: ( vnode, container ) => {
        container.innerHTML = '';
        return render( vnode, container );
    }
}

在index.html里面增加容器:<div id="root"></div>
重新 parcel index.html 運(yùn)行,得到輸出結(jié)果:

image.png

源碼地址:https://github.com/liuxiaocong/dailyMove/tree/master/simpleReact

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末亡容,一起剝皮案震驚了整個濱河市冤今,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辟汰,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異翩蘸,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)催首,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進(jìn)店門泄鹏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人舶治,你說我怎么就攤上這事∶姑停” “怎么了珠闰?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坛悉。 經(jīng)常有香客問我承绸,道長裸影,這世上最難降的妖魔是什么八酒? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮界轩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浊猾。我一直安慰自己,他們只是感情好衔彻,可當(dāng)我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布偷办。 她就那樣靜靜地躺著,像睡著了一般柄沮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上祖搓,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天湖苞,我揣著相機(jī)與錄音,去河邊找鬼镐作。 笑死,一個胖子當(dāng)著我的面吹牛滑肉,可吹牛的內(nèi)容都是我干的摘仅。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼六荒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了掏击?” 一聲冷哼從身側(cè)響起秩铆,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤灯变,失蹤者是張志新(化名)和其女友劉穎捅膘,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寻仗,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡署尤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了俗扇。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡狐援,死狀恐怖究孕,靈堂內(nèi)的尸體忽然破棺而出爹凹,到底是詐尸還是另有隱情,我是刑警寧澤禾酱,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站颗管,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏垦江。R本人自食惡果不足惜搅方,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望姨涡。 院中可真熱鬧,春花似錦赏表、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涕癣。三九已至,卻和暖如春坠韩,著一層夾襖步出監(jiān)牢的瞬間炼列,已是汗流浹背只搁。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留俭尖,地道東北人氢惋。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像稽犁,于是被迫代替她去往敵國和親焰望。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,554評論 2 349