Skip to content

Commit

Permalink
Merge pull request #192 from richieli/sonic-react
Browse files Browse the repository at this point in the history
add sonic-react
  • Loading branch information
vasdeveloper authored Jan 11, 2018
2 parents 839a46d + c803683 commit e1f7e30
Show file tree
Hide file tree
Showing 17 changed files with 1,116 additions and 0 deletions.
208 changes: 208 additions & 0 deletions sonic-react/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
## Getting started with React
[![license](http://img.shields.io/badge/license-BSD3-brightgreen.svg?style=flat)](https://github.com/Tencent/VasSonic/blob/master/LICENSE)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/Tencent/VasSonic/pulls)
[![wiki](https://img.shields.io/badge/Wiki-open-brightgreen.svg)](https://github.com/Tencent/VasSonic/wiki)
---

## How to use the demo

>This demo will give you a quick start for using Sonic with React.
### Dependencies
Node Version > 7.0
### Installation
```bash
git clone https://github.com/Tencent/VasSonic.git <my-project-name>
cd <my-project-name>/sonic-react
npm install # Install project dependencies
```
### Usage
```bash
npm run build # Builds the application to ./.next
npm start # Start the development server
```
Now you can visit http://localhost:3000/demo to view this demo using Mobile Emulation Mode in Chrome dev tools.

## How to use Sonic on server-side

>NOTE: This demo using Server Side Rendering (SSR) with [Redux](https://redux.js.org/), [Next.js](https://github.com/zeit/next.js/) and [Koa2](http://koajs.com/).
1. Add comment tags to separate **template** and **data blocks** in html files which will be published from the server. The **data blocks** should begin with a html comment like `<!-- sonicdiff-moduleName -->` and close with `<!-- sonicdiff-moduleName-end -->`(the moduleName is custom). And the other part of the html is called **template** in Sonic. In this demo, it is implemented like the code shown below.

- Below is the origin html, we will generate comment tags according to the `data-sonicdiff` attribute and the script block including `__NEXT_DATA__`:

```HTML
<!-- add comment tags to separate template and data blocks from the initial html -->
<!DOCTYPE html>
<html>
<head></head>
<body>
… …
<div id="root" data-sonicdiff="firstScreenHtml">
… …
</div>
… …
<script>
__NEXT_DATA__=xxx
</script>
</body>
</html>
```

- Then, we have a transform function at server side:

```js
function formatHtml(html) {
const $ = cheerio.load(html);
$('*[data-sonicdiff]').each(function(index, element) {
let tagName = $(this).data('sonicdiff');
return $(this).replaceWith('<!--sonicdiff-' + tagName + '-->' + $(this).clone() + '<!--sonicdiff-' + tagName + '-end-->');
});
html = $.html();
html = html.replace(/<script\s*>\s*__NEXT_DATA__\s*=([\s\S]+?)<\/script>/ig, function(data1) {
return '<!--sonicdiff-initState-->' + data1 + '<!--sonicdiff-initState-end-->';
});
return html;
}
```
- After the transform, the latest code user will received will be like:

```HTML
<!-- add comment tags to separate template and data blocks from the initial html -->
<!DOCTYPE html>
<html>
<head></head>
<body>
… …
+ <!-- sonicdiff-firstScreenHtml -->
<div id="root" data-sonicdiff="firstScreenHtml">
… …
</div>
+ <!-- sonicdiff-firstScreenHtml-end -->
… …
+ <!-- sonicdiff-initState -->
<script>
__NEXT_DATA__=xxx
</script>
+ <!-- sonicdiff-initState-end -->
</body>
</html>
```

2. Intercept the html response from server and use [sonic_differ](https://github.com/Tencent/VasSonic/blob/master/sonic-nodejs/common/diff.js) module to process the response.

```js
server.use(async (ctx, next) => {
await next();
// only intercept html request
if (!ctx.response.is('html')) {
return;
}
// process non-sonic mode
if (!ctx.request.header['accept-diff']) {
ctx.body = ctx.state.resHtml;
return;
}
// use sonic_differ module to process the response
let sonicData = sonicDiff(ctx, formatHtml(ctx.state.resHtml));
if (sonicData.cache) {
// 304 Not Modified, return nothing.
ctx.body = '';
} else {
// other Sonic status.
ctx.body = sonicData.data;
}
});
```

For more details please refer to [server.js](https://github.com/Tencent/VasSonic/blob/master/sonic-react/server.js).

## How to use Sonic on client-side

Handle the response from mobile client which include Sonic response code and diff data in `componentDidMount()`.

```js
componentDidMount() {
// handle the response from mobile client which include Sonic response code and diff data.
this.getSonicData((status, sonicUpdateData) => {
switch (status) {
// here, we only process the case when data updates
case 3:
// update the Redux store based on changes from the mobile client
let initState = sonicUpdateData['{initState}'] || '';
initState.replace(/<!--sonicdiff-initState-->\s*<script>\s*__NEXT_DATA__\s*=([\s\S]+?)module=/ig, function(matched, $1) {
window.__NEXT_DATA__ = JSON.parse($1);
});
this.props.initImgArr(window.__NEXT_DATA__.props.initialState.gameArea);
break;
default:
break
}
// display sonic status
this.props.setSonicStatus(status);
});
}

getSonicData(callback) {
let sonicHadExecute = 0; // whether the callback is triggered
const timeout = 3000; // a timeout to trigger callback

// Interacts with mobile client by JavaScript interface to get Sonic diff data.
window.sonic && window.sonic.getDiffData();

function sonicCallback(data) {
if (sonicHadExecute === 0) {
sonicHadExecute = 1;
callback(data['sonicStatus'], data['sonicUpdateData']);
}
}

setTimeout(function() {
if (sonicHadExecute === 0) {
sonicHadExecute = 1;
callback(0, {});
}
}, timeout);

// the mobile client will invoke method getDiffDataCallback which can send Sonic response code and diff data to websites.
window['getDiffDataCallback'] = function (sonicData) {
/**
* Sonic status:
* 0: It fails to get any data from mobile client.
* 1: It is first time for mobile client to use Sonic.
* 2: Mobile client reload the whole websites.
* 3: Websites will be updated dynamically with local refresh.
* 4: The Sonic request of mobile client receives a 304 response code and nothing has been modified.
*/
let sonicStatus = 0;
let sonicUpdateData = {}; // sonic diff data
sonicData = JSON.parse(sonicData);
switch (parseInt(sonicData['srcCode'], 10)) {
case 1000:
sonicStatus = 1;
break;
case 2000:
sonicStatus = 2;
break;
case 200:
sonicStatus = 3;
sonicUpdateData = JSON.parse(sonicData['result'] || '{}');
break;
case 304:
sonicStatus = 4;
break;
}
sonicCallback({ sonicStatus: sonicStatus, sonicUpdateData: sonicUpdateData });
};
}
```
For more details please refer to [demo.js](https://github.com/Tencent/VasSonic/blob/master/sonic-react/pages/demo.js)

## Support
Any problem?

1. Learn more from [sample](https://github.com/Tencent/VasSonic/tree/master/sonic-react).
2. Contact us for help.

## License
VasSonic is under the BSD license. See the [LICENSE](https://github.com/Tencent/VasSonic/blob/master/LICENSE) file for details.
Loading

0 comments on commit e1f7e30

Please sign in to comment.