JSX什么鬼(一起來寫一個JSX渲染引擎)

原文地址:https://jasonformat.com/wtf-is-jsx/

JSX 實際上很簡單:讀完這篇文章,你就會完全了解這個可選擇的模版引擎

副標題:“和JSX共處”

注解

你在每個文件和每個函數(shù)里定義這個,告訴轉譯器(如:Babel)每個節(jié)點在運行時階段需要調用的函數(shù)名。

在下面的例子里,我們稱之為“對每個節(jié)點,插入h()函數(shù)的調用”

/*@jsx h/

轉譯

如果你還沒有使用過轉譯器,你應該嘗試使用。因為用es6/ES2015寫,調試,測試或運行js都更加有效率。其中Babel是最流行的,也是最被推薦使用的。我們假設你使用了babel

如今babel不僅提供轉換你的ES6/ES7+語法支持,并且提供直接開箱即用,轉換JSX的支持。你可以直接使用這種特性。
我們先來看個簡單的例子:
有jsx之前(你怎么寫代碼)

/** @jsx h */
let foo = <div id="foo">Hello!</div>; 

有jsx后(你運行的代碼)

var foo = h('div', {id:"foo"}, 'Hello!');

你可能看到第二段代碼,覺得用函數(shù)來創(chuàng)建UI也不錯

這就是我為什么從JSX講起:如果沒有這個,你手動寫出來仍然很簡單

JSX只是一種已經很優(yōu)雅語法的語法糖

有人甚至整個項目用它hypescript

我們來寫個jsx渲染器

首先,我們要定義下我們轉換的代碼調用的h()函數(shù)。

你可以把這個函數(shù)命名為任何名字,我之所以用h(),是因為在hypescript里這種類型的‘build’函數(shù)就是這么稱呼的

  function h(nodeName, attributes, ...args) {  
      let children = args.length ? [].concat(...args) : null;
      return { nodeName, attributes, children };
}

好了,就是這么簡單
你不熟悉ES6/ES2005?

1.參數(shù)中的'...'是剩余參數(shù),該操作符會收集剩余的參數(shù)到一個數(shù)組里
2.concat(...args)是spread操作符:這個操作符會把剛剛的參數(shù)數(shù)組展開到arguments里,再傳給concat()方法。這里用concat()是為了合并所有子節(jié)點的嵌套數(shù)組。

現(xiàn)在我們通過h()方法輸出一個嵌套的JSON對象,這個‘樹狀’對象就像下面這樣:

{
  nodeName: "div",
  attributes: {
    "id": "foo"
  },
  children: ["Hello!"]
}

所以我們只需要一個函數(shù),接受這樣的參數(shù)格式,并輸出真實的dom節(jié)點

function render(vnode) {  
    // Strings just convert to #text Nodes:
    if (vnode.split) return document.createTextNode(vnode);

    // create a DOM element with the nodeName of our VDOM element:
    let n = document.createElement(vnode.nodeName);

    // copy attributes onto the new node:
    let a = vnode.attributes || {};
    Object.keys(a).forEach( k => n.setAttribute(k, a[k]) );

    // render (build) and then append child nodes:
    (vnode.children || []).forEach( c => n.appendChild(render(c)) );

    return n;
}

理解這個很容易。
你也可以把‘虛擬DOM’認為是如何構建DOM結構的一種簡單配置。

虛擬dom的優(yōu)勢是它十分輕量。輕量對象引用其他輕量對象。非常容易優(yōu)化的應用程序結構。這也表示它沒有綁定任何渲染邏輯和很慢的DOM方法。

使用JSX

現(xiàn)在知道JSX被轉換成了對h()的調用。這些函數(shù)調用創(chuàng)建一個簡單的“虛擬”DOM樹。
我們可以使用render()函數(shù)去創(chuàng)建匹配的“真實”DOM樹。

就像這樣:

// JSX -> VDOM:
let vdom = <div id="foo">Hello!</div>;

// VDOM -> DOM:
let dom = render(vdom);

// add the tree to <body>:
document.body.appendChild(dom);  

局部模版,迭代和邏輯:沒有新語法

區(qū)別于模版語言引入的有限概念和局限,我們有javascript所擁有的一切能力

'局部模版'是由無邏輯/少邏輯的模版引擎為了在不同的地方重用視圖而引入的概念。

迭代是幾乎所有模版語法都會引入的一個東西(我也是這么做)。同樣,對于JSX:和其他javascript程序一樣。你可以選擇一種適合的迭代方式:[].forEach,[].map(),for和while循環(huán)等等。

和迭代一樣,模版語法都喜歡重定義邏輯。一方面,無邏輯模版里,視圖加入邏輯很不方便:不合理的,如{{#if value}}這樣的設計,邏輯加入到了controller層,使controller變的十分臃腫。這種規(guī)避方式,創(chuàng)造了一種語言來描述更復雜的邏輯,使得bug不好預測而且容易產生安全隱患。

另一方面,用代碼生成技術的引擎(一種簡陋到不可原諒的技術),通常自詡可以執(zhí)行任何JavaScript所寫的邏輯甚至迭代表達式。這里有原因,我們?yōu)槭裁匆欢ㄒ苊馐褂眠@項技術:你的代碼已經脫離了原來的’位置‘(模塊,閉包或這標簽內),在別處執(zhí)行。這對我而言,不可預測,并且不安全。

JSX擁有javascript的一切能力,不需要在build階段生成怪異的代碼,并且不使用eval()

// Array of strings we want to show in a list:
let items = ['foo', 'bar', 'baz'];

// creates one list item given some text:
function item(text) {  
    return <li>{text}</li>;
}

// a "view" with "iteration" and "a partial":
let list = render(  
  <ul>
    { items.map(item) }
  </ul>
);

render()返回一個DOM節(jié)點(上面是<ul>),所以我們只要把它加入DOM中:

document.body.appendChild(list);  

合在一起

這里是我們虛擬DOM渲染的完整版,下面的是CodePen上有樣式的版本

const ITEMS = 'hello there people'.split(' ');

// turn an Array into list items: 
let list = items => items.map( p => <li> {p} </li> );

// view with a call out ("partial") to generate a list from an Array:
let vdom = (  
    <div id="foo">
        <p>Look, a simple JSX DOM renderer!</p>
        <ul>{ list(ITEMS) }</ul>
    </div>
);

// render() converts our "virtual DOM" (see below) to a real DOM tree:
let dom = render(vdom);

// append the new nodes somewhere:
document.body.appendChild(dom);

// Remember that "virtual DOM"? It's just JSON - each "VNode" is an object with 3 properties.
let json = JSON.stringify(vdom, null, '  ');

// The whole process (JSX -> VDOM -> DOM) in one step:
document.body.appendChild(  
    render( <pre id="vdom">{ json }</pre> )
);

CodePenDemo

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • HTML模版 之后出現(xiàn)的React代碼嵌套入模版中。 1. Hello world 這段代碼將一個一級標題插入到指...
    ryanho84閱讀 6,445評論 0 9
  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內容,還有我對于 Vue 1.0 印象不深的內容。關于...
    云之外閱讀 5,177評論 0 29
  • 0. 寫在前面 當你開始工作時,你不是在給你自己寫代碼,而是為后來人寫代碼。 —— Nichloas C. Zak...
    康斌閱讀 5,521評論 1 42
  • 對于天秤座來說,選擇是我們的死穴。一直認定天秤座是糾結的,所以我也糾結,這是天生的。但幼稚的想法隨之自己經歷...
    I叫不糾結閱讀 186評論 0 1
  • 好好照顧自己的身體,才能給家人更好的愛。 “我跟你說一件事?!睆┫壬砬橥蝗蛔兊脟烂C起來,我在短短幾秒腦海里過了無...
    紓琪閱讀 427評論 14 8

友情鏈接更多精彩內容