# [Tranel](https://github.com/etoah/tranel)
[![](https://img.shields.io/badge/Powered%20By%20Plugge-brightgreen.svg)](https://www.npmjs.com/package/plugge)

在Worker内的使用DOM, 或其它主线程的API.

## 特点

- 像渲染线程一样操作DOM
- 可能代理主线程的任意方法，比Worker-DOM应用范围更广， 性能更佳([Tranel VS WorkerDOM Render Big Table](./docs/Tranel-WorkerDOM.md))

### Demo: 
1. [PlayVideo](http://tranel.pages.oa.com/demo/worker-video/)
2. [fibonacci](http://tranel.pages.oa.com/demo/fibonacci/)

### Benchmarks：
- 大列表渲染性能(12018个DOM节点)
  1. [Tranel 渲染](http://tranel.pages.oa.com/benchmarks/big-table/tranel-turbo.html)
  2. [Worker-DOM 渲染](http://tranel.pages.oa.com/benchmarks/big-table/worker-dom.html)

## Usage 

```bash
$ npm install --save tranel-dom(待发布)
```
在渲染线程中引用：

```html
  <script src="../../lib/tranel-dom.js"></script>
  <script>
    new TranelDOM().proxy({
      workerUrl: "./worker.js",
      proxyObject: window,
      hookGlobal: true
    });
  </script>
```
在Worker线程(worker.js)：

```javascript
importScripts("../../lib/tranel-dom.js");
TranelDOM()。proxy({ hookGlobal: true, proxyObject: self });
```

可以像渲染线程一样处理DOM

```javascript
let $wcomputeButton = document.getElementById('wcompute');
let $wresult = document.getElementById('wresult');
let $input = document.getElementById('input')
$wcomputeButton.addEventListener('click', async () => {
  $wresult.innerText = $input.value
  
  console.log($input.value) //读取不可序列化对象的变量
})

// 浏览器实现 proposal-weakrefs 后可以手动清除
T.clean($wcomputeButton) // 不用时可清除

```


**注意**
1. 读取时是异步操作, 比如(`let title = await(T(T.document.title))`);
2. 传引用(不可序列化的对象)时会增加引用计数的数量， 可能导致不能回收可以调用T.clean(例:`T.clean(event)`)清除引用, [WeakFactory](https://github.com/tc39/proposal-weakrefs)实现后可以自动管理引用和内存。


打开 [demo](./demo/) 获取更多信息.

**注意**
1. 读取时是异步操作, 比如(`let title = await(T(T.document.title))`);
2. 传引用(不可序列化的对象)时会增加引用计数的数量， 可能导致不能回收可以调用T.clean(例:`T.clean(event)`)清除引用, [WeakFactory](https://github.com/tc39/proposal-weakrefs)实现后可以自动管理引用和内存。


## 与Worker-DOM的区别
Worker-DOM是在worker(WorkerGlobalScope)环境中, 实现一套DOM API, 再发到主线程执行, 工作量大，需要实例化虚拟的WorkerDOM元素， 性能([benchmarks](./benchmarks/big-table))欠佳. Tranel是用`Proxy`代理worker中的操作并发到主线程执行。

具体的性能对比数据：请查看[Tranel VS WorkerDOM Render Big Table](./docs/Tranel-WorkerDOM.md)

由于用了Proxy, Tranel可以调用任意的主线程的全局的API.

## 性能

测试环境： i7-6700@3.4GHz

### 1 WebWorker启动性能

**启动 6ms左右**， 相关的其它性能也可以查看[Examining Web Worker Performance](https://www.loxodrome.io/post/web-worker-performance/)

### 2 Proxy生成指令的性能

**778Q/MS**, 即1ms可生成778个指令,详情查看([big-table benchmarks](./benchmarks/big-table)), 示例中35016个指令生成需要45ms
> 这个时间包括参数的序列化， 与参数的复杂度有关， 如果参数是大json, 时间会变长。

### 3 Worker与主线程传输性能

#### 3.1 结构化数据
**40KB/MS**: 暴力测试发现1679kb, 用时40ms左右, 实测40kb/ms, 对于指令的传输完全足够.

#### 3.2 线性数据
**4.8MB/MS**: 对于线性结构可以用 [Transferable Objects](https://html.spec.whatwg.org/multipage/structured-data.html#transferable-objects), 通过传引用(而不是拷贝)， 速度基本不受大小影响(可能会应分配内存影响)。 按[Chrome团队的测试](https://developers.google.com/web/updates/2011/12/Transferable-Objects-Lightning-Fast), 可到达4.8MB/ms, 可以用于传输一些线性或已序列化的数据.


## 兼容性

支持所有现在现代浏览器(`Proxy`)，对于不支持`Proxy`的， 可用`proxy-polyfill`可兼容 IE9+, Safari 6+等。


## 开发指南

安装依赖

```bash
$ npm install
```
To dev & serve demo

```bash
$ npm run dev
```

To build the project:

```bash
$ npm run build
```

To run unit tests:

```bash
$ npm t
```


## Documents(待完成)
[API](./docs/api.md)

## Directory
```
├── demo - Using demo
├── benchmarks 
├── dist - Compiler output code
├── docs - Project documents
├── src - Source code directory
├── test - Unit tests
├── CHANGELOG.md - Change log

```

## 相关链接

- [worker-dom](https://github.com/ampproject/worker-dom)
- [Chrome AMP: WorkerDOM: Concurrency for JavaScript programming with the DOM](https://amphtml.wordpress.com/2018/08/21/workerdom/)
