更新: 这个答案似乎很受欢迎,因此我花了一些时间对其进行清理,添加一些新信息并澄清了一些我认为不够清楚的内容。如果您认为其他任何需要澄清或更新的内容,请发表评论。
您的大部分顾虑实际上都是意见和个人喜好问题,但我会尽可能客观地回答:
本机与编译
用普通JavaScript编写JavaScript,用CSS编写CSS,用HTML编写HTML。
过去有很多人在争论是应该 手动编写本机 Assembly还是使用C之类的高级语言来使编译器为您生成Assembly代码。甚至在此之前,人们还是不信任汇编程序,而是更喜欢手工编写本机代码(我不是在开玩笑)。
同时,今天有很多人用Haml或Jade编写HTML ,用Sass或Less编写CSS 以及使用CoffeeScript或TypeScript编写JavaScript 。在那。有用。有些人喜欢它,有些则不喜欢。
关键是,不使用普通JavaScript编写JavaScript,不使用CSS编写CSS和不使用HTML编写HTML 根本上没有错误。这实际上是一个偏好问题。
内部与外部DSL
相反,使用Shadow DOM React进行样式封装则需要使用JavaScript编写CSS。不漂亮。
不管是否,它肯定是表达力。JavaScript是一种非常强大的语言,比CSS(甚至包括任何CSS预处理器)要强大得多。这取决于您是喜欢内部DSL还是外部DSL。同样,优先事项。
(注意:我是在谈论原始问题中引用的React中的内联样式。)
DSL类型-说明
更新:在写完答案后的一段时间里阅读了我的答案,我认为我需要在这里解释我的意思。DSL是一种特定于域的语言,它可以是内部的(使用像JavaScript这样的宿主语言的语法-例如,不带JSX的React,或者像上面提到的React中的内联样式),也可以是外部的(使用不同的语法)而不是宿主语言-在此示例中,将JavaScript内联CSS(外部DSL))。
这可能会造成混淆,因为某些文献使用不同于“内部”和“外部”的术语来描述这些类型的DSL。有时使用“嵌入式”代替“内部”,但“嵌入式”一词可能具有不同的含义-例如Lua 被描述为“ Lua:一种可扩展的嵌入式语言”,其中嵌入式与嵌入式(内部)DSL没有任何关系(在在某种意义上说,它是完全相反的-外部DSL),但它意味着它以与SQLite是嵌入式数据库相同的意义被嵌入。甚至在eLua中,“ e”在第三种意义上代表“嵌入式”-这是指嵌入式系统!这就是为什么我不喜欢使用“嵌入式DSL”一词的原因,因为诸如eLua之类的东西可以是从两种不同意义上“嵌入式”的“ DSL”,而根本不是“嵌入式DSL”!
更糟的是,某些项目使混合方案更加混乱。例如。熨斗模板被描述为“无DSL”,而实际上,它只是内部DSL的完美示例,其语法如下:map.where('href').is('/').insert('newurl');
话虽这么说,当我写到“ JavaScript是一种非常强大的语言,比CSS(甚至包括任何CSS预处理器)要强大得多。这取决于您是喜欢内部DSL还是外部DSL。优先事项。” 我在谈论这两种情况:
一:
/** @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>
第一个示例使用问题中描述的内容:“用JavaScript编写CSS。不漂亮。” 第二个示例使用Sass。虽然我同意使用JavaScript编写CSS可能不是很漂亮(对于“漂亮”的某些定义),但是这样做有一个好处。
我可以在Sass中使用变量和函数,但是它们是按词法作用域还是动态作用域?它们是静态类型还是动态类型?强还是弱?数字类型呢?类型强制?哪些价值观是真实的,哪些价值观是虚假的?我可以使用高阶函数吗?递归?尾声?词汇闭包?是按正常顺序还是适用顺序对它们进行评估?是否有懒惰或渴望的评估?函数的参数是通过值还是通过引用传递?他们易变吗?一成不变?坚持不懈?那对象呢?上课吗 原型?遗产?
这些不是琐碎的问题,但是如果我想了解Sass或Less代码,我必须知道它们的答案。我已经知道了JavaScript的那些答案,因此这意味着我已经在这些级别上理解了每个内部DSL(例如React中的内联样式),因此,如果我使用React,那么我只需要知道这些答案中的一组(以及许多类似的答案) )的问题,而当我用于例如 然后,Sass和Handlebars我必须知道三个答案,并理解它们的含义。
并不是说一种方法总是更好,但是每次您将另一种语言引入混合语言时,您都付出了一些乍一看可能并不明显的代价,而这种代价就是复杂性。
我希望我能澄清一下我的意思。
数据绑定
双向装订
这是一个非常有趣的话题,实际上也是一个优先事项。双向并不总是比单向更好。这是一个有关如何在应用程序中建模可变状态的问题。我一直将双向绑定视为某种有点违背函数式编程原理的想法,但是函数式编程并不是唯一可行的范例,有些人喜欢这种行为,并且两种方法在实践中似乎都很好用。如果您对与React中的状态建模相关的设计决策的细节感兴趣,请观看Pete Hunt的演讲(问题链接)以及Tom Occhino和Jordan Walke的演讲, 他们在本书中对此做了很好的解释。我的意见。
更新:另请参见Pete Hunt的另一篇演讲:可预测,不正确:功能DOM编程。
更新2:这是值得注意的是,许多开发商都主张对双向数据流或双向绑定,有人甚至称其为反模式。举个例子的助焊剂应用架构,明确避免了MVC模型(这被证明是难以为大型Facebook和Instagram的应用程序),取而代之的是严格的单向数据流(见黑客方式:重新思考Web应用程序开发在Facebook上通过交谈Tom Occhino,Jing Chen和Pete Hunt作了很好的介绍)。另外,对AngularJS的批评也很多 (松散地基于MVC模型的最流行的Web框架,以双向数据绑定而著称)包含针对该双向数据流的参数,请参见:
更新3:另外一个有趣的一篇文章,很好地解释了一些上面disscussed问题被解构ReactJS的流量-不使用MVC与ReactJS通过的Mikael Brassman,作者RefluxJS(单向数据流应用架构熔剂启发一个简单的库)。
更新4: Ember.js当前正在远离双向数据绑定,并且在将来的版本中,默认情况下它将是单向的。参见:灰烬的未来由斯特凡·彭纳从Embergarten研讨会在多伦多叨唠2014年11月15日。
更新5:另请参阅:Ember 2.0 RFC之路-Tom Dale在pull请求中的有趣讨论:
“当我们设计原始的模板层时,我们发现使所有数据绑定都是双向的不是很有害:如果不设置双向绑定,那实际上是单向绑定!
从那以后,我们意识到(在React的朋友的帮助下),组件希望能够将数据分发给他们的孩子,而不必警惕任何随意的突变。
此外,组件之间的通信通常最自然地表示为事件或回调。在Ember中这是可能的,但是双向数据绑定的优势常常使人们走上使用双向绑定作为通信渠道的道路。有经验的灰烬开发商不(通常)犯这样的错误,但它是一个容易做。” [强调]
本机与VM
本机浏览器支持(请阅读“保证更快”)
现在终于有了一些无关紧要的问题。
实际上,这里恰恰相反。当然,“本机”代码可以用C ++编写,但是您认为JavaScript引擎是用什么编写的?
实际上,JavaScript引擎在当今使用的优化中确实令人惊叹-这些天不仅是V8,而且SpiderMonkey甚至Chakra都闪闪发光。并且请记住,对于JIT编译器,代码不仅像它本来可能的本机那样原始,而且还存在运行时优化的机会,而这在任何静态编译的代码中都是不可能做到的。
当人们认为JavaScript速度很慢时,通常是指访问DOM的JavaScript。DOM很慢。它是本机的,用C ++编写,但是由于它必须实现的复杂性,它的速度实在太慢了。
打开控制台并输入:
console.dir(document.createElement('div'));
并查看div
甚至没有附加到DOM 的空元素必须实现多少个属性。这些只是第一级 属性,即“自身属性”。不是从原型链继承的:
对齐,等待,onvolumechange,ontimeupdate,onsuspend,onsubmit,安装,onshow,onselect,onseeking,onseeked,onscroll,onresize,onreset,onratechange,onprogress,onplaying,onplay,onpause,onmousewheel,onmouseup,onmouseover,onmouseout,onmous onmouseenter,onmousedown,onloadstart,onloadedmetadata,onloadeddata,onload,onkeyup,onkeypress,onkeydown,oninvalid,oninput,onfocus,onerror,oned,onemptied,ondurationchange,ondrop,ondragstart,ondragover,ondragleave,ondragenter,ondrague,onclickd, oncontextmenu,onclose,onclick,onchange,oncanplaythrough,oncanplay,oncancel,onblur,onabort,spellcheck,isContentEditable,contentEditable,outerText,innerText,accessKey,隐藏,webkitdropzone,draggable,tabIndex,dir,translate,lang,title,childElementCount,lastElementChild,firstElementChild,子项,nextElementSibling,previousElementSibling,onwheel,onwebkitfullscreenerror,onwebkitfullscreenchange,onselectstart,onsearch,onpaste,oncut,oncopy,onbeforepaste,onbeforecut,onbeforecopy,webkitShadowRoot,数据集,classList,className,outerHTML,innerHTML,scrollHeight,scrollLeft,scrollLeft clientHeight,clientWidth,clientTop,clientLeft,offsetParent,offsetHeight,offsetWidth,offsetTop,offsetLeft,localName,prefix,namespaceURI,id,样式,属性,tagName,parentElement,textContent,baseURI,ownerDocument,nextSibling,previousSibling,lastChild,firstChild,firstChild,childNodes, parentNode,nodeType,nodeValue,nodeNameoncopy,onbeforepaste,onbeforecut,onbeforecopy,webkitShadowRoot,数据集,classList,className,outerHTML,innerHTML,scrollHeight,scrollWidth,scrollTop,scrollLeft,clientHeight,clientWidth,clientTop,clientLeft,offsetParent,offsetParent,offsetHeight,offsetWidth,offsetTop,offsetLeft,localName,prefix, namespaceURI,id,样式,属性,tagName,parentElement,textContent,baseURI,ownerDocument,nextSibling,previousSibling,lastChild,firstChild,childNodes,parentNode,nodeType,nodeValue,nodeNameoncopy,onbeforepaste,onbeforecut,onbeforecopy,webkitShadowRoot,数据集,classList,className,outerHTML,innerHTML,scrollHeight,scrollWidth,scrollTop,scrollLeft,clientHeight,clientWidth,clientTop,clientLeft,offsetParent,offsetParent,offsetHeight,offsetWidth,offsetTop,offsetLeft,localName,prefix, namespaceURI,id,样式,属性,tagName,parentElement,textContent,baseURI,ownerDocument,nextSibling,previousSibling,lastChild,firstChild,childNodes,parentNode,nodeType,nodeValue,nodeNameparentElement,textContent,baseURI,ownerDocument,nextSibling,previousSibling,lastChild,firstChild,childNodes,parentNode,nodeType,nodeValue,nodeNameparentElement,textContent,baseURI,ownerDocument,nextSibling,previousSibling,lastChild,firstChild,childNodes,parentNode,nodeType,nodeValue,nodeName
其中许多实际上是嵌套对象-要div
在浏览器中查看空本机的第二级(自己)属性,请参阅此小提琴。
我是说认真的,每个div节点上都有onvolumechange属性吗?错了吗 不,这只是事件处理程序之一的旧式DOM Level 0传统事件模型版本,“ 必须受所有HTML元素支持,内容属性和IDL属性都应支持” [强调] HTML规范第6.1.6.2节由W3C-没办法解决。
同时,这些是div
React中伪DOM的第一级属性:
道具,_owner,_lifeCycleState,_pendingProps,_pendingCallbacks,_pendingOwner
完全不同,不是吗?实际上,这是将整个对象序列化为JSON(LIVE DEMO)的原因,因为您实际上可以 将其序列化为JSON,因为它不包含任何循环引用-在本机DOM领域中这是不可想象的(它只会抛出异常)):
{
"props": {},
"_owner": null,
"_lifeCycleState": "UNMOUNTED",
"_pendingProps": null,
"_pendingCallbacks": null,
"_pendingOwner": null
}
这几乎是React可以比本机浏览器DOM更快的主要原因-因为它不必实现这种混乱。
请参阅Steven Luscher的演示文稿,以了解更快的方法:用C ++编写的本机DOM或完全用JavaScript编写的伪DOM。这是一个非常公平和有趣的演示。
更新: 未来版本中的Ember.js将使用受React启发的虚拟DOM来提高性能。参见:灰烬的未来由斯特凡·彭纳从Embergarten研讨会在多伦多叨唠2014年11月15日。
概括起来:Web组件中的功能(例如模板,数据绑定或自定义元素)将比React具有很多优势,但是直到文档对象模型本身被大大简化之后,性能才不是其中之一。
更新资料
我发布此答案两个月后,这里有一些相关新闻。正如我刚刚在Twitter上所写的那样,尽管Wikipedia称 “ Atom基于Chromium并用C ++编写”,但GitHub用JavaScript编写的GitHub 最新版本的Atom文本编辑器仍使用Facebook的React获得更好的性能。本机C ++ DOM实现(请参阅《原子核》)和 由于它附带了自己的Web浏览器,因此可以保证对Web组件的支持。这只是一个现实世界项目的最新示例,该项目可以使用Web应用程序通常无法使用的任何其他类型的优化,但是即使Atom仍选择使用本身用JavaScript编写的React来实现最佳性能。并不是一开始就使用React构建的,因此这样做并不是一件小事。
更新2
有由托德·帕克一个有趣的对比使用WebPagetest比较的性能TodoMVC写成角,骨干的例子,灰烬,聚合物,CanJS,YUI,淘汰赛,反应,鞋带。这是我到目前为止看到的最客观的比较。这里重要的是,所有各个示例都是由所有这些框架的专家编写的,它们都可以在GitHub上获得,并且可以认为认为某些代码可以进行优化以提高运行速度的人可以对其进行改进。
更新3
未来版本中的Ember.js将包含此处讨论的许多React功能(包括虚拟DOM和单向数据绑定,仅举几例),这意味着起源于React的想法已经迁移到其他框架中。请参阅:通往Ember 2.0的RFC之路-Tom Dale(开始日期:2014-12-03)的拉取请求中的有趣讨论:“在Ember 2.0中,我们将采用“虚拟DOM”和数据流模型,其中包括来自React的最佳创意,并简化了组件之间的通信。”
同样,Angular.js 2.0也实现了此处讨论的许多概念。
更新4
我必须详细说明几个问题才能回答Igwe Kalu的评论:
“当React最终简化为普通JavaScript时,将React(JSX或编译输出)与普通JavaScript进行比较是不明智的。[...]可以将React用于DOM插入的任何策略应用于不使用React的情况。在考虑相关功能时,除了方便性之外,没有增加任何特殊的好处。” (完整评论在这里)
如果还不够清楚,我会在部分答案中比较直接在本机DOM(在浏览器中作为宿主对象实现)和React的假/虚拟DOM(在JavaScript中实现)上运行的性能。我要说明的一点是,用JavaScript实现的虚拟DOM 可以胜过用C ++实现的真实DOM,而不是 React不能胜过JavaScript(这显然是没有用的,因为它是用JavaScript编写的)。我的观点是,并不总是保证“本机” C ++代码比“非本机” JavaScript更快。用React来说明这一点只是一个例子。
但这句话引起了一个有趣的问题。从某种意义上说,您确实出于任何原因(例如性能,可移植性,功能)均不需要任何框架(React,Angular或jQuery),因为您始终可以重新创建框架为您所做的工作并重新发明轮子-如果您可以证明成本合理。
但是-戴维·史密斯很好地把它放在如何比较Web框架的性能时,错过了这一点:“当比较两个web框架,这个问题是不是可以我的应用程序快速与框架X.的问题是将我的应用程序框架,快速X。”
在我2011年的回答中:不使用jQuery的一些经验性技术原因是我解释了一个类似的问题,即没有jQuery之类的库就不可能编写可移植的DOM操作代码,但是人们很少这样做。
当使用编程语言,库或框架时,人们倾向于使用最方便或惯用的做事方式,而不是完美但不便的方式。好的框架的真正价值在于使原本很难做到的事情变得容易-秘诀就是使正确的事情变得方便。结果仍然具有与最简单的lambda演算形式或最原始的Turing机器一样完全相同的功能,但是某些概念的相对表现力意味着这些概念往往更容易或根本不易于表达。正确的解决方案不仅可行,而且已广泛实施。
更新5
反应+性能=?保罗·刘易斯(Paul Lewis)在2015年7月发表的一篇文章中展示了一个示例,其中针对无限数量的Flickr图片列表,React比手工编写的普通JavaScript慢,这在移动设备上尤其重要。此示例表明,每个人都应始终测试特定用例以及特定目标平台和设备的性能。
感谢凯文Lozandier为它带给我的注意。