Add todoapp benchmark
This commit is contained in:
parent
479db8c2cc
commit
02e7b40c07
218 changed files with 63168 additions and 0 deletions
1
bench/implementations/react-15.3.1/build.min.js
vendored
Executable file
1
bench/implementations/react-15.3.1/build.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
23
bench/implementations/react-15.3.1/index.html
Executable file
23
bench/implementations/react-15.3.1/index.html
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
<!doctype html>
|
||||
<html lang="en" data-framework="react">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>React • TodoMVC</title>
|
||||
<link rel="stylesheet" href="node_modules/todomvc-common/base.css">
|
||||
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
|
||||
</head>
|
||||
<body>
|
||||
<section class="todoapp"></section>
|
||||
<footer class="info">
|
||||
<p>Double-click to edit a todo</p>
|
||||
<p>Created by <a href="http://github.com/petehunt/">petehunt</a></p>
|
||||
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
|
||||
</footer>
|
||||
|
||||
<script src="node_modules/classnames/index.js"></script>
|
||||
<script src="node_modules/director/build/director.js"></script>
|
||||
<script src="node_modules/react/dist/react-with-addons.min.js"></script>
|
||||
<script src="node_modules/react-dom/dist/react-dom.min.js"></script>
|
||||
<script src="build.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
426
bench/implementations/react-15.3.1/js/app.jsx
Executable file
426
bench/implementations/react-15.3.1/js/app.jsx
Executable file
|
|
@ -0,0 +1,426 @@
|
|||
/*jshint quotmark:false */
|
||||
/*jshint white:false */
|
||||
/*jshint trailing:false */
|
||||
/*jshint newcap:false */
|
||||
/*global React, Router*/
|
||||
var app = app || {};
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
app.ALL_TODOS = 'all';
|
||||
app.ACTIVE_TODOS = 'active';
|
||||
app.COMPLETED_TODOS = 'completed';
|
||||
|
||||
app.Utils = {
|
||||
uuid: function () {
|
||||
/*jshint bitwise:false */
|
||||
var i, random;
|
||||
var uuid = '';
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
random = Math.random() * 16 | 0;
|
||||
if (i === 8 || i === 12 || i === 16 || i === 20) {
|
||||
uuid += '-';
|
||||
}
|
||||
uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random))
|
||||
.toString(16);
|
||||
}
|
||||
|
||||
return uuid;
|
||||
},
|
||||
|
||||
pluralize: function (count, word) {
|
||||
return count === 1 ? word : word + 's';
|
||||
},
|
||||
|
||||
extend: function () {
|
||||
var newObj = {};
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var obj = arguments[i];
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
newObj[key] = obj[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return newObj;
|
||||
}
|
||||
};
|
||||
|
||||
var Utils = app.Utils;
|
||||
// Generic "model" object. You can use whatever
|
||||
// framework you want. For this application it
|
||||
// may not even be worth separating this logic
|
||||
// out, but we do this to demonstrate one way to
|
||||
// separate out parts of your application.
|
||||
app.TodoModel = function (key) {
|
||||
this.key = key;
|
||||
this.todos = [];
|
||||
this.onChanges = [];
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.subscribe = function (onChange) {
|
||||
this.onChanges.push(onChange);
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.inform = function () {
|
||||
this.onChanges.forEach(function (cb) { cb(); });
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.addTodo = function (title) {
|
||||
this.todos = this.todos.concat({
|
||||
id: Utils.uuid(),
|
||||
title: title,
|
||||
completed: false
|
||||
});
|
||||
|
||||
this.inform();
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.toggleAll = function (checked) {
|
||||
// Note: it's usually better to use immutable data structures since they're
|
||||
// easier to reason about and React works very well with them. That's why
|
||||
// we use map() and filter() everywhere instead of mutating the array or
|
||||
// todo items themselves.
|
||||
this.todos = this.todos.map(function (todo) {
|
||||
return Utils.extend({}, todo, {completed: checked});
|
||||
});
|
||||
|
||||
this.inform();
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.toggle = function (todoToToggle) {
|
||||
this.todos = this.todos.map(function (todo) {
|
||||
return todo !== todoToToggle ?
|
||||
todo :
|
||||
Utils.extend({}, todo, {completed: !todo.completed});
|
||||
});
|
||||
|
||||
this.inform();
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.destroy = function (todo) {
|
||||
this.todos = this.todos.filter(function (candidate) {
|
||||
return candidate !== todo;
|
||||
});
|
||||
|
||||
this.inform();
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.save = function (todoToSave, text) {
|
||||
this.todos = this.todos.map(function (todo) {
|
||||
return todo !== todoToSave ? todo : Utils.extend({}, todo, {title: text});
|
||||
});
|
||||
|
||||
this.inform();
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.clearCompleted = function () {
|
||||
this.todos = this.todos.filter(function (todo) {
|
||||
return !todo.completed;
|
||||
});
|
||||
|
||||
this.inform();
|
||||
};
|
||||
|
||||
|
||||
var TodoFooter = React.createClass({
|
||||
render: function () {
|
||||
var activeTodoWord = app.Utils.pluralize(this.props.count, 'item');
|
||||
var clearButton = null;
|
||||
|
||||
if (this.props.completedCount > 0) {
|
||||
clearButton = (
|
||||
<button
|
||||
className="clear-completed"
|
||||
onClick={this.props.onClearCompleted}>
|
||||
Clear completed
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
var nowShowing = this.props.nowShowing;
|
||||
return (
|
||||
<footer className="footer">
|
||||
<span className="todo-count">
|
||||
<strong>{this.props.count}</strong> {activeTodoWord} left
|
||||
</span>
|
||||
<ul className="filters">
|
||||
<li>
|
||||
<a
|
||||
href="#/"
|
||||
className={classNames({selected: nowShowing === app.ALL_TODOS})}>
|
||||
All
|
||||
</a>
|
||||
</li>
|
||||
{' '}
|
||||
<li>
|
||||
<a
|
||||
href="#/active"
|
||||
className={classNames({selected: nowShowing === app.ACTIVE_TODOS})}>
|
||||
Active
|
||||
</a>
|
||||
</li>
|
||||
{' '}
|
||||
<li>
|
||||
<a
|
||||
href="#/completed"
|
||||
className={classNames({selected: nowShowing === app.COMPLETED_TODOS})}>
|
||||
Completed
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{clearButton}
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var ESCAPE_KEY = 27;
|
||||
var ENTER_KEY = 13;
|
||||
|
||||
var TodoItem = React.createClass({
|
||||
handleSubmit: function (event) {
|
||||
var val = this.state.editText.trim();
|
||||
if (val) {
|
||||
this.props.onSave(val);
|
||||
this.setState({editText: val});
|
||||
} else {
|
||||
this.props.onDestroy();
|
||||
}
|
||||
},
|
||||
|
||||
handleEdit: function () {
|
||||
this.props.onEdit();
|
||||
this.setState({editText: this.props.todo.title});
|
||||
},
|
||||
|
||||
handleKeyDown: function (event) {
|
||||
if (event.which === ESCAPE_KEY) {
|
||||
this.setState({editText: this.props.todo.title});
|
||||
this.props.onCancel(event);
|
||||
} else if (event.which === ENTER_KEY) {
|
||||
this.handleSubmit(event);
|
||||
}
|
||||
},
|
||||
|
||||
handleChange: function (event) {
|
||||
if (this.props.editing) {
|
||||
this.setState({editText: event.target.value});
|
||||
}
|
||||
},
|
||||
|
||||
getInitialState: function () {
|
||||
return {editText: this.props.todo.title};
|
||||
},
|
||||
|
||||
/**
|
||||
* Safely manipulate the DOM after updating the state when invoking
|
||||
* `this.props.onEdit()` in the `handleEdit` method above.
|
||||
* For more info refer to notes at https://facebook.github.io/react/docs/component-api.html#setstate
|
||||
* and https://facebook.github.io/react/docs/component-specs.html#updating-componentdidupdate
|
||||
*/
|
||||
componentDidUpdate: function (prevProps) {
|
||||
if (!prevProps.editing && this.props.editing) {
|
||||
var node = React.findDOMNode(this.refs.editField);
|
||||
node.focus();
|
||||
node.setSelectionRange(node.value.length, node.value.length);
|
||||
}
|
||||
},
|
||||
|
||||
render: function () {
|
||||
return (
|
||||
<li className={classNames({
|
||||
completed: this.props.todo.completed,
|
||||
editing: this.props.editing
|
||||
})}>
|
||||
<div className="view">
|
||||
<input
|
||||
className="toggle"
|
||||
type="checkbox"
|
||||
checked={this.props.todo.completed}
|
||||
onChange={this.props.onToggle}
|
||||
/>
|
||||
<label onDoubleClick={this.handleEdit}>
|
||||
{this.props.todo.title}
|
||||
</label>
|
||||
<button className="destroy" onClick={this.props.onDestroy} />
|
||||
</div>
|
||||
<input
|
||||
ref="editField"
|
||||
className="edit"
|
||||
value={this.state.editText}
|
||||
onBlur={this.handleSubmit}
|
||||
onChange={this.handleChange}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var ENTER_KEY = 13;
|
||||
|
||||
var TodoApp = React.createClass({
|
||||
getInitialState: function () {
|
||||
return {
|
||||
nowShowing: app.ALL_TODOS,
|
||||
editing: null,
|
||||
newTodo: ''
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function () {
|
||||
var setState = this.setState;
|
||||
var router = Router({
|
||||
'/': setState.bind(this, {nowShowing: app.ALL_TODOS}),
|
||||
'/active': setState.bind(this, {nowShowing: app.ACTIVE_TODOS}),
|
||||
'/completed': setState.bind(this, {nowShowing: app.COMPLETED_TODOS})
|
||||
});
|
||||
router.init('/');
|
||||
},
|
||||
|
||||
handleChange: function (event) {
|
||||
this.setState({newTodo: event.target.value});
|
||||
},
|
||||
|
||||
handleNewTodoKeyDown: function (event) {
|
||||
if (event.keyCode !== ENTER_KEY) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
var val = this.state.newTodo.trim();
|
||||
|
||||
if (val) {
|
||||
this.props.model.addTodo(val);
|
||||
this.setState({newTodo: ''});
|
||||
}
|
||||
},
|
||||
|
||||
toggleAll: function (event) {
|
||||
var checked = event.target.checked;
|
||||
this.props.model.toggleAll(checked);
|
||||
},
|
||||
|
||||
toggle: function (todoToToggle) {
|
||||
this.props.model.toggle(todoToToggle);
|
||||
},
|
||||
|
||||
destroy: function (todo) {
|
||||
this.props.model.destroy(todo);
|
||||
},
|
||||
|
||||
edit: function (todo) {
|
||||
this.setState({editing: todo.id});
|
||||
},
|
||||
|
||||
save: function (todoToSave, text) {
|
||||
this.props.model.save(todoToSave, text);
|
||||
this.setState({editing: null});
|
||||
},
|
||||
|
||||
cancel: function () {
|
||||
this.setState({editing: null});
|
||||
},
|
||||
|
||||
clearCompleted: function () {
|
||||
this.props.model.clearCompleted();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var footer;
|
||||
var main;
|
||||
var todos = this.props.model.todos;
|
||||
|
||||
var shownTodos = todos.filter(function (todo) {
|
||||
switch (this.state.nowShowing) {
|
||||
case app.ACTIVE_TODOS:
|
||||
return !todo.completed;
|
||||
case app.COMPLETED_TODOS:
|
||||
return todo.completed;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}, this);
|
||||
|
||||
var todoItems = shownTodos.map(function (todo) {
|
||||
return (
|
||||
<TodoItem
|
||||
todo={todo}
|
||||
onToggle={this.toggle.bind(this, todo)}
|
||||
onDestroy={this.destroy.bind(this, todo)}
|
||||
onEdit={this.edit.bind(this, todo)}
|
||||
editing={this.state.editing === todo.id}
|
||||
onSave={this.save.bind(this, todo)}
|
||||
onCancel={this.cancel}
|
||||
/>
|
||||
);
|
||||
}, this);
|
||||
|
||||
var activeTodoCount = todos.reduce(function (accum, todo) {
|
||||
return todo.completed ? accum : accum + 1;
|
||||
}, 0);
|
||||
|
||||
var completedCount = todos.length - activeTodoCount;
|
||||
|
||||
if (activeTodoCount || completedCount) {
|
||||
footer =
|
||||
<TodoFooter
|
||||
count={activeTodoCount}
|
||||
completedCount={completedCount}
|
||||
nowShowing={this.state.nowShowing}
|
||||
onClearCompleted={this.clearCompleted}
|
||||
/>;
|
||||
}
|
||||
|
||||
if (todos.length) {
|
||||
main = (
|
||||
<section className="main">
|
||||
<input
|
||||
className="toggle-all"
|
||||
type="checkbox"
|
||||
onChange={this.toggleAll}
|
||||
checked={activeTodoCount === 0}
|
||||
/>
|
||||
<ul className="todo-list">
|
||||
{todoItems}
|
||||
</ul>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<header className="header">
|
||||
<h1>todos</h1>
|
||||
<input
|
||||
className="new-todo"
|
||||
placeholder="What needs to be done?"
|
||||
value={this.state.newTodo}
|
||||
onKeyDown={this.handleNewTodoKeyDown}
|
||||
onChange={this.handleChange}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</header>
|
||||
{main}
|
||||
{footer}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var model = new app.TodoModel('react-todos');
|
||||
|
||||
function render() {
|
||||
ReactDOM.render(
|
||||
<TodoApp model={model}/>,
|
||||
document.getElementsByClassName('todoapp')[0]
|
||||
);
|
||||
}
|
||||
|
||||
model.subscribe(render);
|
||||
render();
|
||||
})();
|
||||
20
bench/implementations/react-15.3.1/license.md
Normal file
20
bench/implementations/react-15.3.1/license.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
Everything in this repo is MIT License unless otherwise specified.
|
||||
|
||||
Copyright (c) Addy Osmani, Sindre Sorhus, Pascal Hartig, Stephen Sawchuk.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
20
bench/implementations/react-15.3.1/package.json
Executable file
20
bench/implementations/react-15.3.1/package.json
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"make": "babel --plugins transform-react-jsx js/app.jsx | uglifyjs -o build.min.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"classnames": "^2.2.5",
|
||||
"director": "^1.2.8",
|
||||
"react": "^15.3.1",
|
||||
"react-dom": "^15.3.1",
|
||||
"todomvc-app-css": "^2.0.0",
|
||||
"todomvc-common": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.10.1",
|
||||
"babel-plugin-transform-react-jsx": "^6.8.0",
|
||||
"babel-plugin-uglify": "^1.0.2",
|
||||
"uglify-js": "^2.6.4"
|
||||
}
|
||||
}
|
||||
31
bench/implementations/react-15.3.1/readme.md
Executable file
31
bench/implementations/react-15.3.1/readme.md
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
# React TodoMVC for Benchmarking
|
||||
|
||||
This is an update of the React TodoMVC example to use React 15.1 and
|
||||
precompiled/minified JSX instead of the slow JSXTransformer. The idea is that
|
||||
this is a reasonable approximation of the performance characteristics of modern
|
||||
production React code.
|
||||
|
||||
This is not a perfect reflection of React best practices, because there is no
|
||||
such thing. If you ask five professional React users what current React best
|
||||
practices are, you'll get five different answers—each of which will benchmark
|
||||
differently. React Router, Immutable.js, Redux (with or without sagas), Ramda,
|
||||
ES2015 (with various Babel plugins), Webpack...the list never ends.
|
||||
|
||||
Rather than wading into that combinatorial explosion, this example is leaving
|
||||
the original React TodoMVC implementation's supporting stack alone, and only
|
||||
performing the following upgrades:
|
||||
|
||||
* Upgrade to React 15.1
|
||||
* Move everything into one JSX file and precompile it with Babel
|
||||
* Minify with uglify
|
||||
|
||||
If you're curious how it benchmarks with your particular stack, please fork
|
||||
this repo and find out!
|
||||
|
||||
# Building
|
||||
|
||||
1. `npm install`
|
||||
2. `npm run make`
|
||||
3. Open a local server (e.g. with `npm install -g http-server`) and open index.html
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue