Skip to content

Latest commit

 

History

History
208 lines (183 loc) · 7.44 KB

README.md

File metadata and controls

208 lines (183 loc) · 7.44 KB

Getting started with React

license PRs Welcome 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

git clone https://github.com/Tencent/VasSonic.git <my-project-name>
cd <my-project-name>/sonic-react
npm install  # Install project dependencies

Usage

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, Next.js and Koa2.

  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__:
<!-- 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:
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:
<!-- 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>
  1. Intercept the html response from server and use sonic_differ module to process the response.
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.

How to use Sonic on client-side

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

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

Support

Any problem?

  1. Learn more from sample.
  2. Contact us for help.

License

VasSonic is under the BSD license. See the LICENSE file for details.