非死book React 和 Web Components(Polymer)對比優勢和劣勢
譯者前言
這是一篇來自 StackOverflow 的問答,提問的人認為 React 相比 WebComponents
有一些“先天不足”之處,列舉如下:
- 原生瀏覽器支持
- 原生語法支持(意即不把樣式和結構混雜在 JS 中)
- 使用 Shadow DOM 封裝樣式
- 數據的雙向綁定
這些都是確然的。不過他還是希望聽聽大家的看法,于是就有了這篇精彩的回答。
需要說明的是,這篇回答并沒有討論太多的技術細節,但卻廣泛全面的談及了許多
觀念性的東西,并且補充了許多進階的閱讀資料。可以說“含金量”一般但是“信
息量”很大,特別能夠幫助很多進階者開拓視野,增長知識,轉換觀念。
另外,答案里所引用的幾個演講視頻都非常精彩!遺憾地是訪問受限——大家懂的。
然而我始終覺得這年頭不會“KX上網”的話就不要搞開發了,所以在此還是極力
推薦你好好看一看。
我覺得會對一些人有所助益的,特別是提問題總是提不到點子上的朋友(笑),特
此翻譯并記錄于下:
原題:Pros and Cons of 非死book's React vs. Web Components
(Polymer)
Update: this answer seems to be pretty popular so I took some time to
clean it up a little bit, add some new info and clarify some things that
I thought was not clear enough.
更新:看來我的答案蠻受青睞的,于是我花了點時間略做整理,添加了新的內
容并把之前沒有講清楚的地方也做了進一步的解釋。
Most of your concerns are really a matter of opinion and personal
preference but I'll try to answer as objectively as I can:
你所提及的大部分實際上都屬于個人偏好,不過我還是盡我所能客觀的回答如下:
Native vs. Compiled
Write JavaScript in vanilla JavaScript, write CSS in CSS, write HTML
in HTML.
原生語言對決預編譯語言
用純 JavaScript 來寫 JavaScript,用 CSS 來寫 CSS ,用 HTML 來寫 HTML。
Back in the day there were hot debates whether one should write native
Assembly by hand or use a higher level language like C to make the
compiler generate Assembly code for you. Even before that people refused
to trust assemblers and preferred to write native machine code by hand
(and I'm not joking).
回顧往昔,人們也曾爭論到底是直接手寫匯編還是用像 C 這樣的高階語言利用編譯
器來生成匯編代碼。甚至在更早以前人們拒絕信任匯編程序而寧愿手工編寫原生機
器代碼(我沒開玩笑)。
Meanwhile, today there are a lot of people who write HTML in Haml or
Jade, CSS in Sass or Less and JavaScript in CoffeeScript or TypeScript.
It's there. It works. Some people prefer it, some don't.
時至今日,許多人都使用 Haml 或 Jade 編寫 HTML,用 Sass 或 Less 編寫 CSS
,用 CoffeeScript 或 TypeScript 編寫 JavaScript。存在即真理,能抓老鼠的
就是好貓。有人愛,當然也會有人恨。
The point is that there is nothing fundamentally wrong in not writing
JavaScript in vanilla JavaScript, CSS in CSS and HTML in HTML. It's
really a matter of preference.
重點是在本質上使用預編譯語言沒有什么錯誤。這純粹是喜好的問題。
Internal vs. External DSLs
Style encapsulation using Shadow DOM React instead has this, which
requires writing CSS in JavaScript. Not pretty.
內部與外部 DSLs 的對決
使用 Shadow DOM 封裝樣式,而 React 則使用這個解決方案
,需要把 CSS 寫進 JavaScript 里。不優雅。
Pretty or not, it is certainly expressive. JavaScript is a very powerful
language, much more powerful than CSS (even including any of CSS
preprocessors). It kind of depends on whether you prefer internal or
external DSLs for those sorts of things. Again, a matter of preference.
優雅與否,確有其意義。JavaScript 是一門非常強大的語言,遠甚于 CSS(算上
任何一種 CSS 預處理語言也是如此)。這個問題視乎你喜歡內部還是外部 DSLs
。還是的,偏好問題。
(Note: I was talking about the inline styles in React that was
referenced in the original question.)
(注意:我指的是原題中引用的在 React 中內聯樣式的寫法。)
Types of DSLs - explanation
Update: Reading my answer some time after writing it I think that I need
to explain what I mean here. DSL is a domain-specific language and it
can be either internal (using syntax of the host language like
JavaScript - like for example React without JSX, or like the inline
styles in React mentioned above) or it can be external (using
a different syntax than the host language
- like in this example would be inlining CSS (an external DLS) inside
JavaScript).
DSLs 的種類 - 解釋
更新:回答完以后又重讀一遍我的答案,我覺得有必要進一步解釋一下。DSL
指的是領域特定語言,它可以是內部的(使用宿主語言的語法,好比
JavaScript - 之于不使用 JSX 的 React,或者之前提到的在 React 中內聯樣式
的寫法),也可以是外部的(使用和宿主語言不同的語法 - 比如本例中在
JavaScript 內部編寫的內聯 CSS (屬于外部 DSLs))。
譯注:此處作者的回答似乎有些自相矛盾,因為在 React 中寫內聯 CSS 和在
JavaScript 中寫內聯 CSS 應該是一回事(都屬于使用外部 DSLs),畢竟
React 本身就是使用宿主語言(也就是 JavaScript - 這里屬于內部 DSLs)來
編寫的呀。
It can be confusing because some literature uses different terms than
"internal" and "external" to describe those kinds of DLSs. Sometimes
"embedded" is used instead of "internal" but the word "embedded" can
mean different things - for example Lua is described as "Lua: an
extensible embedded language" where embedded has nothing to do with
embedded (internal) DSL (in which sense it is quite the opposite - an
external DSL) but it means that it is embedded in the same sense
that, say, SQLite is an embedded database. There is even eLua where "e"
stands for "embedded" in a third sense - that it is meant for embedded
systems! That's why I don't like using the term "embedded DSL" because
things like eLua can be "DSLs" that are "embedded" in two different
senses while not being an "embedded DSL" at all!
某些資料里用不同的術語來描述上述的 DSLs 種類,這常常會令人混淆。有時候
“嵌入式”會用來指代“內部的”但是“嵌入式”也可以是別的意思——比方說 Lua 可
被描述為“Lua:一種可擴展的嵌入式語言”,在這里“嵌入式”和“內部 DSLs” 一
毛錢關系都沒有(更甚者完全相反,嵌入式是一種外部 DSL),它說的是和
“SQLite 是一種嵌入式數據庫”一個意思。甚至還有一種 eLua,它的 “e” 代
表另外一種“嵌入式”——嵌入式系統!這就是為什么我不喜歡使用術語“嵌入式 DSL”
的原因,像 eLua 可以是擁有兩種“嵌入式”含義的 DSLs,但卻和“嵌入式 DSLs”
沒有任何關聯。
To make things worse some projects introduce even more confusion to the
mix. Eg. Flatiron templates are describes as "DSL-free" while in fact it
is just a perfect example of an internal DSL with syntax like:
map.where('href').is('/').insert('newurl');
更糟的是一些項目引入的概念讓這潭濁水變得更混了。例如:Flatiron 模板自稱
“無 DSL”而實際上卻是一個完美的內部 DSL 的例子,其語法如下:
map.where('href').is('/').insert('newurl');
That having been said, when I wrote "JavaScript is a very powerful
language, much more powerful than CSS (even including any of CSS
preprocessors). It kind of depends on whether you prefer internal or
external DSLs for those sorts of things. Again, a matter of preference."
I was talking about those two scenarios:
如前所述,當我寫道“JavaScript 是一門非常強大的語言,遠甚于 CSS(算上
任何一種 CSS 預處理語言也是如此)。這個問題視乎你喜歡內部還是外部 DSLs
。還是的,偏好問題。”我所指的是如下兩個場景:
一:
/** @jsx React.DOM */
var colored = { color: myColor };
React.renderComponent(<div style={colored}>Hello World!</div>, mountNode);
二:
// SASS: .colored {
color: $my-color;
}// HTML: <div class="colored">Hello World!</div>
The first example uses what was
described in the question as: "writing CSS in JavaScript. Not pretty."
The second example uses Sass. While I agree that using JavaScript to
write CSS may not be pretty (for some definitions of "pretty") but there
is one advantage of doing it.
第一個例子如同原題所述:“在 JavaScript 里寫 CSS。不優雅。”第二個例子則
使用 Sass。盡管我也同意使用 JavaScript 寫 CSS 可能不太優雅(某種意義上的
“優雅”)然而這么做還是有一個優點的。
I can have variables and functions in Sass but are they lexically scoped
or dynamically scoped? Are they statically or dynamically typed?
Strongly or weakly? What about the numeric types? Type coersion? Which
values are truthy and which are falsy? Can I have higher-order
functions? Recursion? Tail calls? Lexical closures? Are they evaluated
in normal order or applicative order? Is there lazy or eager exaluation?
Are arguments to functions passed by value or by reference? Are they
mutable? Immutable? Persistent? What about objects? Classes? Prototypes?
Inheritance?
我可以在 Sass 里使用變量和函數,可它們是詞法作用域還是動態作用域的存在呢
?它們是靜態類型還是動態類型?數字類型如何?類型強制轉換又如何?哪些是直
真哪些又是假值?我能用高階函數嗎?遞歸呢?尾調用?詞法閉包?求值是順序還
是按應用序?是否可以使用惰性求值或主動求值?函數是按值傳參還是按引用傳參
?它們是可變的?不可變的?持久化的?還有對象呢?類呢?原型呢?繼承呢?
Those are not trivial questions and yet I have to know answers to them
if I want to understand Sass or Less code. I already know those
answers for JavaScript so it means that I already understand every
internal DSL (like the inline styles in React) on those very levels so
if I use React then I have to know only one set of answers to those
(and many similar) questions, while when I use for eg. Sass and
Handlebars then I have to know three sets of those answers and
understand their implications.
這些并非無礙痛癢的問題,如果我想要理解 Sass 或 Less 的代碼就必須知道上述
疑問的答案。我已經知道這些問題(以及許多類似問題)在 JavaScript 里的答案
了,也就意味著我已經理解每一種內部 DSLs 了,然而當我使用比方說 Sass
和 Handlebars 那么我得知道三套答案(包含 JS)同時還得理解它們的實現。
It's not to say that one way or the other is always better but every
time you introduce another language to the mix then you pay some price
that may not be as obvious at a first glance, and this price is
complexity.
這倒不是說某一種方式肯定會更好,但是每一次你引入另外一種語言就不得不花代
價(去學習和理解它),這種代價可能在一開始不是那么明顯并且會很復雜。
I hope I clarified what I originally meant a little bit.
希望我把我的本意解釋的比較清楚了。
Data binding
Two-way binding
數據綁定
雙向綁定
This is a really interesting subject and in fact also a matter of
preference. Two-way is not always better than one-way. It's a question
of how do you want to model mutable state in your application. I always
viewed two-way bindings as an idea somewhat contrary to the principles
of functional programming but functional programming is not the only
paradigm that works, some people prefer this kind of behavior and both
approaches seem to work pretty well in practice. If you're interested in
the details of the design decisions related to the modeling of the state
in React then watch the talk by Pete Hunt (linked to in the question)
and the talk by Tom Occhino and Jordan Walke who explain it very well
in my opinion.
這是很有意思的話題而且實際上還是偏好問題。雙向綁定不總是比單向綁定好。這
取決于你想要怎樣來模型化應用的可變狀態(也就是如何通過對象來描述可變狀態
,或者說為可變狀態建模)。我一直將雙向綁定視作對立于函數式編程的一種思路
,不過函數式編程也不是唯一可用的范式,一些人喜歡這樣的方式并且在實際中也
能很好地工作。如果你有興趣了解在 React 中關于狀態模型化背后的設計決策細
節的話,推薦你觀看 Pete Hunt 的演講 以及 Tom
Occhina 和 Jordan Walke 的演講,他倆把我的觀點解釋的非常精
彩。
Update: see also another talk by Pete Hunt: Be predictable, not correct:
functional DOM programming.
更新:同時看看 Pete Hunt 的另外一個演講:Be predictable, not
correct: functional DOM programming。
Native vs. VM
Native browser support (read "guaranteed to be faster")
原生對決 VM(虛擬機)
瀏覽器原生支持(暗示“確保更快”)
Now finally something that is not a matter of opinion.
終于到不是偏好問題的部分了。
Actually here it is exactly the other way around. Of course "native"
code can be written in C++ but what do you think the JavaScript engines
are written in?
好有一比亦具異曲同工之妙,“原生”代碼可以用 C++ 來寫,不過你覺得
JavaScript 引擎是用什么寫的呢?
譯注:此段不好直譯。作者的本意是瀏覽器的原生支持是用可以用 C++ 搞定,但是虛
擬機(JavaScript 的解釋器,比如 V8)也是用 C++ 寫的),所以不能因為虛
擬機不屬于瀏覽器原生支持的部分就認為它沒有原生支持“快”。
As a matter of fact the JavaScript engines are truly amazing in the
optimizations that they use today - and not only V8 any more, also
SpiderMonkey and even Chakra shines these days. And keep in mind that
with JIT compilers the code is not only as native as it can possibly be
but there are also run time optimization opportunities that are simply
impossible to do in any statically compiled code.
實際上今天的 JavaScript 引擎的性能優化做得異常出色——而且不僅僅是 V8,
還包括 SpiderMonkey 甚或 Chakra。要明白有了 JIT 編譯器,代碼(執行)不但
是無限逼近于原生而且還會有靜態編譯代碼所不具備的運行時優化。
When people think that JavaScript is slow, they usually mean JavaScript
that accesses the DOM. The DOM is slow. It is native, written in C++ and
yet it is slow as hell because of the complexity that it has to
implement.
人們所談論的“JavaScript 很慢”,實際上指的是訪問 DOM 的 時候,DOM 很慢
才是真的。即便它是原生的,使用 C++ 編寫,卻還是慢到令人發指,這是因為它
的實現非常復雜。
Open your console and write:
打開控制臺(瀏覽器開發者工具中的)然后執行:
console.dir(document.createElement('div'));
and see how many properties an empty div element that is not even
attached to the DOM has to implement. These are only the first level
properties that are "own properties" ie. not inherited from the
prototype chain:
看看一個空的 div
元素(并且還沒有插入到 DOM)有多少屬性要實現吧。這還
只是第一層的也就是“自有屬性”,沒包括自原型鏈繼承而來的。
align, onwaiting, onvolumechange, ontimeupdate, onsuspend, onsubmit,
onstalled, onshow, onselect, onseeking, onseeked, onscroll, onresize,
onreset, onratechange, onprogress, onplaying, onplay, onpause,
onmousewheel, onmouseup, onmouseover, onmouseout, onmousemove,
onmouseleave, onmouseenter, onmousedown, onloadstart,
onloadedmetadata, onloadeddata, onload, onkeyup, onkeypress,
onkeydown, oninvalid, oninput, onfocus, onerror, onended, onemptied,
ondurationchange, ondrop, ondragstart, ondragover, ondragleave,
ondragenter, ondragend, ondrag, ondblclick, oncuechange,
oncontextmenu, onclose, onclick, onchange, oncanplaythrough,
oncanplay, oncancel, onblur, onabort, spellcheck, isContentEditable,
contentEditable, outerText, innerText, accessKey, hidden,
webkitdropzone, draggable, tabIndex, dir, translate, lang, title,
childElementCount, lastElementChild, firstElementChild, children,
nextElementSibling, previousElementSibling, onwheel,
onwebkitfullscreenerror, onwebkitfullscreenchange, onselectstart,
onsearch, onpaste, oncut, oncopy, onbeforepaste, onbeforecut,
onbeforecopy, webkitShadowRoot, dataset, classList, className,
outerHTML, innerHTML, scrollHeight, scrollWidth, scrollTop,
scrollLeft, clientHeight, clientWidth, clientTop, clientLeft,
offsetParent, offsetHeight, offsetWidth, offsetTop, offsetLeft,
localName, prefix, namespaceURI, id, style, attributes, tagName,
parentElement, textContent, baseURI, ownerDocument, nextSibling,
previousSibling, lastChild, firstChild, childNodes, parentNode,
nodeType, nodeValue, nodeName
Many of them are actually nested objects - to see second level (own)
properties of an empty native div in your browser, see this fiddle.
它們中有許多實際上還是層疊的對象——要查看瀏覽器里一個空的原生 div
的
第二層(自有的)屬性,請見這個 fiddle。
I mean seriously, onvolumechange property on every single div node? Is
it a mistake? Nope, it's just a legacy DOM Level 0 traditional event
model version of one of the event handlers "that must be supported by
all HTML elements, as both content attributes and IDL attributes"
[emphasis added] in Section 6.1.6.2 of the HTML spec by W3C - no way
around it.
說真的,有必要讓每一個 div 節點都包括 onvolumechange
屬性嗎?這是個錯
誤?不是的,這只是老舊的 DOM Level 0 版本中傳統事件模型里的一個事件回調
,它“必須為所有的 HTML 元素所支持,既作為內容屬性也作為 IDL 屬性”(重
點強調),定義于 W3C 的 HTML 規范中的 Section 6.1.6.2,無可
回避。
Meanwhile, these are the first level properties of a fake-DOM div in
React:
與之對應的,以下是 React 中偽 DOM div 元素的第一層屬性列表:
props, _owner, _lifeCycleState, _pendingProps, _pendingCallbacks, _pendingOwner
Quite a difference, isn't it? In fact this is the entire object
serialized to JSON (LIVE DEMO), because hey you actually can serialize
it to JSON as it doesn't contain any circular references - something
unthinkable in the world of native DOM (where it would just throw an
exception):
大不一樣,不是嗎?實際上這是序列化為 JSON 的一整個對象(演示
),因此你完全可以把它序列化成 JSON 因為它不會包含循環引用,
這在原生 DOM 的世界里是不可想象的(那只會拋出異常)。
{
"props": {},
"_owner": null,
"_lifeCycleState": "UNMOUNTED",
"_pendingProps": null,
"_pendingCallbacks": null,
"_pendingOwner": null }
This is pretty much the main reason why React can be faster
than the native browser DOM - because it doesn't have to implement
this mess.
這就是 React 比原生瀏覽器 DOM 快的主要原因——它不用實現那些有的沒的
。
See this presentation by Steven Luscher to see what is faster: native
DOM written in C++ or a fake DOM written entirely in JavaScript. It's
a very fair and entertaining presentation.
看看這個 Steven Luscher 的演講 吧,看看哪個更快:用 C++
寫的原生 DOM 還是用 JavaScript 寫的偽 DOM。這是一個非常公平且有很歡樂的
演講。
To sum it up: features from Web Components like templates, data binding
or custom elements will have a lot of advantages over React but until
the document object model itself gets significantly simplified then
performance will not be one of them.
總結陳詞:WebComponenets 的特點如模板,數據綁定或自定義元素終會有很多勝
于 React 的優勢,然而性能絕非其一除非文檔對象模型能夠得到顯著地簡化。
來自:http://segmentfault.com/blog/nightire/1190000000753400