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 );
這時候會遇到上面的錯誤,因為目前我們的語法是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)容就是上面的
4,使用 ‘parcel index.html’ 命令進(jìn)行打包操作
5酌媒,打包完成后得到dist的輸出
運(yùn)行index.html可以得到下述結(jié)果(可能需要更改下js的路徑)
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é)果:
源碼地址:https://github.com/liuxiaocong/dailyMove/tree/master/simpleReact