diff --git a/2012.html b/2012.html index e3ecca9..d8515cc 100644 --- a/2012.html +++ b/2012.html @@ -8,7 +8,7 @@ - + + + + +
+ + +
+

Archive for + + 2015 + +

+ +

Page 1 of 1

+ + + + + + + +
+
+ + + + + + + \ No newline at end of file diff --git a/2015/01.html b/2015/01.html new file mode 100644 index 0000000..5e6b567 --- /dev/null +++ b/2015/01.html @@ -0,0 +1,155 @@ + + + + + + 刘旸 Zation + + + + + + + + + +
+ + +
+

Archive for + + Jan 2015 + +

+ +

Page 1 of 1

+ + + + + + + +
+
+ + + + + + + \ No newline at end of file diff --git a/2015/01/13.html b/2015/01/13.html new file mode 100644 index 0000000..24d6cbf --- /dev/null +++ b/2015/01/13.html @@ -0,0 +1,153 @@ + + + + + + 刘旸 Zation + + + + + + + + + +
+ + +
+

Archive for + + Jan 13 2015 + +

+ +

Page 1 of 1

+ + + + + + + +
+
+ + + + + + + \ No newline at end of file diff --git a/2015/01/13/replace_backbonejs_view_with_reactj_1.html b/2015/01/13/replace_backbonejs_view_with_reactj_1.html new file mode 100644 index 0000000..df8b615 --- /dev/null +++ b/2015/01/13/replace_backbonejs_view_with_reactj_1.html @@ -0,0 +1,291 @@ + + + + + + 刘旸 Zation + + + + + + + + + +
+ + +
+
+
+

+ 用React.js替换Backbone.js的View(一) +

+ +

——Backbone.js View的陷阱以及React.js的优点

+ +
+ + +
+

最近终于找到时间,学习了一下Facebook出品的React.js,发现虽然没有很深的体会到性能上的好处,但是这种编程方式带来的好处确实是很大的。这里我准备跟Backbone.js的View做一下对比,同时下一篇文章中提供一个示例说明一下如何用React.js替换Backbone.js的View。

+ +

Backbone.js中View的陷阱

+ +

这里用陷阱这个词,是因为下面这些其实并不是Backbone.js本身所引发的问题,但却是使用Backbone.js的View时,常常会犯的错误。

+ +

绑定的事件没有解绑而造成的内存泄露

+ +

对于某个View,如果在它内部绑定事件的方式不正确,会造成remove的时候没有解绑,最后造成内存泄露。例如:

+
var SomeModelView = Backbone.View.extend({
+    initialize: function() {
+        this.model.on('change', this.render, this);
+    },
+    render: function() {
+        // render a template
+    }
+});
+
+

有两种方式可以解决这个问题,第一个是使用listenTo

+
var SomeModelView = Backbone.View.extend({
+    initialize: function() {
+        this.listenTo(this.model, 'change', this.render);
+    },
+    render: function() {
+        // render a template
+    }
+});
+
+

第二个是显式的在close方法中解绑:

+
var SomeModelView = Backbone.View.extend({
+    initialize: function() {
+        this.model.on('change', this.render, this);
+    },
+    close: function() {
+        this.model.off('change', this.render);
+    },
+    render: function() {
+        // render a template
+    }
+});
+
+

注:如果没有特殊情况,都推荐使用第一种解决方案。

+ +

其实以上的问题还不是真正的麻烦,真正的麻烦在于所有的View都需要以调用remove的方式来删除,否则不仅是model或collection上面的事件无法解绑,DOM事件都会没有解绑,从而造成事件重复绑定或内存泄露的问题。而对于一个Backbone.js的应用来说,一个View有十多个内嵌了N层的SubView是很正常的情况,如何保证这些SubView都被remove掉了是非常大的问题,例如在切换页面的时候,需要首先删除一个页面级别的View再插入另一个。这里有一篇文章详细叙述了如何解决这个问题,我这里就不复述了。

+ +

DOM的render可能效率很低

+ +

如果多次使用append来插入SubView,由于每次都会导致浏览器重新计算Element的位置和大小,所以可能效率会很低,例如:

+
var SomeCollectionView = Backbone.View.extend({
+    initialize: function() {
+        var self = this;
+        this._views = [];
+        // create a sub view for every model in the collection
+        this.collection.each(function(model) {
+            self._views.push(new SomeModelView({
+                model: model
+            }));
+        });
+    },
+    render: function() {
+        var self = this;
+        this.$el.empty();
+        // render each subview, appending to our root element
+        _.each(this._views, function(subview) {
+            self.$el.append(subview.render().el);
+        });
+    }
+});
+
+

这个时候我们可以使用documentFragment来在内存中先把我们要插入的DOM组织好,再一次性的完成append操作:

+
render: function() {
+    this.$el.empty();
+    var container = document.createDocumentFragment();
+    // render each subview, appending to our root element
+    _.each(this._views, function(subview) {
+        container.appendChild(subview.render().el)
+    });
+    this.$el.append(container);
+}
+
+

看起来这也不是真正麻烦的问题,至少我们还有解决方案,但是如果是一个大量数据的collection需要render,那么就需要我们自己在render的时候做增量式的操作,否则效率无论如何都提高不起来,这往往在老的浏览器或者移动端是无法接受的。

+ +

React.js的优点

+ +

如果我们有一个跟Backbone.js同样轻量级,并且又自己解决了以上问题的框架,我们为什么不试试看呢?

+ +

Virtual DOM

+ +

是的,所有的问题都可以使用一个Virtual DOM来解决。这个Virtual DOM由React.js来维护,每次对于DOM的修改都是增量式的,效率会非常高。并且对于删除的DOM,它上面的DOM事件会自动删除,不需要我们显式的调用一次,并且对于DOM的删除操作不需要显示的调用remove,一切有React.js来搞定。

+ +

这样就避免了很多内存泄露的可能性,也大大提高了性能。也就是同时解决了之前的两个问题。

+ +

Template与View不再分离

+ +

在大多数人的观念中,Template和View是完全不同的两种事物:一个是显示的内容,一个是显示的逻辑;一个是HTML,一个是JS。但是我们在实际开发的过程中,往往是开了两个窗口,左边是Template,右边是View,我们会非常欢快的在两边频繁切换。我们会做这样的操作,就是因为View的逻辑,就是Template的逻辑;Template的内容,其实也是View的内容。这两者没有大家想想的那么大的区别,分开以后反而是导致了代码不易读的问题,我常常会为了了解Template中的一个显示是如何得到的,而在各种View中找半天。

+ +

Template与View的合并带来了代码可读性的提高,但是可能也有人会觉得把这两种合并到一起会让代码变得更加臃肿。这里就要提到React.js的Component这个概念了,有了Component的细化,其实每一个Component的代码是很独立,并且也很少的。

+ +

Component

+ +

Compnent这个概念的出现,就是为了使得页面组件可以更加容易的被重用。在React.js给出的官方示例中,我们可以看到Component的粒度可以非常细,同时可以利用Props来保证Component之间的解耦。

+ +

总结

+ +

有了Virtual DOM,JSX(合并Template与View),Component这三大利器,React.js现在已经成为了Template和View这一层的不二框架了。再加上它本身的轻量级,还有Facebook来维护,采用它的风险可以说非常低。由于以上种种原因,是的一个两年不到的项目,在Github上有了13,120的Star数量,社区也提供了相当的热情

+ +

一些资料

+ +

下面是一些关于React.js的资料:

+ + + +
+
+
+
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/2015/01/15.html b/2015/01/15.html new file mode 100644 index 0000000..98a736f --- /dev/null +++ b/2015/01/15.html @@ -0,0 +1,153 @@ + + + + + + 刘旸 Zation + + + + + + + + + +
+ + +
+

Archive for + + Jan 15 2015 + +

+ +

Page 1 of 1

+ + + + + + + +
+
+ + + + + + + \ No newline at end of file diff --git a/2015/01/15/replace_backbonejs_view_with_reactj_2.html b/2015/01/15/replace_backbonejs_view_with_reactj_2.html new file mode 100644 index 0000000..a28afad --- /dev/null +++ b/2015/01/15/replace_backbonejs_view_with_reactj_2.html @@ -0,0 +1,477 @@ + + + + + + 刘旸 Zation + + + + + + + + + +
+ + +
+
+
+

+ 用React.js替换Backbone.js的View(二) +

+ +

——Todo MVC示例

+ +
+ + +
+

Backbone.js和React.js在设计思想上都借鉴了Reactive Programming,即:当Model修改时,这种变更可以反向传播到View,使得View同时被更改,也就是双向绑定。但Backbone.js需要你自己来写如何修改View,而在React.js中,你只需要关心如何根据Model来显示View,如何修改可以完全交给React.js。也就是说他们都在做一种简化,而React.js做的更加彻底,这也是它的核心思想和优点。

+ +

真正的学习还是需要写代码,所以这里用经典的Todo MVC作为示例。所有的代码可以在我的Github上找到。

+ +

环境准备

+ +

React.js推荐使用JSX来写View,所以我们需要准备一下JSX的环境。按照最简单的做法,我们只需要将in-browser JSX transformer引入即可:

+
$ bower install --save react
+
+

然后在index.html中,加入一下的代码,作为script标签的第一个:

+
<script src="bower_components/react/react-with-addons.js"></script>
+<script src="bower_components/react/JSXTransformer.js"></script>
+
+

这样我们的JSX代码就可以在运行时编译了。

+ +

替换的方式

+ +

这里我们实际上是做的一个重构(虽然没有测试),为了尽量使得每一步都比较容易验证,我们每次commit的修改都会尽量很小,而且每次commit的代码都要保证是工作的,不会破坏原有的功能。所以我们会在原有的代码的基础上增加React.js的代码,完成一部分再删除一部分Backbone.js View的代码,最后再完成整个替换。

+ +

一般来说我推荐先替换Template,再替换DOM事件的绑定和处理,最后再整体用某个Component替换掉Backbone.js View。下面我会用TodoItem View替换过程的开始部分作为示例,讲解一下如何重构,同时也讲解一些React.js的基本概念。

+ +

Template

+ +

我们之前提到过,React.js的Component其实就是View + Template的结合。那么我们应该怎样来划分Component呢?这里React.js官方给出的意见是:遵从单一职责的原则,也就是一个Component只做一件事。具体如何划分就要看你的Domain和团队自己的规则了。

+ +

现在我们新建TodoItem Component,新建todo-item.jsx文件,将原来template中的内容挪过来,并且用React.js的方式来render:

+
var app = app || {};
+
+(function () {
+    'use strict';
+
+    app.TodoItem = React.createClass({
+
+        render: function() {
+            var todoData = this.props.todo.toJSON();
+            return (
+                <div>
+                    <div className="view">
+                        <input className="toggle" type="checkbox" checked={todoData.completed} />
+                        <label>{todoData.title}</label>
+                        <button className="destroy"></button>
+                    </div>
+                    <input className="edit" defaultValue={todoData.title} />
+                </div>
+            );
+        }
+    });
+})();
+
+

替换todo-view.js中的template:

+
@@ -12,9 +12,6 @@
+                //... is a list tag.
+                tagName:  'li',
+
+-               // Cache the template function for a single item.
+-               template: _.template($('#item-template').html()),
+-
+                // The DOM events specific to an item.
+                events: {
+                        'click .toggle': 'toggleCompleted',
+@@ -48,7 +45,9 @@
+                                return;
+                        }
+
+-                       this.$el.html(this.template(this.model.toJSON()));
++                       React.render(React.createElement(app.TodoItem, {
++                               todo: this.model
++                       }), this.$el[0]);
+                        this.$el.toggleClass('completed', this.model.get('completed'));
+                        this.toggleVisible();
+                        this.$input = this.$('.edit');
+
+

这里我们用到了React.js Component的props属性,这个属性是由父Component传下来的数据,props本身是不可以由子Component自己去改变的,后面我们会讲到,对用户操作会改变的数据,应该使用state

+ +

index.html中删除原有的模板,引入todo-item.jsx,同时用JSXTransformer来管理所有View的加载:

+
@@ -23,16 +23,6 @@
+            <p>Written by <a href="https://github.com/addyosmani">Addy Osmani</a></p>
+            <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
+        </footer>
+-       <script type="text/template" id="item-template">
+-           <div>
+-               <div class="view">
+-                   <input class="toggle" type="checkbox" <%= completed ? 'checked' : '' %>>
+-                   <label><%- title %></label>
+-                   <button class="destroy"></button>
+-               </div>
+-               <input class="edit" value="<%- title %>">
+-           </div>
+-       </script>
+        <script type="text/template" id="stats-template">
+            <span id="todo-count"><strong><%= remaining %></strong> <%= remaining === 1 ? 'item' : 'items' %> left</span>
+            <ul id="filters">
+
+@@ -52,16 +42,16 @@
+        <script src="js/models/todo.js"></script>
+        <script src="js/collections/todos.js"></script>
+-       <script src="js/views/todo-view.js"></script>
+-       <script src="js/views/app-view.js"></script>
+-       <script src="js/routers/router.js"></script>
+-       <script src="js/app.js"></script>
++       <script type="text/jsx" src="js/components/todo-item.jsx"></script>
++       <script type="text/jsx" src="js/views/todo-view.js"></script>
++       <script type="text/jsx" src="js/views/app-view.js"></script>
++       <script type="text/jsx" src="js/routers/router.js"></script>
++       <script type="text/jsx" src="js/app.js"></script>
+    </body>
+ </html>
+
+

到这里,替换就告一段落了。这个时候我们可以看到原有的功能都是可工作的,现在就可以做一个提交了。

+ +

DOM事件

+ +

下面我们开始把DOM事件的处理挪到todo-item.jsx中,并且以React.js的方式来做。以toggle为例:

+
index js/components/todo-item.jsx
+@@ -5,12 +5,28 @@
+    app.TodoItem = React.createClass({
+
++       getInitialState: function() {
++           return {
++               completed: this.props.todo.get('completed')
++           };
++       },
++
++       // Toggle the `"completed"` state of the model.
++       toggleCompleted: function () {
++           this.setState({
++               completed: this.props.todo.toggle()
++           });
++       },
++
+        render: function() {
+            var todoData = this.props.todo.toJSON();
+            return (
+                <div>
+                    <div className="view">
+-                       <input className="toggle" type="checkbox" checked={todoData.completed} />
++                       <input className="toggle"
++                           type="checkbox"
++                           checked={this.state.completed}
++                           onChange={this.toggleCompleted}/>
+                        <label>{todoData.title}</label>
+                        <button className="destroy"></button>
+                    </div>
+
index js/models/todo.js
+@@ -18,9 +18,11 @@
+        // Toggle the `completed` state of this todo item.
+        toggle: function () {
++           var completed = !this.get('completed');
+            this.save({
+-               completed: !this.get('completed')
++               completed: completed
+            });
++           return completed;
+        }
+    });
+ })();
+
index js/views/todo-view.js
+@@ -14,7 +14,6 @@
+        // The DOM events specific to an item.
+        events: {
+-           'click .toggle': 'toggleCompleted',
+            'dblclick label': 'edit',
+            'click .destroy': 'clear',
+            'keypress .edit': 'updateOnEnter',
+@@ -64,11 +63,6 @@
+                app.TodoFilter === 'completed';
+        },
+
+-       // Toggle the `"completed"` state of the model.
+-       toggleCompleted: function () {
+-           this.model.toggle();
+-       },
+-
+        // Switch this view into `"editing"` mode, displaying the input field.
+        edit: function () {
+            this.$el.addClass('editing');
+
+

到这里toggle事件就被重构完成了。我们引入了一个新的概念,就是state。我们之前提到过,state是用来表示Component中,用户通过操作可能改变的数据。关于stateprops的详细区别,这里有一篇文章(props vs state)介绍得很好,这里把最关键的部分转载并翻译一下:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-propsstate
能否从Component获取初始值?YesYes
Component中的值改变,是否会影响自身的值?YesNo
能否在自身中设置默认值?^YesYes
能否在自身中改变值?NoYes
能否给子Component设置默认值?YesYes
自身的值改变,是否会影响传给子Component中的值?YesNo
+ +

^ 注意:props和state的默认值,都会被父Component传过来的默认值覆盖。

+ +

DOM操作

+ +

在View这一层,我们常常会有针对某个Element进行操作的需求,以前我们可能会通过Class、ID或Tag来获取到这个Element,然后调用DOM方法来操作它。而React.js提供了refs来完成这个功能:

+
index js/components/todo-item.jsx
+@@ -18,6 +18,11 @@
+            });
+        },
+
++       // Switch this view into `"editing"` mode, displaying the input field.
++       edit: function () {
++           this.refs.editInput.getDOMNode().focus();
++       },
++
+        render: function() {
+            var todoData = this.props.todo.toJSON();
+            return (
+@@ -27,10 +32,10 @@
+                            type="checkbox"
+                            checked={this.state.completed}
+                            onChange={this.toggleCompleted}/>
+-                       <label>{todoData.title}</label>
++                       <label onDoubleClick={this.edit}>{todoData.title}</label>
+                        <button className="destroy"></button>
+                    </div>
+-                   <input className="edit" defaultValue={todoData.title} />
++                   <input ref="editInput" className="edit" defaultValue={todoData.title} />
+                </div>
+            );
+        }
+
index js/views/todo-view.js
+@@ -66,7 +66,6 @@
+        // Switch this view into `"editing"` mode, displaying the input field.
+        edit: function () {
+            this.$el.addClass('editing');
+-           this.$input.focus();
+        },
+
+        // Close the `"editing"` mode, saving changes to the todo.
+
+

这里之所以React.js专门提供了refs,而没有推荐使用传统方式,我觉得是因为:

+ +
    +
  1. 避免多余的class或ID,这样我们可以将class只用于样式上,将ID只用于form中,让他们的使用更加符合原始的设计;
  2. +
  3. refs返回的其实不单是DOM对象,而是一个称为“backing instance”的东西,这个我没有查到具体含义是什么,猜测应该是React.js中Virtual DOM中的实例。
  4. +
+ +

总结

+ +

此间省略N步,我们得到了最后的重构成果,如果希望看中间过程的,可以查看中间的commit diff。

+ +

在替换前后之间,我们可以比较一下监听事件的不同,首先来看看替换前我们监听了哪些事件:

+
//todo-view.js
+...
+initialize: function () {
+    this.listenTo(this.model, 'change', this.render);
+    this.listenTo(this.model, 'destroy', this.remove);
+    this.listenTo(this.model, 'visible', this.toggleVisible);
+}
+...
+
//app-view.js
+...
+initialize: function () {
+...
+    this.listenTo(app.todos, 'add', this.addOne);
+    this.listenTo(app.todos, 'reset', this.addAll);
+    this.listenTo(app.todos, 'change:completed', this.filterOne);
+    this.listenTo(app.todos, 'filter', this.filterAll);
+    this.listenTo(app.todos, 'all', this.render);
+...
+}
+...
+
+

然后我们可以全文搜索一下替换后的事件监听,只剩下一个:

+
//app.js
+...
+app.todos.on('all', render);
+...
+
+

也就是说,不管数据如何变化,不管哪些数据变化了,我都直接拿我关心的数据来render就完了。由于Virtual DOM帮我做了增量式的DOM修改,这一部分就不用我来操心了,那么之前的一大堆事件监听以及相应的事件处理回调都可以省略了,这样代码逻辑的复杂度会降低很多,可维护和可读性会提高很多,同时也没有性能的担忧。React.js真是处理Tempate和View的一大神器啊!

+ +

而且重构到最后我发现,如果不是因为Backbone.js的router依赖于jQuery,连jQuery我都可以直接删了。

+ +
+
+
+
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/about.html b/about.html index ef02493..9b51d83 100644 --- a/about.html +++ b/about.html @@ -8,7 +8,7 @@ - + "},{b:""}]}]},e.CLCM,e.CBCM,{cN:"preprocessor",b:"#",e:"$",k:"if else elif endif define undef warning error line region endregion pragma checksum"},{cN:"string",b:'@"',e:'"',c:[{b:'""'}]},e.ASM,e.QSM,e.CNM,{bK:"class namespace interface",e:/[{;=]/,i:/[^\s:]/,c:[e.TM,e.CLCM,e.CBCM]},{bK:"new return throw await",r:0},{cN:"function",b:"("+n+"\\s+)+"+e.IR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:t,c:[{b:e.IR+"\\s*\\(",rB:!0,c:[e.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:t,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]}]}}),hljs.registerLanguage("css",function(e){var t="[a-zA-Z-][a-zA-Z0-9_-]*",n={cN:"function",b:t+"\\(",rB:!0,eE:!0,e:"\\("};return{cI:!0,i:"[=/|']",c:[e.CBCM,{cN:"id",b:"\\#[A-Za-z0-9_-]+"},{cN:"class",b:"\\.[A-Za-z0-9_-]+",r:0},{cN:"attr_selector",b:"\\[",e:"\\]",i:"$"},{cN:"pseudo",b:":(:)?[a-zA-Z0-9\\_\\-\\+\\(\\)\\\"\\']+"},{cN:"at_rule",b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{cN:"at_rule",b:"@",e:"[{;]",c:[{cN:"keyword",b:/\S+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[n,e.ASM,e.QSM,e.CSSNM]}]},{cN:"tag",b:t,r:0},{cN:"rules",b:"{",e:"}",i:"[^\\s]",r:0,c:[e.CBCM,{cN:"rule",b:"[^\\s]",rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:"[A-Z\\_\\.\\-]+",e:":",eE:!0,i:"[^\\s]",starts:{cN:"value",eW:!0,eE:!0,c:[n,e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"hexcolor",b:"#[0-9A-Fa-f]+"},{cN:"important",b:"!important"}]}}]}]}]}}),hljs.registerLanguage("diff",function(){return{aliases:["patch"],c:[{cN:"chunk",r:10,v:[{b:/^\@\@ +\-\d+,\d+ +\+\d+,\d+ +\@\@$/},{b:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{b:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{cN:"header",v:[{b:/Index: /,e:/$/},{b:/=====/,e:/=====$/},{b:/^\-\-\-/,e:/$/},{b:/^\*{3} /,e:/$/},{b:/^\+\+\+/,e:/$/},{b:/\*{5}/,e:/\*{5}$/}]},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"change",b:"^\\!",e:"$"}]}}),hljs.registerLanguage("http",function(){return{i:"\\S",c:[{cN:"status",b:"^HTTP/[0-9\\.]+",e:"$",c:[{cN:"number",b:"\\b\\d{3}\\b"}]},{cN:"request",b:"^[A-Z]+ (.*?) HTTP/[0-9\\.]+$",rB:!0,e:"$",c:[{cN:"string",b:" ",e:" ",eB:!0,eE:!0}]},{cN:"attribute",b:"^\\w",e:": ",eE:!0,i:"\\n|\\s|=",starts:{cN:"string",e:"$"}},{b:"\\n\\n",starts:{sL:"",eW:!0}}]}}),hljs.registerLanguage("ini",function(e){return{cI:!0,i:/\S/,c:[{cN:"comment",b:";",e:"$"},{cN:"title",b:"^\\[",e:"\\]"},{cN:"setting",b:"^[a-z0-9\\[\\]_-]+[ \\t]*=[ \\t]*",e:"$",c:[{cN:"value",eW:!0,k:"on off true false yes no",c:[e.QSM,e.NM],r:0}]}]}}),hljs.registerLanguage("java",function(e){var t=e.UIR+"(<"+e.UIR+">)?",n="false synchronized int abstract float private char boolean static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private",r="(\\b(0b[01_]+)|\\b0[xX][a-fA-F0-9_]+|(\\b[\\d_]+(\\.[\\d_]*)?|\\.[\\d_]+)([eE][-+]?\\d+)?)[lLfF]?",i={cN:"number",b:r,r:0};return{aliases:["jsp"],k:n,i:/<\//,c:[{cN:"javadoc",b:"/\\*\\*",e:"\\*/",r:0,c:[{cN:"javadoctag",b:"(^|\\s)@[A-Za-z]+"}]},e.CLCM,e.CBCM,e.ASM,e.QSM,{cN:"class",bK:"class interface",e:/[{;=]/,eE:!0,k:"class interface",i:/[:"\[\]]/,c:[{bK:"extends implements"},e.UTM]},{bK:"new throw return",r:0},{cN:"function",b:"("+t+"\\s+)+"+e.UIR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:n,c:[{b:e.UIR+"\\s*\\(",rB:!0,r:0,c:[e.UTM]},{cN:"params",b:/\(/,e:/\)/,k:n,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]},i,{cN:"annotation",b:"@[A-Za-z]+"}]}}),hljs.registerLanguage("javascript",function(e){return{aliases:["js"],k:{keyword:"in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const class",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document"},c:[{cN:"pi",r:10,v:[{b:/^\s*('|")use strict('|")/},{b:/^\s*('|")use asm('|")/}]},e.ASM,e.QSM,e.CLCM,e.CBCM,e.CNM,{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{b:/;/,r:0,sL:"xml"}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{cN:"params",b:/\(/,e:/\)/,c:[e.CLCM,e.CBCM],i:/["'\(]/}],i:/\[|%/},{b:/\$[(.]/},{b:"\\."+e.IR,r:0}]}}),hljs.registerLanguage("json",function(e){var t={literal:"true false null"},n=[e.QSM,e.CNM],r={cN:"value",e:",",eW:!0,eE:!0,c:n,k:t},i={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:!0,eE:!0,c:[e.BE],i:"\\n",starts:r}],i:"\\S"},s={b:"\\[",e:"\\]",c:[e.inherit(r,{cN:null})],i:"\\S"};return n.splice(n.length,0,i,s),{c:n,k:t,i:"\\S"}}),hljs.registerLanguage("makefile",function(e){var t={cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]};return{aliases:["mk","mak"],c:[e.HCM,{b:/^\w+\s*\W*=/,rB:!0,r:0,starts:{cN:"constant",e:/\s*\W*=/,eE:!0,starts:{e:/$/,r:0,c:[t]}}},{cN:"title",b:/^[\w]+:\s*$/},{cN:"phony",b:/^\.PHONY:/,e:/$/,k:".PHONY",l:/[\.\w]+/},{b:/^\t+/,e:/$/,r:0,c:[e.QSM,t]}]}}),hljs.registerLanguage("xml",function(){var e="[A-Za-z0-9\\._:-]+",t={b:/<\?(php)?(?!\w)/,e:/\?>/,sL:"php",subLanguageMode:"continuous"},n={eW:!0,i:/]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xsl","plist"],cI:!0,c:[{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"|$)",e:">",k:{title:"style"},c:[n],starts:{e:"",rE:!0,sL:"css"}},{cN:"tag",b:"|$)",e:">",k:{title:"script"},c:[n],starts:{e:"",rE:!0,sL:"javascript"}},t,{cN:"pi",b:/<\?\w+/,e:/\?>/,r:10},{cN:"tag",b:"",c:[{cN:"title",b:/[^ \/><\n\t]+/,r:0},n]}]}}),hljs.registerLanguage("markdown",function(){return{aliases:["md","mkdown","mkd"],c:[{cN:"header",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"blockquote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"`.+?`"},{b:"^( {4}| )",e:"$",r:0}]},{cN:"horizontal_rule",b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:!0,c:[{cN:"link_label",b:"\\[",e:"\\]",eB:!0,rE:!0,r:0},{cN:"link_url",b:"\\]\\(",e:"\\)",eB:!0,eE:!0},{cN:"link_reference",b:"\\]\\[",e:"\\]",eB:!0,eE:!0}],r:10},{b:"^\\[.+\\]:",rB:!0,c:[{cN:"link_reference",b:"\\[",e:"\\]:",eB:!0,eE:!0,starts:{cN:"link_url",e:"$"}}]}]}}),hljs.registerLanguage("nginx",function(e){var t={cN:"variable",v:[{b:/\$\d+/},{b:/\$\{/,e:/}/},{b:"[\\$\\@]"+e.UIR}]},n={eW:!0,l:"[a-z/_]+",k:{built_in:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},r:0,i:"=>",c:[e.HCM,{cN:"string",c:[e.BE,t],v:[{b:/"/,e:/"/},{b:/'/,e:/'/}]},{cN:"url",b:"([a-z]+):/",e:"\\s",eW:!0,eE:!0,c:[t]},{cN:"regexp",c:[e.BE,t],v:[{b:"\\s\\^",e:"\\s|{|;",rE:!0},{b:"~\\*?\\s+",e:"\\s|{|;",rE:!0},{b:"\\*(\\.[a-z\\-]+)+"},{b:"([a-z\\-]+\\.)+\\*"}]},{cN:"number",b:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{cN:"number",b:"\\b\\d+[kKmMgGdshdwy]*\\b",r:0},t]};return{aliases:["nginxconf"],c:[e.HCM,{b:e.UIR+"\\s",e:";|{",rB:!0,c:[{cN:"title",b:e.UIR,starts:n}],r:0}],i:"[^\\s\\}]"}}),hljs.registerLanguage("objectivec",function(e){var t={keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"NSString NSData NSDictionary CGRect CGPoint UIButton UILabel UITextView UIWebView MKMapView NSView NSViewController NSWindow NSWindowController NSSet NSUUID NSIndexSet UISegmentedControl NSObject UITableViewDelegate UITableViewDataSource NSThread UIActivityIndicator UITabbar UIToolBar UIBarButtonItem UIImageView NSAutoreleasePool UITableView BOOL NSInteger CGFloat NSException NSLog NSMutableString NSMutableArray NSMutableDictionary NSURL NSIndexPath CGSize UITableViewCell UIView UIViewController UINavigationBar UINavigationController UITabBarController UIPopoverController UIPopoverControllerDelegate UIImage NSNumber UISearchBar NSFetchedResultsController NSFetchedResultsChangeType UIScrollView UIScrollViewDelegate UIEdgeInsets UIColor UIFont UIApplication NSNotFound NSNotificationCenter NSNotification UILocalNotification NSBundle NSFileManager NSTimeInterval NSDate NSCalendar NSUserDefaults UIWindow NSRange NSArray NSError NSURLRequest NSURLConnection NSURLSession NSURLSessionDataTask NSURLSessionDownloadTask NSURLSessionUploadTask NSURLResponseUIInterfaceOrientation MPMoviePlayerController dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},n=/[a-zA-Z@][a-zA-Z0-9_]*/,r="@interface @class @protocol @implementation";return{aliases:["m","mm","objc","obj-c"],k:t,l:n,i:""}]}]},{cN:"class",b:"("+r.split(" ").join("|")+")\\b",e:"({|$)",eE:!0,k:r,l:n,c:[e.UTM]},{cN:"variable",b:"\\."+e.UIR,r:0}]}}),hljs.registerLanguage("perl",function(e){var t="getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qqfileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmgetsub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedirioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when",n={cN:"subst",b:"[$@]\\{",e:"\\}",k:t},r={b:"->{",e:"}"},i={cN:"variable",v:[{b:/\$\d/},{b:/[\$\%\@](\^\w\b|#\w+(\:\:\w+)*|{\w+}|\w+(\:\:\w*)*)/},{b:/[\$\%\@][^\s\w{]/,r:0}]},s={cN:"comment",b:"^(__END__|__DATA__)",e:"\\n$",r:5},o=[e.BE,n,i],u=[i,e.HCM,s,{cN:"comment",b:"^\\=\\w",e:"\\=cut",eW:!0},r,{cN:"string",c:o,v:[{b:"q[qwxr]?\\s*\\(",e:"\\)",r:5},{b:"q[qwxr]?\\s*\\[",e:"\\]",r:5},{b:"q[qwxr]?\\s*\\{",e:"\\}",r:5},{b:"q[qwxr]?\\s*\\|",e:"\\|",r:5},{b:"q[qwxr]?\\s*\\<",e:"\\>",r:5},{b:"qw\\s+q",e:"q",r:5},{b:"'",e:"'",c:[e.BE]},{b:'"',e:'"'},{b:"`",e:"`",c:[e.BE]},{b:"{\\w+}",c:[],r:0},{b:"-?\\w+\\s*\\=\\>",c:[],r:0}]},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\/\\/|"+e.RSR+"|\\b(split|return|print|reverse|grep)\\b)\\s*",k:"split return print reverse grep",r:0,c:[e.HCM,s,{cN:"regexp",b:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",r:10},{cN:"regexp",b:"(m|qr)?/",e:"/[a-z]*",c:[e.BE],r:0}]},{cN:"sub",bK:"sub",e:"(\\s*\\(.*?\\))?[;{]",r:5},{cN:"operator",b:"-\\w\\b",r:0}];return n.c=u,r.c=u,{aliases:["pl"],k:t,c:u}}),hljs.registerLanguage("php",function(e){var t={cN:"variable",b:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},n={cN:"preprocessor",b:/<\?(php)?|\?>/},r={cN:"string",c:[e.BE,n],v:[{b:'b"',e:'"'},{b:"b'",e:"'"},e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},i={v:[e.BNM,e.CNM]};return{aliases:["php3","php4","php5","php6"],cI:!0,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception default die require __FUNCTION__ enddeclare final try switch continue endfor endif declare unset true false trait goto instanceof insteadof __DIR__ __NAMESPACE__ yield finally",c:[e.CLCM,e.HCM,{cN:"comment",b:"/\\*",e:"\\*/",c:[{cN:"phpdoc",b:"\\s@[A-Za-z]+"},n]},{cN:"comment",b:"__halt_compiler.+?;",eW:!0,k:"__halt_compiler",l:e.UIR},{cN:"string",b:"<<<['\"]?\\w+['\"]?$",e:"^\\w+;",c:[e.BE]},n,t,{b:/->+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{cN:"function",bK:"function",e:/[;{]/,eE:!0,i:"\\$|\\[|%",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)",c:["self",t,e.CBCM,r,i]}]},{cN:"class",bK:"class interface",e:"{",eE:!0,i:/[:\(\$"]/,c:[{bK:"extends implements"},e.UTM]},{bK:"namespace",e:";",i:/[\.']/,c:[e.UTM]},{bK:"use",e:";",c:[e.UTM]},{b:"=>"},r,i]}}),hljs.registerLanguage("python",function(e){var t={cN:"prompt",b:/^(>>>|\.\.\.) /},n={cN:"string",c:[e.BE],v:[{b:/(u|b)?r?'''/,e:/'''/,c:[t],r:10},{b:/(u|b)?r?"""/,e:/"""/,c:[t],r:10},{b:/(u|r|ur)'/,e:/'/,r:10},{b:/(u|r|ur)"/,e:/"/,r:10},{b:/(b|br)'/,e:/'/},{b:/(b|br)"/,e:/"/},e.ASM,e.QSM]},r={cN:"number",r:0,v:[{b:e.BNR+"[lLjJ]?"},{b:"\\b(0o[0-7]+)[lLjJ]?"},{b:e.CNR+"[lLjJ]?"}]},i={cN:"params",b:/\(/,e:/\)/,c:["self",t,r,n]};return{aliases:["py","gyp"],k:{keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda nonlocal|10 None True False",built_in:"Ellipsis NotImplemented"},i:/(<\/|->|\?)/,c:[t,r,n,e.HCM,{v:[{cN:"function",bK:"def",r:10},{cN:"class",bK:"class"}],e:/:/,i:/[${=;\n]/,c:[e.UTM,i]},{cN:"decorator",b:/@/,e:/$/},{b:/\b(print|exec)\(/}]}}),hljs.registerLanguage("ruby",function(e){var t="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",n="and false then defined module in return redo if BEGIN retry end for true self when next until do begin unless END rescue nil else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",r={cN:"yardoctag",b:"@[A-Za-z]+"},i={cN:"value",b:"#<",e:">"},s={cN:"comment",v:[{b:"#",e:"$",c:[r]},{b:"^\\=begin",e:"^\\=end",c:[r],r:10},{b:"^__END__",e:"\\n$"}]},o={cN:"subst",b:"#\\{",e:"}",k:n},u={cN:"string",c:[e.BE,o],v:[{b:/'/,e:/'/},{b:/"/,e:/"/},{b:/`/,e:/`/},{b:"%[qQwWx]?\\(",e:"\\)"},{b:"%[qQwWx]?\\[",e:"\\]"},{b:"%[qQwWx]?{",e:"}"},{b:"%[qQwWx]?<",e:">"},{b:"%[qQwWx]?/",e:"/"},{b:"%[qQwWx]?%",e:"%"},{b:"%[qQwWx]?-",e:"-"},{b:"%[qQwWx]?\\|",e:"\\|"},{b:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/}]},a={cN:"params",b:"\\(",e:"\\)",k:n},f=[u,i,s,{cN:"class",bK:"class module",e:"$|;",i:/=/,c:[e.inherit(e.TM,{b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{cN:"inheritance",b:"<\\s*",c:[{cN:"parent",b:"("+e.IR+"::)?"+e.IR}]},s]},{cN:"function",bK:"def",e:" |$|;",r:0,c:[e.inherit(e.TM,{b:t}),a,s]},{cN:"constant",b:"(::)?(\\b[A-Z]\\w*(::)?)+",r:0},{cN:"symbol",b:e.UIR+"(\\!|\\?)?:",r:0},{cN:"symbol",b:":",c:[u,{b:t}],r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{b:"("+e.RSR+")\\s*",c:[i,s,{cN:"regexp",c:[e.BE,o],i:/\n/,v:[{b:"/",e:"/[a-z]*"},{b:"%r{",e:"}[a-z]*"},{b:"%r\\(",e:"\\)[a-z]*"},{b:"%r!",e:"![a-z]*"},{b:"%r\\[",e:"\\][a-z]*"}]}],r:0}];o.c=f,a.c=f;var l="[>?]>",c="[\\w#]+\\(\\w+\\):\\d+:\\d+>",h="(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>",p=[{b:/^\s*=>/,cN:"status",starts:{e:"$",c:f}},{cN:"prompt",b:"^("+l+"|"+c+"|"+h+")",starts:{e:"$",c:f}}];return{aliases:["rb","gemspec","podspec","thor","irb"],k:n,c:[s].concat(p).concat(f)}}),hljs.registerLanguage("sql",function(e){var t={cN:"comment",b:"--",e:"$"};return{cI:!0,i:/[<>]/,c:[{cN:"operator",bK:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate savepoint release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup",e:/;/,eW:!0,k:{keyword:"abs absolute acos action add adddate addtime aes_decrypt aes_encrypt after aggregate all allocate alter analyze and any are as asc ascii asin assertion at atan atan2 atn2 authorization authors avg backup before begin benchmark between bin binlog bit_and bit_count bit_length bit_or bit_xor both by cache call cascade cascaded case cast catalog ceil ceiling chain change changed char_length character_length charindex charset check checksum checksum_agg choose close coalesce coercibility collate collation collationproperty column columns columns_updated commit compress concat concat_ws concurrent connect connection connection_id consistent constraint constraints continue contributors conv convert convert_tz corresponding cos cot count count_big crc32 create cross cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime data database databases datalength date_add date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts datetimeoffsetfromparts day dayname dayofmonth dayofweek dayofyear deallocate declare decode default deferrable deferred degrees delayed delete des_decrypt des_encrypt des_key_file desc describe descriptor diagnostics difference disconnect distinct distinctrow div do domain double drop dumpfile each else elt enclosed encode encrypt end end-exec engine engines eomonth errors escape escaped event eventdata events except exception exec execute exists exp explain export_set extended external extract fast fetch field fields find_in_set first first_value floor flush for force foreign format found found_rows from from_base64 from_days from_unixtime full function get get_format get_lock getdate getutcdate global go goto grant grants greatest group group_concat grouping grouping_id gtid_subset gtid_subtract handler having help hex high_priority hosts hour ident_current ident_incr ident_seed identified identity if ifnull ignore iif ilike immediate in index indicator inet6_aton inet6_ntoa inet_aton inet_ntoa infile initially inner innodb input insert install instr intersect into is is_free_lock is_ipv4 is_ipv4_compat is_ipv4_mapped is_not is_not_null is_used_lock isdate isnull isolation join key kill language last last_day last_insert_id last_value lcase lead leading least leaves left len lenght level like limit lines ln load load_file local localtime localtimestamp locate lock log log10 log2 logfile logs low_priority lower lpad ltrim make_set makedate maketime master master_pos_wait match matched max md5 medium merge microsecond mid min minute mod mode module month monthname mutex name_const names national natural nchar next no no_write_to_binlog not now nullif nvarchar oct octet_length of old_password on only open optimize option optionally or ord order outer outfile output pad parse partial partition password patindex percent_rank percentile_cont percentile_disc period_add period_diff pi plugin position pow power pragma precision prepare preserve primary prior privileges procedure procedure_analyze processlist profile profiles public publishingservername purge quarter query quick quote quotename radians rand read references regexp relative relaylog release release_lock rename repair repeat replace replicate reset restore restrict return returns reverse revoke right rlike rollback rollup round row row_count rows rpad rtrim savepoint schema scroll sec_to_time second section select serializable server session session_user set sha sha1 sha2 share show sign sin size slave sleep smalldatetimefromparts snapshot some soname soundex sounds_like space sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sql_variant_property sqlstate sqrt square start starting status std stddev stddev_pop stddev_samp stdev stdevp stop str str_to_date straight_join strcmp string stuff subdate substr substring subtime subtring_index sum switchoffset sysdate sysdatetime sysdatetimeoffset system_user sysutcdatetime table tables tablespace tan temporary terminated tertiary_weights then time time_format time_to_sec timediff timefromparts timestamp timestampadd timestampdiff timezone_hour timezone_minute to to_base64 to_days to_seconds todatetimeoffset trailing transaction translation trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse ucase uncompress uncompressed_length unhex unicode uninstall union unique unix_timestamp unknown unlock update upgrade upped upper usage use user user_resources using utc_date utc_time utc_timestamp uuid uuid_short validate_password_strength value values var var_pop var_samp variables variance varp version view warnings week weekday weekofyear weight_string when whenever where with work write xml xor year yearweek zon",literal:"true false null",built_in:"array bigint binary bit blob boolean char character date dec decimal float int integer interval number numeric real serial smallint varchar varying int8 serial8 text"},c:[{cN:"string",b:"'",e:"'",c:[e.BE,{b:"''"}]},{cN:"string",b:'"',e:'"',c:[e.BE,{b:'""'}]},{cN:"string",b:"`",e:"`",c:[e.BE]},e.CNM,e.CBCM,t]},e.CBCM,t]}});var articles=[{title:"用React.js替换Backbone.js的View(二)",link:"/2015/01/15/replace_backbonejs_view_with_reactj_2.html",tags:["Javascript","Backbone.js","React.js"]},{title:"用React.js替换Backbone.js的View(一)",link:"/2015/01/13/replace_backbonejs_view_with_reactj_1.html",tags:["Javascript","Backbone.js","React.js"]},{title:"编写易读、可测试、可运行的API文档",link:"/2014/12/09/readable_testable_runnable_API_documentation.html",tags:["Document","API Blueprint"]},{title:"国内项目天坑记(二)",link:"/2014/06/09/grunt_config_as_modules.html" +,tags:["Grunt"]},{title:"国内项目天坑记(一)",link:"/2014/06/05/frontend_architecture_in_a_large_corp_portal_site.html",tags:["Backbone.js"]},{title:"打造离线使用的Mobile Web App",link:"/2013/05/28/build_offline_mobile_web_app.html",tags:["Mobile","HTML5","AppCache"]},{title:"如何构建自动化的前端开发流程",link:"/2013/03/15/how-to-build-frontend-dev-env.html",tags:["grunt","bower"]},{title:"使用CSS3制作一个有趣的搜索框",link:"/2013/02/04/use_css3_to_create_innovation_search_box.html",tags:["css3","animation"]},{title:"数独生成浅尝",link:"/2013/01/28/sudoku_arithmetic_research.html",tags:["arithmetic"]},{title:"利用Backbone.js重构项目代码",link:"/2013/01/22/backbone_refator.html",tags:["JavaScript","Backbone.js"]},{title:"Sass & Compass Best Practices 3",link:"/2013/01/13/sass_compass_best_practices_3.html",tags:["CSS","Compass","SASS"]},{title:"Sass & Compass Best Practices 2",link:"/2013/01/09/sass_compass_best_practices_2.html",tags:["CSS","Compass","SASS"]},{title:"Sass & Compass Best Practices 1",link:"/2013/01/07/sass_compass_best_practices_1.html",tags:["CSS","Compass","SASS"]},{title:"不在这里重生,就在这里死去",link:"/2012/12/31/summary_of_2012.html",tags:["personal"]}];$(function(){function t(e,t){var n=!1;return $.each(e,function(){if(this.toLowerCase().indexOf(t)>=0){n=!0;return}}),n}$(".article-content a").attr("target","_blank"),hljs.initHighlightingOnLoad(),$(".about").length>0?$(".about-link").addClass("active"):$(".resume").length>0?$(".resume-link").addClass("active"):$(".blog-link").addClass("active");var e=$(".search");e.on("focus",function(){$(".search-container").addClass("active"),$(".social-links").addClass("striction")}).on("blur",function(){$(".search-container").removeClass("active"),$(".social-links").removeClass("striction"),$(".search").val(""),setTimeout(function(){$(".search-result").html("")},100)}),e.on("keydown, keyup",function(){$(".search-result").html("");var e=$(".search").val().toLowerCase();e!==""&&$.each(articles,function(){if(this.title.toLowerCase().indexOf(e)>=0||t(this.tags,e)){var n=$('
  • '+this.title+"
  • ");$(".search-result").append(n)}})})}); \ No newline at end of file diff --git a/resume.html b/resume.html index b350ac4..a024e54 100644 --- a/resume.html +++ b/resume.html @@ -8,7 +8,7 @@ - + + + + +
    + + +
    +
    + +
    +

    + 用React.js替换Backbone.js的View(二) +

    + +

    ——Todo MVC示例

    + +
    +
    +

    Backbone.js和React.js在设计思想上都借鉴了Reactive Programming,即:当Model修改时,这种变更可以反向传播到View,使得View同时被更改,也就是双向绑定。但Backbone.js需要你自己来写如何修改View,而在React.js中,你只需要关心如何根据Model来显示View,如何修改可以完全交给React.js。也就是说他们都在做一种简化,而React.js做的更加彻底,这也是它的核心思想和优点。

    + +

    真正的学习还是需要写代码,所以这里用经典的Todo MVC作为示例。所有的代码可以在我的Github上找到。

    + +
    +
    +
    + +
    +

    + 用React.js替换Backbone.js的View(一) +

    + +

    ——Backbone.js View的陷阱以及React.js的优点

    + +
    +
    +

    最近终于找到时间,学习了一下Facebook出品的React.js,发现虽然没有很深的体会到性能上的好处,但是这种编程方式带来的好处确实是很大的。这里我准备跟Backbone.js的View做一下对比,同时下一篇文章中提供一个示例说明一下如何用React.js替换Backbone.js的View。

    + +
    +
    + +
    +
    + + + + + + + \ No newline at end of file diff --git a/tags/sass.html b/tags/sass.html index 7e1185f..2886c99 100644 --- a/tags/sass.html +++ b/tags/sass.html @@ -8,7 +8,7 @@ - +