Add todoapp benchmark
This commit is contained in:
parent
479db8c2cc
commit
02e7b40c07
218 changed files with 63168 additions and 0 deletions
1
bench/implementations/angular-1.5.8-optimized/build.min.js
vendored
Executable file
1
bench/implementations/angular-1.5.8-optimized/build.min.js
vendored
Executable file
|
|
@ -0,0 +1 @@
|
|||
(function(){"use strict";angular.module("todoCtrl",[]).controller("TodoCtrl",function TodoCtrl($scope,$location){var TC=this;var todos=TC.todos=[];TC.ESCAPE_KEY=27;TC.editedTodo={};TC.updateRemainingCount=function(){TC.remainingCount=todos.filter(function(todo){return!todo.completed}).length;TC.allChecked=TC.remainingCount===0};function resetTodo(){TC.newTodo={title:"",completed:false}}resetTodo();if($location.path()===""){$location.path("/")}TC.location=$location;$scope.$watch("TC.location.path()",function(path){TC.statusFilter={"/active":{completed:false},"/completed":{completed:true}}[path]});TC.addTodo=function(){var newTitle=TC.newTodo.title=TC.newTodo.title.trim();if(newTitle.length===0){return}todos.push(TC.newTodo);resetTodo();TC.updateRemainingCount()};TC.editTodo=function(todo){TC.editedTodo=todo;TC.originalTodo=angular.copy(todo)};TC.doneEditing=function(todo,index){TC.editedTodo={};todo.title=todo.title.trim();if(!todo.title){TC.removeTodo(index)}TC.updateRemainingCount()};TC.revertEditing=function(index){TC.editedTodo={};todos[index]=TC.originalTodo;TC.updateRemainingCount()};TC.removeTodo=function(index){todos.splice(index,1);TC.updateRemainingCount()};TC.clearCompletedTodos=function(){TC.todos=todos=todos.filter(function(val){return!val.completed});TC.updateRemainingCount()};TC.markAll=function(completed){todos.forEach(function(todo){todo.completed=completed});TC.updateRemainingCount()}});angular.module("todoFocus",[]).directive("todoFocus",function($timeout){return function(scope,elem,attrs){scope.$watch(attrs.todoFocus,function(newVal){if(newVal){$timeout(function(){elem[0].focus()},0,false)}})}});angular.module("todomvc",["todoCtrl","todoFocus"])})();
|
||||
77
bench/implementations/angular-1.5.8-optimized/index.html
Executable file
77
bench/implementations/angular-1.5.8-optimized/index.html
Executable file
|
|
@ -0,0 +1,77 @@
|
|||
<!doctype html>
|
||||
<html lang="en" ng-app="todomvc" data-framework="angularjs">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>AngularJS • TodoMVC</title>
|
||||
<link rel="stylesheet" href="node_modules/todomvc-common/base.css">
|
||||
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
|
||||
<style>
|
||||
[ng-cloak] {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<section class="todoapp" ng-controller="TodoCtrl as TC">
|
||||
<header class="header">
|
||||
<h1>todos</h1>
|
||||
<form id="todo-form">
|
||||
<input class="new-todo" placeholder="What needs to be done?" ng-model="TC.newTodo.title" autofocus ng-keydown="($event.keyCode === 13) && TC.addTodo()">
|
||||
</form>
|
||||
</header>
|
||||
<section class="main" ng-show="TC.todos.length" ng-cloak>
|
||||
<input class="toggle-all" type="checkbox" ng-model="TC.allChecked" ng-click="TC.markAll(TC.allChecked)">
|
||||
<label for="toggle-all">Mark all as complete</label>
|
||||
<ul class="todo-list">
|
||||
<li ng-repeat="todo in TC.todos | filter:TC.statusFilter" ng-class="{completed: todo.completed, editing: todo === TC.editedTodo}">
|
||||
<div class="view">
|
||||
<input class="toggle" type="checkbox" ng-model="todo.completed" ng-change="TC.updateAndStore()">
|
||||
<label ng-dblclick="TC.editTodo(todo)">{{todo.title}}</label>
|
||||
<button class="destroy" ng-click="TC.removeTodo($index)"></button>
|
||||
</div>
|
||||
<form ng-submit="TC.doneEditing(todo, $index)">
|
||||
<input class="edit" ng-trim="false" ng-model="todo.title" ng-blur="TC.doneEditing(todo, $index)" ng-keydown="($event.keyCode === TC.ESCAPE_KEY) && TC.revertEditing($index)" todo-focus="todo === TC.editedTodo">
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<footer class="footer" ng-show="TC.todos.length" ng-cloak>
|
||||
<span class="todo-count"><strong>{{TC.remainingCount}}</strong>
|
||||
<ng-pluralize count="TC.remainingCount" when="{ one: 'item left', other: 'items left' }"></ng-pluralize>
|
||||
</span>
|
||||
<ul class="filters">
|
||||
<li>
|
||||
<a ng-class="{selected: TC.location.path() == '/'} " href="#/">All</a>
|
||||
</li>
|
||||
<li>
|
||||
<a ng-class="{selected: TC.location.path() == '/active'}" href="#/active">Active</a>
|
||||
</li>
|
||||
<li>
|
||||
<a ng-class="{selected: TC.location.path() == '/completed'}" href="#/completed">Completed</a>
|
||||
</li>
|
||||
</ul>
|
||||
<button class="clear-completed" ng-click="TC.clearCompletedTodos()" ng-show="TC.remainingCount < TC.todos.length">Clear completed</button>
|
||||
</footer>
|
||||
</section>
|
||||
<footer class="info">
|
||||
<p>Double-click to edit a todo</p>
|
||||
<p>Credits:
|
||||
<a href="http://twitter.com/cburgdorf">Christoph Burgdorf</a>,
|
||||
<a href="http://ericbidelman.com">Eric Bidelman</a>,
|
||||
<a href="http://jacobmumm.com">Jacob Mumm</a> and
|
||||
<a href="http://blog.igorminar.com">Igor Minar</a>
|
||||
</p>
|
||||
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
|
||||
</footer>
|
||||
<script>
|
||||
window.requestAnimationFrame = window.webkitRequestAnimationFrame = function(callback) {
|
||||
callback();
|
||||
};
|
||||
</script>
|
||||
<script src="node_modules/angular/angular.js"></script>
|
||||
<script src="build.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
118
bench/implementations/angular-1.5.8-optimized/js/app.js
Executable file
118
bench/implementations/angular-1.5.8-optimized/js/app.js
Executable file
|
|
@ -0,0 +1,118 @@
|
|||
/* jshint undef: true, unused: true */
|
||||
/*global angular */
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular.module('todoCtrl', [])
|
||||
|
||||
/**
|
||||
* The main controller for the app. The controller:
|
||||
* - retrieves and persists the model via the todoStorage service
|
||||
* - exposes the model to the template and provides event handlers
|
||||
*/
|
||||
.controller('TodoCtrl', function TodoCtrl($scope, $location) {
|
||||
var TC = this;
|
||||
var todos = TC.todos = [];
|
||||
|
||||
TC.ESCAPE_KEY = 27;
|
||||
TC.editedTodo = {};
|
||||
|
||||
TC.updateRemainingCount = function () {
|
||||
TC.remainingCount = todos.filter(function (todo) { return !todo.completed; }).length;
|
||||
TC.allChecked = (TC.remainingCount === 0);
|
||||
}
|
||||
|
||||
function resetTodo() {
|
||||
TC.newTodo = {title: '', completed: false};
|
||||
}
|
||||
|
||||
resetTodo();
|
||||
|
||||
if ($location.path() === '') {
|
||||
$location.path('/');
|
||||
}
|
||||
|
||||
TC.location = $location;
|
||||
|
||||
$scope.$watch('TC.location.path()', function (path) {
|
||||
TC.statusFilter = { '/active': {completed: false}, '/completed': {completed: true} }[path];
|
||||
});
|
||||
|
||||
TC.addTodo = function () {
|
||||
var newTitle = TC.newTodo.title = TC.newTodo.title.trim();
|
||||
if (newTitle.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
todos.push(TC.newTodo);
|
||||
resetTodo();
|
||||
TC.updateRemainingCount();
|
||||
};
|
||||
|
||||
TC.editTodo = function (todo) {
|
||||
TC.editedTodo = todo;
|
||||
|
||||
// Clone the original todo to restore it on demand.
|
||||
TC.originalTodo = angular.copy(todo);
|
||||
};
|
||||
|
||||
TC.doneEditing = function (todo, index) {
|
||||
TC.editedTodo = {};
|
||||
todo.title = todo.title.trim();
|
||||
|
||||
if (!todo.title) {
|
||||
TC.removeTodo(index);
|
||||
}
|
||||
TC.updateRemainingCount();
|
||||
};
|
||||
|
||||
TC.revertEditing = function (index) {
|
||||
TC.editedTodo = {};
|
||||
todos[index] = TC.originalTodo;
|
||||
TC.updateRemainingCount();
|
||||
};
|
||||
|
||||
TC.removeTodo = function (index) {
|
||||
todos.splice(index, 1);
|
||||
TC.updateRemainingCount();
|
||||
};
|
||||
|
||||
TC.clearCompletedTodos = function () {
|
||||
TC.todos = todos = todos.filter(function (val) {
|
||||
return !val.completed;
|
||||
});
|
||||
TC.updateRemainingCount();
|
||||
};
|
||||
|
||||
TC.markAll = function (completed) {
|
||||
todos.forEach(function (todo) {
|
||||
todo.completed = completed;
|
||||
});
|
||||
TC.updateRemainingCount();
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
angular.module('todoFocus', [])
|
||||
|
||||
/**
|
||||
* Directive that places focus on the element it is applied to when the expression it binds to evaluates to true
|
||||
*/
|
||||
.directive('todoFocus', function ($timeout) {
|
||||
return function (scope, elem, attrs) {
|
||||
scope.$watch(attrs.todoFocus, function (newVal) {
|
||||
if (newVal) {
|
||||
$timeout(function () {
|
||||
elem[0].focus();
|
||||
}, 0, false);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
/**
|
||||
* The main TodoMVC app module that pulls all dependency modules declared in same named files
|
||||
*
|
||||
* @type {angular.Module}
|
||||
*/
|
||||
angular.module('todomvc', ['todoCtrl', 'todoFocus']);
|
||||
})();
|
||||
14
bench/implementations/angular-1.5.8-optimized/package.json
Normal file
14
bench/implementations/angular-1.5.8-optimized/package.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"make": "uglifyjs js/app.js -o build.min.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"angular": "^1.5.8",
|
||||
"todomvc-app-css": "^2.0.6",
|
||||
"todomvc-common": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"uglify-js": "^2.6.4"
|
||||
}
|
||||
}
|
||||
25
bench/implementations/angular-1.5.8-optimized/readme.md
Executable file
25
bench/implementations/angular-1.5.8-optimized/readme.md
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
# Angular TodoMVC for Benchmarking
|
||||
|
||||
This is an update of the
|
||||
_[Angular TodoMVC example](https://github.com/tastejs/todomvc/tree/gh-pages/examples/angularjs-perf)_
|
||||
with the following changes:
|
||||
|
||||
* Upgrade to Angular 1.5.7
|
||||
* Update todomvc-app-css to 2.0.6 to match other examples in this benchmark
|
||||
* Remove localStorage functionality - not relevant to measuring render performance
|
||||
* Concat JS into one file
|
||||
* Minify with uglify
|
||||
|
||||
and additional performance optimizations:
|
||||
|
||||
#### Removed the deep $watch in favor of direct function calls
|
||||
The original example uses $scope.$watch with object equality enabled to track changes to the array of todos and perform actions when changes are detected. This is not a good practice as it causes a deep array comparison to happen every digest cycle, which can significantly degrade performance when working with large collections. Instead we can perform the required actions directly whenever todos are modified, since we control all points of possible modification.
|
||||
|
||||
#### Removed "track by $index" from the ng-repeat directive
|
||||
The original example uses the ng-repeat directive with a "track by $index" clause to display the list of todos. _[Refer to section "Tracking and Duplicates" here for information on using "track by".](https://code.angularjs.org/1.5.7/docs/api/ng/directive/ngRepeat)_ In this particular case it is not necessary to use it, as long as care is taken not to persist the "$$hashKey" property added by Angular to local storage, which would cause duplicates to appear when the collection is loaded and new items are added, triggering an exception. The version without the "track by" clause could reasonably be written by someone implementing this from scratch and demonstrates better performance.
|
||||
|
||||
# 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