├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── src ├── common │ ├── channel.js │ └── constants.js ├── page │ ├── NodeIDOperations.js │ ├── WorkerDomNodeImpl.js │ ├── index.js │ └── inject.js ├── webpack.config.js └── worker │ ├── ReactWWChildOperations.js │ ├── ReactWWComponent.js │ ├── ReactWWIDOperations.js │ ├── ReactWWInjection.js │ ├── ReactWWReconcileTransaction.js │ ├── ReactWWTextComponent.js │ ├── WorkerBridge.js │ ├── WorkerDomNodeStub.js │ └── index.js └── test ├── dbmonster ├── components │ ├── app.jsx │ └── data.js ├── index.html ├── main-normal.jsx ├── main-worker.js └── worker-impl.jsx ├── perf.js ├── todo ├── components │ ├── app.jsx │ ├── clock.jsx │ ├── todoForm.jsx │ └── todoItem.jsx ├── index.html ├── normal.jsx ├── worker-impl.jsx └── worker.jsx └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Parsahuram . All rights reserved. 2 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 3 | 4 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 5 | 6 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 7 | 8 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Renderer using Web Workers 2 | 3 | A React Custom renderer using Web Workers. All the Virtual DOM reconcilliations happen in a WebWorker thread. Only node updates are sent over to the UI thread, result in a much more responsive UI. 4 | 5 | An existing React application can leverage WebWorkers using this library with minimal change. Look at the usage section for details. 6 | 7 | ## Demo 8 | 9 | The demo is hosted at [http://web-perf.github.io/react-worker-dom/](http://web-perf.github.io/react-worker-dom/). To run a local version of the demo, 10 | 11 | - Clone the repo run `npm install` to install all dependencies. 12 | - Build the app using `npm run demo` 13 | - Open `http://localhost:8080/test/dbmonster/` to view the demo app, or `http://localhost:8080/test/todo` for the todo app. 14 | - Tweak the params in the URL to change to use web workers, increase number of components, etc. 15 | 16 | ## Usage 17 | 18 | ### A typical React application 19 | 20 | A typical React application would looks something like the following. 21 | 22 | ```js 23 | // File: main.jsx 24 | import React from 'react'; 25 | import reactDOM from 'react-dom'; 26 | reactDOM.render(, document.getElementById('container')); 27 | ``` 28 | 29 | ### Using it with Web Workers 30 | 31 | To use this renderer, we would need to split the above file into 2 parts, one that is on the page, and another that starts as a web worker. 32 | 33 | 34 | ```js 35 | // File: main.js - included using a script tag in index.html 36 | import React from 'react'; 37 | import reactDOM from 'react-worker-dom'; // Instead of using react-dom 38 | reactDOM.render(new Worker('worker.js'), document.getElementById('container')); 39 | ``` 40 | 41 | The `worker.js` file is the one that now holds the actual Component. 42 | 43 | ```js 44 | // File: worker.jsx - loaded in index.html using new Worker('worker.jsx') in the file script above; 45 | import React from 'react'; 46 | import ReactWorkerDOM from 'react-worker-dom-worker'; 47 | ReactWorkerDOM.render(); 48 | ``` 49 | 50 | Look at `test\dbmonster` and `test\todoapp` directory for the examples. 51 | 52 | ## Testing Performance 53 | 54 | To manually look at frame rates, load the dbmonster [demo pages](http://web-perf.github.io/react-worker-dom/) in Chrome, and view the [frame meter](https://developer.chrome.com/devtools/docs/rendering-settings#show-fps%20meter) in devtools. 55 | 56 | To automatically collect frame rates and compare it with the normal version 57 | - Run `npm run demo` to start the server and host the web pages 58 | - Run `npm run perf chrome worker` to test frame rates for various rows in chrome in a Web Worker. Instead of `chrome`, you could use `android`, and instead of `worker`, you could use `normal` to test the other combinations. 59 | - The frame rates are available in `_dbmonster.json` file, for each row count. 60 | 61 | ## Roadmap 62 | Here are the things that need to be done next. 63 | 64 | - Add support for form elements like ``, ` 22 | 23 | 26 | 27 | 28 | 29 | {this.state.text ? this.state.text : ''} 30 |  will be added to the list 31 | 32 | 33 | ); 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /test/todo/components/todoItem.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | module.exports = React.createClass({ 4 | onDelete: function() { 5 | this.props.onDelete(this.props.index); 6 | }, 7 | 8 | onToggle: function(e) { 9 | this.props.onToggle(this.props.index, e.target.checked); 10 | }, 11 | 12 | moveUp: function(e){ 13 | this.props.onMove(this.props.index, -1); 14 | }, 15 | 16 | moveDown: function(){ 17 | this.props.onMove(this.props.index, 1); 18 | }, 19 | 20 | render: function() { 21 | return ( 22 |
  • 23 |
    24 | 25 | 26 |    27 | 28 | 29 |
    30 | 31 |    32 | 33 | {this.props.item.text} 34 | 35 |
  • 36 | ); 37 | } 38 | }); -------------------------------------------------------------------------------- /test/todo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React-Dom-Worker:: TODO app 8 | 9 | 10 | 17 | 18 | 19 | 20 |
    21 |
    22 | 23 | Implemented using Webworkers. View normal version. 24 | 25 | 26 | Implemented with plan react. No webworkers used. View version with web workers 27 | 28 |
    29 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /test/todo/normal.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {render} from 'react-dom'; 3 | 4 | import App from './components/app.jsx'; 5 | 6 | render( , document.getElementById('content')); 7 | -------------------------------------------------------------------------------- /test/todo/worker-impl.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {render} from 'react-worker-dom-worker'; 3 | 4 | import App from './components/app.jsx'; 5 | 6 | render(); 7 | -------------------------------------------------------------------------------- /test/todo/worker.jsx: -------------------------------------------------------------------------------- 1 | 2 | import {render} from 'react-worker-dom'; 3 | 4 | 5 | 6 | render(new Worker('/react-worker-dom/dist/todo/worker-impl.js'), document.getElementById('content')); 7 | -------------------------------------------------------------------------------- /test/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | module.exports = { 3 | context: __dirname, 4 | entry: { 5 | // DB Monster 6 | 'dbmonster/main-normal': './dbmonster/main-normal.jsx', 7 | 'dbmonster/main-worker': './dbmonster/main-worker.js', 8 | 'dbmonster/worker-impl': './dbmonster/worker-impl.jsx', 9 | 10 | // ToDo App 11 | 'todo/normal': './todo/normal.jsx', 12 | 'todo/worker': './todo/worker.jsx', 13 | 'todo/worker-impl': './todo/worker-impl.jsx' 14 | }, 15 | output: { 16 | filename: '[name].js', 17 | path: path.join(__dirname, '../dist'), 18 | publicPath: '/react-worker-dom/dist' 19 | }, 20 | devtool: 'source-map', 21 | module: { 22 | loaders: [{ 23 | test: /\.jsx?$/, 24 | loader: 'babel-loader?presets[]=es2015&presets[]=react&presets[]=stage-0', 25 | }] 26 | }, 27 | resolve: { 28 | alias: { 29 | 'react-worker-dom': path.resolve(__dirname, './../src/page/index.js'), 30 | 'react-worker-dom-worker': path.resolve(__dirname, './../src/worker/index.js') 31 | } 32 | }, 33 | }; 34 | --------------------------------------------------------------------------------