the way of mobile baidu front end engineering

Posted by fierce at 2020-02-26

Introduction to Scorpio system

Background introduction

Mature teams should not stay in the stage of showing off skills and following the trend


Based on the current situation, lay out the future

There are too many SVN modules. To make a unified front-end solution, the public code can only be maintained separately through the SVN: externals attribute

Front end common SVN

Detach the front common from the SVN of template

common ├─demo # demo示例目录,不上线 ├─docs # 文档目录,jsdoc生成,svn忽略 ├─plugin # smarty插件和解决方案 ├─static # 静态资源 │ ├─bdbox # js模块化库 │ ├─css │ ├─img │ └─js ├─test # 放fis的调试json文件,用于本地开发,不上线 │ └─page │ └─xxx ├─widget # widget目录 └─nodejsLib # nodejs工具

The redundant code is separated, and the dependency between modules is clear, which is convenient for static resource management?

Thinking about using and not using the third party Library

Zebto + customized amd library bdbox

Bdbox module library

18 categories, 50 + modules, covering all application scenarios of mobile Baidu

Management of modules


Module writing

/** * 简单模板 * @memberOf Bdbox.utils * @name template * @param {String} html 模板String内容 * @param {Object} data 模板data对象 * @return {String} 返回处理后的模板 * @author wangyongqing01 * @version $Id: template.js 175996 2014-05-16 00:48:03Z wangyongqing01 $ * @example * var t = Bdbox.utils.template('I am <%=name%>', {name:'Theo Wang'}); * // I am Theo Wang * console.log(t); */ module.exports = function(html, data) { for (var i in data) { html = html.replace(new RegExp('<%=\\s*' + i + '\\s*%>', 'g'), data[i]); } return html; };

The parent template is the solution, the child template focuses on business, and the parent-child inheritance relationship

Template splitting scheme

tpl/layout tpl/page {%extends file=$tplData.parentTplPath %} {%block name="head"%}...{%/block%} {%block name="body"%}...{%/block%} {%html framework="common:bdbox" rendermode="inline|tag|combo"%}

Introduction to page rendering mode

Inline mode

Online environment, suitable for slow network

Tag mode

Offline environment, suitable for debugging

Combo mode

Online environment, suitable for 3G + network

Nginx combo service:

Intelligently switch rendering mode according to network speed

<!DOCTYPE html> {%if $network == 'fast' %} {%html rendermode="combo"%} {%else%} {%html rendermode="inline" localstorage="true" lscookiepath="/xxx" %} {%/if%} {%head%} ..... {%/head%} ....

Inline + localstorage storage

Solve the problem of slow network cache

Fine granularity / multi-dimensional, more storage, lower update frequency

Local storage and update scheme


{ "jA": { "hash": "2c79d70", "files": [ "common:widget/localstorage/zepto-ajax.tpl" ], "version": 1 }, "jZ": { "hash": "5358395", "files": [ "common:widget/localstorage/zepto.tpl" ], "version": 1 } }

Automatic generation of file version logic

<script data-lsid="jZ"> __inline('/static/js/zepto.js'); </script> {%if ($_ls_nonsupport) || ($_parsedLSCookies.jZ.isUpdate ) %} <script data-lsv="{%$_parsedLSCookies.jZ.version|escape:html%}" data-lsid="jZ"> var Zepto=xxx </script> {%else%} <script>LS.exec("jZ","js");</script> {%/if%}

Effectively avoid novice mistakes, free hands!

Cookie store version number effect

The version number is 36 base. If it exceeds 36, it starts from 1 to ensure that the length is always 1.

The expiration time of cookies is one week. There is no need to consider the overlapping of version numbers


When 4G becomes popular, as long as the slow judgment branch is removed, all can be replaced to the optimal scheme

JS template is compiled into bdbox module, which is convenient for management, cross domain pull and modular storage

Template selection: arttemplate

Template selection

Template compilation: before Compilation

{{include '../public/header'}} <div id="main"> <h3>{{title}}</h3> <ul> {{each list}} <li><a href="{{$value.url}}">{{$value.title}}</a></li> {{/each}} </ul> </div> {{include '../public/footer'}}

Template compilation: Bdbox module after compilation

define('baiduboxapp:tmpl/test/test2', function(require, exports, module, $) { $.template('baiduboxapp:tmpl/test/test2', function($data, $filename) { //忽略部分代码 include('../public/header'); $out += '\n\n<div id="main">\n <h3>'; $out += $escape(title); $out += '</h3>\n <ul>\n '; $each(list, function($value, $index) { $out += '\n <li><a href="'; $out += $escape($value.url); $out += '">'; $out += $escape($value.title); $out += '</a></li>\n '; }); $out += '\n </ul>\n</div>\n\n'; include('../public/footer'); $out += '\n'; return new String($out); }); module.exports = function(id, data) { var html = $.template("baiduboxapp:tmpl/test/test2", data); $.byId(id).innerHTML = html; } });

After compilation: depends on auto generation

"baiduboxapp:tmpl/test/test2": { "uri": "baiduboxapp/tmpl/test/test2.js", "type": "js", "deps": [ "baiduboxapp:tmpl/public/header", "baiduboxapp:tmpl/public/footer" ] } {%require name="common:bdbox/template"%} <div id="content"></div> {%script%} require('baiduboxapp:tmpl/test/test2'); var data = { title:'加载模板演示', time: +new Date(), list: [...] }; var html = Bdbox.template('baiduboxapp:tmpl/test/test2', data); document.getElementById('content').innerHTML = html; //or Bdbox.tmpl.test.test2('content', data); {%/script%}

Template output in rendering mode of rendermode = "tag"

JS template solution

After componentization, making pages is more like playing building block games

Componentized directory structure

# component 发现频道 discovery ├─header # 头部 │ header.css │ header.js │ header.tmpl │ └─comment # 评论 comment.css comment.js comment.tmpl


Component example: discovery / foo


Template foo.tmpl

{{include './public/header'}} <div id="main"> <h3>{{title}}</h3> <ul> {{each list}} <li><a href="{{$value.url}}">{{$value.title}}</a></li> {{/each}} </ul> </div> {{include './public/footer'}}

Component example: discovery / foo


Interaction foo.js

function bindEvent(id){ $.byId(id).addEventListener(xxx); //.... } module.exports = function(id, data){ template(id, data); bindEvent(id); }

After compilation:

define('baiduboxapp:c_discovery/foo', function(require, exports, module, $){ var template=require("baiduboxapp:c_tmpl/discovery/foo"); function bindEvent(id){ $.byId(id).addEventListener(xxx); } module.exports = function(id, data){ template(id, data); bindEvent(id); } });

Compile to generate dependency table

"baiduboxapp:c_css/discovery/foo": { "uri": "baiduboxapp/components/css/discovery/foo.css", }, "baiduboxapp:c_discovery/foo": { "deps": [ "baiduboxapp:c_css/discovery/foo", "baiduboxapp:c_tmpl/discovery/foo" ] }, "baiduboxapp:c_tmpl/discovery/foo": { "deps": [ "baiduboxapp:c_tmpl/discovery/public/header", "baiduboxapp:c_tmpl/discovery/public/footer", "baiduboxapp:c_css/discovery/foo" ] }, "baiduboxapp:c_tmpl/discovery/public/footer": { "deps": [ "baiduboxapp:c_tmpl/discovery/public/logo" ] }, "baiduboxapp:c_tmpl/discovery/public/header": { "deps": [ "baiduboxapp:c_tmpl/discovery/public/logo" ] }, "baiduboxapp:c_tmpl/discovery/public/logo": { "uri": "baiduboxapp/components/tmpl/discovery/public/logo.js", }

Call a component

require('baiduboxapp:c_discovery/foo');'content', data);

Actual output:

<!--先在head输出css--> <link rel="stylesheet" type="text/css" href="baiduboxapp/components/css/discovery/foo.css" /> <!--在body依次输出模板依赖和js模块依赖--> <script type="text/javascript" src="baiduboxapp/components/tmpl/discovery/public/logo.js"></script> <script type="text/javascript" src="baiduboxapp/components/tmpl/discovery/public/header.js"></script> <script type="text/javascript" src="baiduboxapp/components/tmpl/discovery/public/footer.js"></script> <script type="text/javascript" src="baiduboxapp/components/tmpl/discovery/foo.js"></script> <script type="text/javascript" src="baiduboxapp/components/discovery/foo.js"></script> <div id="content"></div> <script>'content', data); </script>

Combined with the previous solutions, webapp component development is more handy! Detailed documents

Use tools to simulate the interface, run through the process in advance, and reduce the joint commissioning time

Using FIS to reduce the cost of joint debugging with PHPer