Cập nhật: câu trả lời này có vẻ khá phổ biến vì vậy tôi đã dành chút thời gian để dọn dẹp nó một chút, thêm một số thông tin mới và làm rõ một vài điều mà tôi nghĩ là không đủ rõ ràng. Hãy bình luận nếu bạn nghĩ bất cứ điều gì khác cần làm rõ hoặc cập nhật.
Hầu hết các mối quan tâm của bạn thực sự là vấn đề về quan điểm và sở thích cá nhân nhưng tôi sẽ cố gắng trả lời một cách khách quan nhất có thể:
Bản địa so với Biên dịch
Viết JavaScript bằng vanilla JavaScript, viết CSS bằng CSS, viết HTML bằng HTML.
Trước đây, có những cuộc tranh luận sôi nổi về việc liệu người ta nên viết Hội bản gốc bằng tay hay sử dụng ngôn ngữ cấp cao hơn như C để làm cho trình biên dịch tạo mã hội cho bạn. Ngay cả trước đó, mọi người đã từ chối tin tưởng các nhà lắp ráp và thích viết mã máy bằng tay ( và tôi không nói đùa ).
Trong khi đó, hôm nay có rất nhiều người đã viết HTML trong Haml hoặc Jade , CSS trong Sass hoặc Less và JavaScript trong CoffeeScript hoặc đánh máy . Nó đây rồi. Nó hoạt động. Một số người thích nó, một số thì không.
Vấn đề là về cơ bản không có gì sai khi không viết JavaScript bằng vanilla JavaScript, CSS bằng CSS và HTML bằng HTML. Đó thực sự là một vấn đề ưu tiên.
DSL bên trong và bên ngoài
Thay vào đó, đóng gói kiểu bằng Shadow DOM React có cái này, đòi hỏi phải viết CSS bằng JavaScript. Không đẹp.
Khá hay không, nó chắc chắn là biểu cảm. JavaScript là một ngôn ngữ rất mạnh, mạnh hơn nhiều so với CSS (thậm chí bao gồm bất kỳ bộ tiền xử lý CSS nào). Nó phụ thuộc vào việc bạn thích DSL bên trong hay bên ngoài cho những thứ đó. Một lần nữa, một vấn đề ưu tiên.
(Lưu ý: Tôi đã nói về các kiểu nội tuyến trong React được tham chiếu trong câu hỏi ban đầu.)
Các loại DSL - giải thích
Cập nhật: Đọc câu trả lời của tôi một thời gian sau khi viết nó tôi nghĩ rằng tôi cần phải giải thích những gì tôi muốn nói ở đây. DSL là ngôn ngữ dành riêng cho miền và nó có thể là nội bộ (sử dụng cú pháp của ngôn ngữ máy chủ như JavaScript - ví dụ như React without JSX hoặc như các kiểu nội tuyến trong React đã đề cập ở trên) hoặc có thể là bên ngoài (sử dụng một cú pháp khác hơn ngôn ngữ máy chủ - như trong ví dụ này sẽ nội tuyến CSS (DSL bên ngoài) bên trong JavaScript).
Nó có thể gây nhầm lẫn bởi vì một số tài liệu sử dụng các thuật ngữ khác với "bên trong" và "bên ngoài" để mô tả các loại DSL đó. Đôi khi "nhúng" được sử dụng thay vì "nội bộ" nhưng từ "được nhúng" có thể có nghĩa khác nhau - ví dụ Lua được mô tả là "Lua: một ngôn ngữ nhúng có thể mở rộng" trong đó nhúng không liên quan gì đến DSL (bên trong) được nhúng có nghĩa là nó hoàn toàn ngược lại - DSL bên ngoài) nhưng điều đó có nghĩa là nó được nhúng theo cùng nghĩa, giả sử, SQLite là một cơ sở dữ liệu nhúng. Thậm chí còn có eLua trong đó "e" là viết tắt của "nhúng" theo nghĩa thứ ba - rằng nó có nghĩa là cho các hệ thống nhúng! Đó là lý do tại sao tôi không thích sử dụng thuật ngữ "DSL nhúng" bởi vì những thứ như eLua có thể là "DSL" được "nhúng" theo hai nghĩa khác nhau trong khi hoàn toàn không phải là "DSL nhúng"!
Để làm cho mọi thứ tồi tệ hơn, một số dự án giới thiệu thêm sự nhầm lẫn cho hỗn hợp. Ví dụ. Các mẫu Flatiron được mô tả là "Không có DSL" trong khi thực tế nó chỉ là một ví dụ hoàn hảo về DSL bên trong với cú pháp như:map.where('href').is('/').insert('newurl');
Điều đó đã được nói, khi tôi viết "JavaScript là một ngôn ngữ rất mạnh, mạnh hơn nhiều so với CSS (thậm chí bao gồm bất kỳ bộ tiền xử lý CSS nào). Điều đó phụ thuộc vào việc bạn thích DSL bên trong hay bên ngoài cho những thứ đó. Một lần nữa, một vấn đề ưu tiên. " Tôi đã nói về hai kịch bản:
Một:
/** @jsx React.DOM */
var colored = {
color: myColor
};
React.renderComponent(<div style={colored}>Hello World!</div>, mountNode);
Hai:
// SASS:
.colored {
color: $my-color;
}
// HTML:
<div class="colored">Hello World!</div>
Ví dụ đầu tiên sử dụng những gì được mô tả trong câu hỏi là: "viết CSS bằng JavaScript. Không đẹp." Ví dụ thứ hai sử dụng Sass. Mặc dù tôi đồng ý rằng việc sử dụng JavaScript để viết CSS có thể không đẹp (đối với một số định nghĩa "đẹp") nhưng có một lợi thế khi thực hiện.
Tôi có thể có các biến và hàm trong Sass nhưng chúng có phạm vi từ vựng hoặc phạm vi động? Chúng được gõ tĩnh hay động? Mạnh hay yếu? Còn các loại số thì sao? Kiểu coersion? Những giá trị nào là trung thực và đó là giả? Tôi có thể có chức năng bậc cao hơn? Đệ quy? Đuôi gọi? Đóng cửa từ vựng? Họ được đánh giá theo thứ tự bình thường hoặc thứ tự áp dụng? Có đánh giá lười biếng hay háo hức? Là đối số cho các hàm được truyền theo giá trị hoặc bằng tham chiếu? Họ có thể đột biến? Bất biến? Kiên trì? Còn đối tượng thì sao? Các lớp học? Nguyên mẫu? Di sản?
Đó không phải là những câu hỏi tầm thường và tôi phải biết câu trả lời cho họ nếu tôi muốn hiểu mã Sass hoặc Ít hơn. Tôi đã biết những câu trả lời đó cho JavaScript, điều đó có nghĩa là tôi đã hiểu mọi DSL bên trong (như kiểu nội tuyến trong React) ở những cấp độ đó, vì vậy nếu tôi sử dụng React thì tôi chỉ phải biết một bộ câu trả lời cho những câu trả lời đó (và nhiều câu tương tự ) câu hỏi, trong khi khi tôi sử dụng ví dụ. Sass và Tay cầm sau đó tôi phải biết ba bộ câu trả lời đó và hiểu ý nghĩa của chúng.
Không phải nói rằng cách này hay cách khác luôn tốt hơn nhưng mỗi khi bạn giới thiệu một ngôn ngữ khác cho hỗn hợp thì bạn phải trả một số giá có thể không rõ ràng ngay từ cái nhìn đầu tiên và giá này rất phức tạp.
Tôi hy vọng tôi đã làm rõ những gì tôi có nghĩa ban đầu một chút.
Ràng buộc dữ liệu
Ràng buộc hai chiều
Đây là một chủ đề thực sự thú vị và trên thực tế cũng là một vấn đề ưu tiên. Hai chiều không phải lúc nào cũng tốt hơn một chiều. Đó là một câu hỏi về cách bạn muốn mô hình hóa trạng thái có thể thay đổi trong ứng dụng của bạn. Tôi luôn xem các ràng buộc hai chiều là một ý tưởng hơi trái với các nguyên tắc của lập trình chức năng nhưng lập trình chức năng không phải là mô hình duy nhất hoạt động, một số người thích loại hành vi này và cả hai cách tiếp cận dường như hoạt động khá tốt trong thực tế. Nếu bạn quan tâm đến các chi tiết của các quyết định thiết kế liên quan đến mô hình hóa trạng thái trong React thì hãy xem cuộc nói chuyện của Pete Hunt (liên kết với câu hỏi) và cuộc nói chuyện của Tom Occhino và Jordan Walke , người giải thích rất rõ về nó quan điểm của tôi.
Cập nhật: Xem thêm một cuộc nói chuyện khác của Pete Hunt: Có thể dự đoán được, không chính xác: lập trình DOM chức năng .
Cập nhật 2: Nó đáng chú ý là nhiều nhà phát triển đang tranh cãi chống lại luồng dữ liệu hai chiều, hoặc ràng buộc hai chiều, một số thậm chí gọi nó là một chất chống mẫu. Hãy ví dụ như Flux kiến trúc ứng dụng mà tránh được một cách rõ ràng MVC model (được chứng minh là khó có thể mở rộng quy mô cho Facebook lớn và các ứng dụng Instagram) trong lợi của một luồng dữ liệu đúng theo một hướng (xem Way Hacker: Suy nghĩ lại Web Phát triển ứng dụng ở Facebook nói chuyện bằng Tom Occhino, Jing Chen và Pete Hunt cho một lời giới thiệu tốt). Ngoài ra, rất nhiều chỉ trích chống lại AngularJS (khung Web phổ biến nhất dựa trên mô hình MVC, được biết đến với ràng buộc dữ liệu hai chiều) bao gồm các đối số chống lại luồng dữ liệu hai chiều đó, xem:
Cập nhật 3: Một bài viết thú vị khác giải thích độc đáo một số vấn đề được giải thích ở trên là Giải cấu trúc Flux của ReactJS - Không sử dụng MVC với ReactJS của Mikael Brassman, tác giả của RefluxJS (một thư viện đơn giản cho kiến trúc ứng dụng luồng dữ liệu đơn hướng được lấy cảm hứng từ Flux).
Cập nhật 4: Ember.js hiện đang rời khỏi ràng buộc dữ liệu hai chiều và trong các phiên bản trong tương lai, nó sẽ là một chiều theo mặc định. Xem: Cuộc nói chuyện về Tương lai của Ember của Stefan Penner từ Hội nghị chuyên đề Embergarten ở Toronto vào ngày 15 tháng 11 năm 2014.
Cập nhật 5: Xem thêm: Đường đến Ember 2.0 RFC - cuộc thảo luận thú vị trong yêu cầu kéo của Tom Dale :
"Khi chúng tôi thiết kế lớp tạo khuôn ban đầu, chúng tôi đã hình dung rằng việc tạo tất cả các ràng buộc dữ liệu hai chiều sẽ không có hại: nếu bạn không đặt ràng buộc hai chiều, thì đó là ràng buộc một chiều trên thực tế!
Kể từ đó, chúng tôi đã nhận ra (với một số trợ giúp từ bạn bè của chúng tôi tại React), rằng các thành phần muốn có thể cung cấp dữ liệu cho con cái của chúng mà không cần phải cảnh giác với các đột biến bướng bỉnh.
Ngoài ra, giao tiếp giữa các thành phần thường được thể hiện một cách tự nhiên nhất dưới dạng sự kiện hoặc cuộc gọi lại . Điều này có thể xảy ra ở Ember, nhưng sự thống trị của các ràng buộc dữ liệu hai chiều thường dẫn mọi người xuống một con đường sử dụng các ràng buộc hai chiều như một kênh liên lạc . Các nhà phát triển Ember có kinh nghiệm không (thường) mắc lỗi này, nhưng đó là một lỗi dễ mắc phải. " [Nhấn mạnh thêm]
Bản địa so với VM
Hỗ trợ trình duyệt riêng (đọc "đảm bảo sẽ nhanh hơn")
Bây giờ cuối cùng một cái gì đó không phải là một vấn đề quan điểm.
Trên thực tế ở đây nó chính xác là cách khác. Tất nhiên mã "gốc" có thể được viết bằng C ++ nhưng bạn nghĩ công cụ JavaScript được viết bằng gì?
Thực tế, các công cụ JavaScript thực sự tuyệt vời trong các tối ưu hóa mà chúng sử dụng ngày nay - và không chỉ V8 nữa, mà cả SpiderMonkey và thậm chí cả Chakra cũng tỏa sáng trong những ngày này. Và hãy nhớ rằng với trình biên dịch JIT, mã không chỉ có nguồn gốc như nó có thể có mà còn có các cơ hội tối ưu hóa thời gian chạy mà đơn giản là không thể thực hiện trong bất kỳ mã được biên dịch tĩnh nào.
Khi mọi người nghĩ rằng JavaScript chậm, họ thường có nghĩa là JavaScript truy cập DOM. DOM chậm. Nó là bản địa, được viết bằng C ++ và nó chậm như địa ngục vì sự phức tạp mà nó phải thực hiện.
Mở bàn điều khiển của bạn và viết:
console.dir(document.createElement('div'));
và xem có bao nhiêu thuộc tính mà một div
phần tử trống thậm chí không được gắn vào DOM phải thực hiện. Đây chỉ là các thuộc tính cấp đầu tiên là "thuộc tính riêng" tức là. không được kế thừa từ chuỗi nguyên mẫu:
align, onwaiting, onvolumechange, ontimeupdate, onuspend onmouseenter, onmousedown, onloadouse, onloadedart oncontextmenu, onclose, onclick, onchange, oncanplay phiên bản, oncanplay, oncattery, onblur, onabort, kiểm tra chính tả, isContentEditable, contentEditable, outsText, InternalText, accessKey, hidden, webkitdropzone, draggable, tabFirstEuityChild, trẻ em clientHeight, clientWidth, clientTop, clientLeft, offsetParent, offsetHeight, offsetWidth, offsetTopent, offsetLeight, offsetWidth, offsetTop, offsetLeft, localName, tiền tố, tên không gian, chữ cái, thuộc tính, tên miền, tiền tố, tên miền ParentNode, nodeType, nodeValue, nodeNameoncopy, onb Beforepaste, onbeforecut, onbeforecopy, webkitShadowRoot, tập dữ liệu, classList, className, outsHTML, trong namepaceURI, id, style, thuộc tính, tagName, ParentEuity, textContent, baseURI, ownDocument, nextSibling, trướcSibling, lastChild, FirstChild, childNodes, ParentNode, nodeType, nodeValue, nodeNameoncopy, onb Beforepaste, onbeforecut, onbeforecopy, webkitShadowRoot, tập dữ liệu, classList, className, outsHTML, trong namepaceURI, id, style, thuộc tính, tagName, ParentEuity, textContent, baseURI, ownDocument, nextSibling, trướcSibling, lastChild, FirstChild, childNodes, ParentNode, nodeType, nodeValue, nodeNameParentEuity, textContent, baseURI, ownDocument, nextSibling, trướcSibling, lastChild, firstChild, childNodes, ParentNode, nodeType, nodeValue, nodeNameParentEuity, textContent, baseURI, ownDocument, nextSibling, trướcSibling, lastChild, firstChild, childNodes, ParentNode, nodeType, nodeValue, nodeName
Nhiều trong số chúng thực sự là các đối tượng lồng nhau - để xem các thuộc tính cấp hai (sở hữu) của một nguồn gốc trống div
trong trình duyệt của bạn, hãy xem câu đố này .
Ý tôi là nghiêm túc, thuộc tính onvolumechange trên mỗi nút div duy nhất? Có phải là một sai lầm? Không, đây chỉ là phiên bản mô hình sự kiện truyền thống DOM Cấp 0 của một trong những trình xử lý sự kiện " phải được hỗ trợ bởi tất cả các yếu tố HTML , vì cả thuộc tính nội dung và thuộc tính IDL" [nhấn mạnh thêm] trong Phần 6.1.6.2 của thông số HTML bởi W3C - không có cách nào xung quanh nó.
Trong khi đó, đây là các thuộc tính cấp đầu tiên của DOM giả div
trong React:
đạo cụ, _owner, _lifeCyclState, _pendingProps, _pendingCallbacks, _pendingOwner
Hoàn toàn khác biệt phải không? Trên thực tế, đây là toàn bộ đối tượng được tuần tự hóa thành JSON ( LIVE DEMO ), bởi vì bạn thực sự có thể tuần tự hóa nó thành JSON vì nó không chứa bất kỳ tham chiếu vòng tròn nào - một điều không thể tưởng tượng được trong thế giới của DOM gốc ( nơi nó sẽ ném ngoại lệ ):
{
"props": {},
"_owner": null,
"_lifeCycleState": "UNMOUNTED",
"_pendingProps": null,
"_pendingCallbacks": null,
"_pendingOwner": null
}
Đây là lý do chính khiến React có thể nhanh hơn DOM trình duyệt gốc - bởi vì nó không phải thực hiện mớ hỗn độn này .
Xem bản trình bày này của Steven Luscher để xem cái gì nhanh hơn: DOM gốc được viết bằng C ++ hoặc DOM giả được viết hoàn toàn bằng JavaScript. Đó là một bài thuyết trình rất công bằng và thú vị.
Cập nhật: Ember.js trong các phiên bản trong tương lai sẽ sử dụng một DOM ảo được lấy cảm hứng rất nhiều từ React để cải thiện độ hoàn hảo. Xem: Cuộc nói chuyện về Tương lai của Ember của Stefan Penner từ Hội nghị chuyên đề Embergarten ở Toronto vào ngày 15 tháng 11 năm 2014.
Tóm lại: các tính năng từ Thành phần Web như mẫu, ràng buộc dữ liệu hoặc các yếu tố tùy chỉnh sẽ có rất nhiều lợi thế so với React nhưng cho đến khi chính mô hình đối tượng tài liệu được đơn giản hóa đáng kể thì hiệu suất sẽ không phải là một trong số chúng.
Cập nhật
Hai tháng sau khi tôi đăng câu trả lời này, có một số tin tức có liên quan ở đây. Như tôi vừa viết trên Twitter , phiên bản mới nhất của trình soạn thảo văn bản Atom do GitHub viết bằng JavaScript sử dụng React của Facebook để có hiệu suất tốt hơn mặc dù theo Wikipedia "Atom dựa trên Chromium và được viết bằng C ++" nên nó có toàn quyền kiểm soát việc triển khai C ++ DOM gốc (xem Hạt nhân nguyên tử ) và được đảm bảo có hỗ trợ cho các Thành phần Web vì nó có trình duyệt web riêng. Đây chỉ là một ví dụ gần đây về một dự án trong thế giới thực, có thể sử dụng bất kỳ loại tối ưu hóa nào khác thường không có sẵn cho các ứng dụng Web và nó đã chọn sử dụng React được viết bằng JavaScript, để đạt được hiệu suất tốt nhất, ngay cả khi Atom đã không được xây dựng với React để bắt đầu, vì vậy thực hiện nó không phải là một thay đổi nhỏ.
Cập nhật 2
Có một so sánh thú vị của Todd Parker khi sử dụng WebPagetest để so sánh hiệu suất của các ví dụ TodoMVC được viết bằng Angular, Backbone, Ember, Polymer, CanJS, YUI, Knockout, React và Shoestring. Đây là so sánh khách quan nhất mà tôi đã thấy cho đến nay. Điều quan trọng ở đây là tất cả các ví dụ tương ứng được viết bởi các chuyên gia trong tất cả các khung đó, chúng đều có sẵn trên GitHub và có thể được cải thiện bởi bất kỳ ai nghĩ rằng một số mã có thể được tối ưu hóa để chạy nhanh hơn.
Cập nhật 3
Ember.js trong các phiên bản trong tương lai sẽ bao gồm một số tính năng của React được thảo luận ở đây (bao gồm cả DOM ảo và ràng buộc dữ liệu một chiều, chỉ đặt tên cho một số), có nghĩa là các ý tưởng bắt nguồn từ React đã được chuyển sang các khung khác. Xem: Đường đến Ember 2.0 RFC - cuộc thảo luận thú vị trong yêu cầu kéo của Tom Dale (Ngày bắt đầu: 2014-12-03): "Trong Ember 2.0, chúng tôi sẽ áp dụng mô hình" DOM ảo "và mô hình luồng dữ liệu bao trùm ý tưởng tốt nhất từ React và đơn giản hóa giao tiếp giữa các thành phần. "
Đồng thời, Angular.js 2.0 đang triển khai rất nhiều khái niệm được thảo luận ở đây.
Cập nhật 4
Tôi phải giải thích một vài vấn đề để trả lời nhận xét này của Igwe Kalu:
"Không thể so sánh React (JSX hoặc đầu ra biên dịch) với JavaScript đơn giản, khi React cuối cùng giảm xuống JavaScript đơn giản. [...] Dù sử dụng chiến lược nào thì React sử dụng để chèn DOM mà không cần sử dụng React. không thêm bất kỳ lợi ích đặc biệt nào khi xem xét tính năng được đề cập ngoài sự tiện lợi. " (bình luận đầy đủ ở đây )
Trong trường hợp chưa đủ rõ ràng, một phần trong câu trả lời của tôi, tôi đang so sánh hiệu suất hoạt động trực tiếp trên DOM gốc (được triển khai dưới dạng đối tượng máy chủ trong trình duyệt) so với DOM giả / ảo của React (được triển khai bằng JavaScript). Điểm tôi đang cố gắng đưa ra là DOM ảo được triển khai trong JavaScript có thể vượt trội hơn DOM thực được triển khai trong C ++ và không phải React có thể vượt trội hơn JavaScript (điều này rõ ràng sẽ không có ý nghĩa gì vì nó được viết bằng JavaScript). Quan điểm của tôi là mã C ++ "bản địa" không phải lúc nào cũng được đảm bảo nhanh hơn JavaScript "không bản địa". Sử dụng React để minh họa điểm đó chỉ là một ví dụ.
Nhưng bình luận này đã chạm vào một vấn đề thú vị. Theo một nghĩa nào đó, đúng là bạn không cần bất kỳ khung nào (React, Angular hoặc jQuery) vì bất kỳ lý do gì (như hiệu suất, tính di động, tính năng) bởi vì bạn luôn có thể tạo lại những gì khung làm cho bạn và phát minh lại bánh xe - nếu bạn có thể biện minh cho chi phí, đó là.
Nhưng - như Dave Smith độc đáo đặt nó trong Làm thế nào để bỏ lỡ thời điểm khi so sánh hiệu suất khuôn khổ web : "Khi so sánh hai khuôn khổ web, vấn đề không phải là có thể ứng dụng của tôi được nhanh chóng với khuôn khổ X. Câu hỏi đặt ra là sẽ ứng dụng của tôi được nhanh chóng với khuôn khổ X. "
Trong câu trả lời năm 2011 của tôi về: một số lý do kỹ thuật thực nghiệm không sử dụng jQuery Tôi giải thích một vấn đề tương tự, đó là không thể viết mã thao tác DOM di động mà không có thư viện như jQuery, nhưng mọi người hiếm khi làm như vậy.
Khi sử dụng ngôn ngữ lập trình, thư viện hoặc khung, mọi người có xu hướng sử dụng các cách làm việc thuận tiện hoặc thành ngữ nhất, không phải là cách hoàn hảo nhưng bất tiện. Giá trị thực sự của các khung công tác tốt là làm cho dễ dàng những gì sẽ khó thực hiện - và bí mật là làm cho mọi thứ trở nên thuận tiện. Kết quả vẫn có sức mạnh chính xác theo ý của bạn như dạng tính toán lambda đơn giản nhất hoặc máy Turing nguyên thủy nhất, nhưng tính biểu cảm tương đối của các khái niệm nhất định có nghĩa là những khái niệm đó có xu hướng được thể hiện dễ dàng hơn hoặc hoàn toàn, và rằng các giải pháp đúng đắn không chỉ có thể mà còn thực sự được triển khai rộng rãi.
Cập nhật 5
Phản ứng + Hiệu suất =? bài viết của Paul Lewis từ tháng 7 năm 2015 cho thấy một ví dụ trong đó React chậm hơn vanilla JavaScript được viết bằng tay cho một danh sách vô hạn các hình ảnh Flickr, đặc biệt quan trọng trên thiết bị di động. Ví dụ này cho thấy mọi người phải luôn kiểm tra hiệu năng cho trường hợp sử dụng cụ thể và các nền tảng và thiết bị đích cụ thể.
Nhờ Kevin Lozandier cho đưa nó đến sự chú ý của tôi .