Skip to content

Commit e1f7e30

Browse files
authored
Merge pull request #192 from richieli/sonic-react
add sonic-react
2 parents 839a46d + c803683 commit e1f7e30

File tree

17 files changed

+1116
-0
lines changed

17 files changed

+1116
-0
lines changed

sonic-react/README.md

+208
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
## Getting started with React
2+
[![license](http://img.shields.io/badge/license-BSD3-brightgreen.svg?style=flat)](https://github.com/Tencent/VasSonic/blob/master/LICENSE)
3+
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/Tencent/VasSonic/pulls)
4+
[![wiki](https://img.shields.io/badge/Wiki-open-brightgreen.svg)](https://github.com/Tencent/VasSonic/wiki)
5+
---
6+
7+
## How to use the demo
8+
9+
>This demo will give you a quick start for using Sonic with React.
10+
11+
### Dependencies
12+
Node Version > 7.0
13+
### Installation
14+
```bash
15+
git clone https://github.com/Tencent/VasSonic.git <my-project-name>
16+
cd <my-project-name>/sonic-react
17+
npm install # Install project dependencies
18+
```
19+
### Usage
20+
```bash
21+
npm run build # Builds the application to ./.next
22+
npm start # Start the development server
23+
```
24+
Now you can visit http://localhost:3000/demo to view this demo using Mobile Emulation Mode in Chrome dev tools.
25+
26+
## How to use Sonic on server-side
27+
28+
>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/).
29+
30+
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.
31+
32+
- Below is the origin html, we will generate comment tags according to the `data-sonicdiff` attribute and the script block including `__NEXT_DATA__`:
33+
34+
```HTML
35+
<!-- add comment tags to separate template and data blocks from the initial html -->
36+
<!DOCTYPE html>
37+
<html>
38+
<head></head>
39+
<body>
40+
… …
41+
<div id="root" data-sonicdiff="firstScreenHtml">
42+
… …
43+
</div>
44+
… …
45+
<script>
46+
__NEXT_DATA__=xxx
47+
</script>
48+
</body>
49+
</html>
50+
```
51+
52+
- Then, we have a transform function at server side:
53+
54+
```js
55+
function formatHtml(html) {
56+
const $ = cheerio.load(html);
57+
$('*[data-sonicdiff]').each(function(index, element) {
58+
let tagName = $(this).data('sonicdiff');
59+
return $(this).replaceWith('<!--sonicdiff-' + tagName + '-->' + $(this).clone() + '<!--sonicdiff-' + tagName + '-end-->');
60+
});
61+
html = $.html();
62+
html = html.replace(/<script\s*>\s*__NEXT_DATA__\s*=([\s\S]+?)<\/script>/ig, function(data1) {
63+
return '<!--sonicdiff-initState-->' + data1 + '<!--sonicdiff-initState-end-->';
64+
});
65+
return html;
66+
}
67+
```
68+
- After the transform, the latest code user will received will be like:
69+
70+
```HTML
71+
<!-- add comment tags to separate template and data blocks from the initial html -->
72+
<!DOCTYPE html>
73+
<html>
74+
<head></head>
75+
<body>
76+
… …
77+
+ <!-- sonicdiff-firstScreenHtml -->
78+
<div id="root" data-sonicdiff="firstScreenHtml">
79+
… …
80+
</div>
81+
+ <!-- sonicdiff-firstScreenHtml-end -->
82+
… …
83+
+ <!-- sonicdiff-initState -->
84+
<script>
85+
__NEXT_DATA__=xxx
86+
</script>
87+
+ <!-- sonicdiff-initState-end -->
88+
</body>
89+
</html>
90+
```
91+
92+
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.
93+
94+
```js
95+
server.use(async (ctx, next) => {
96+
await next();
97+
// only intercept html request
98+
if (!ctx.response.is('html')) {
99+
return;
100+
}
101+
// process non-sonic mode
102+
if (!ctx.request.header['accept-diff']) {
103+
ctx.body = ctx.state.resHtml;
104+
return;
105+
}
106+
// use sonic_differ module to process the response
107+
let sonicData = sonicDiff(ctx, formatHtml(ctx.state.resHtml));
108+
if (sonicData.cache) {
109+
// 304 Not Modified, return nothing.
110+
ctx.body = '';
111+
} else {
112+
// other Sonic status.
113+
ctx.body = sonicData.data;
114+
}
115+
});
116+
```
117+
118+
For more details please refer to [server.js](https://github.com/Tencent/VasSonic/blob/master/sonic-react/server.js).
119+
120+
## How to use Sonic on client-side
121+
122+
Handle the response from mobile client which include Sonic response code and diff data in `componentDidMount()`.
123+
124+
```js
125+
componentDidMount() {
126+
// handle the response from mobile client which include Sonic response code and diff data.
127+
this.getSonicData((status, sonicUpdateData) => {
128+
switch (status) {
129+
// here, we only process the case when data updates
130+
case 3:
131+
// update the Redux store based on changes from the mobile client
132+
let initState = sonicUpdateData['{initState}'] || '';
133+
initState.replace(/<!--sonicdiff-initState-->\s*<script>\s*__NEXT_DATA__\s*=([\s\S]+?)module=/ig, function(matched, $1) {
134+
window.__NEXT_DATA__ = JSON.parse($1);
135+
});
136+
this.props.initImgArr(window.__NEXT_DATA__.props.initialState.gameArea);
137+
break;
138+
default:
139+
break
140+
}
141+
// display sonic status
142+
this.props.setSonicStatus(status);
143+
});
144+
}
145+
146+
getSonicData(callback) {
147+
let sonicHadExecute = 0; // whether the callback is triggered
148+
const timeout = 3000; // a timeout to trigger callback
149+
150+
// Interacts with mobile client by JavaScript interface to get Sonic diff data.
151+
window.sonic && window.sonic.getDiffData();
152+
153+
function sonicCallback(data) {
154+
if (sonicHadExecute === 0) {
155+
sonicHadExecute = 1;
156+
callback(data['sonicStatus'], data['sonicUpdateData']);
157+
}
158+
}
159+
160+
setTimeout(function() {
161+
if (sonicHadExecute === 0) {
162+
sonicHadExecute = 1;
163+
callback(0, {});
164+
}
165+
}, timeout);
166+
167+
// the mobile client will invoke method getDiffDataCallback which can send Sonic response code and diff data to websites.
168+
window['getDiffDataCallback'] = function (sonicData) {
169+
/**
170+
* Sonic status:
171+
* 0: It fails to get any data from mobile client.
172+
* 1: It is first time for mobile client to use Sonic.
173+
* 2: Mobile client reload the whole websites.
174+
* 3: Websites will be updated dynamically with local refresh.
175+
* 4: The Sonic request of mobile client receives a 304 response code and nothing has been modified.
176+
*/
177+
let sonicStatus = 0;
178+
let sonicUpdateData = {}; // sonic diff data
179+
sonicData = JSON.parse(sonicData);
180+
switch (parseInt(sonicData['srcCode'], 10)) {
181+
case 1000:
182+
sonicStatus = 1;
183+
break;
184+
case 2000:
185+
sonicStatus = 2;
186+
break;
187+
case 200:
188+
sonicStatus = 3;
189+
sonicUpdateData = JSON.parse(sonicData['result'] || '{}');
190+
break;
191+
case 304:
192+
sonicStatus = 4;
193+
break;
194+
}
195+
sonicCallback({ sonicStatus: sonicStatus, sonicUpdateData: sonicUpdateData });
196+
};
197+
}
198+
```
199+
For more details please refer to [demo.js](https://github.com/Tencent/VasSonic/blob/master/sonic-react/pages/demo.js)
200+
201+
## Support
202+
Any problem?
203+
204+
1. Learn more from [sample](https://github.com/Tencent/VasSonic/tree/master/sonic-react).
205+
2. Contact us for help.
206+
207+
## License
208+
VasSonic is under the BSD license. See the [LICENSE](https://github.com/Tencent/VasSonic/blob/master/LICENSE) file for details.

0 commit comments

Comments
 (0)