前者工程之模块化,大家是什么办好前端工程化和静态财富管理

永利402com官方网站 41
永利402com官方网站

大家是什么样盘活前端工程化和静态财富管理

2016/07/30 · 基础技能 ·
工程化,
静态能源

原来的文章出处:
坑坑洼洼实验室   

永利402com官方网站 1

乘胜互连网的前进,我们的事体也渐渐变得尤其目眩神摇且三种化起来,前端程序员也不再只是做轻松的页面开垦这么简单,大家供给直面包车型客车十二分复杂的系统性难题,举例,业务尤其复杂,大家要怎么样清晰地梳理;团队职员越来越多,大家要什么越来越好地展开团队通力合营;功用越来越多,大家要哪些保管页面包车型大巴性质不至于下跌,等等。全部的这么些都得以归咎为怎样提高开垦体验和属性难点。

模块化是风姿罗曼蒂克种处理百废待举系统一分配解变成越来越好的可治本模块的办法,它能够把系统代码划分为黄金年代层层职责单风姿罗曼蒂克,中度解耦且可替换的模块,系统中某一片段的更换将什么影响其余一些就能够变得显著,系统的可维护性特别简明易得。

进级开拓体验

我们任重(Ren Zhong)而道远从以下多少个方面来提高大家的付出体验。

前端开拓领域(JavaScript、CSS、Template)并不曾为开辟者们提供以风姿罗曼蒂克种洗练、井井有条地的法子来治本模块的办法。CommonJS(致力于规划、规划并原则
JavaScript API)的出世开启了“ JavaScript 模块化的时日”。CommonJS
的模块提案为在劳务器端的 JavaScript
模块化做出了相当的大的进献,然则在浏览器下的 JavaScript
模块应用很单薄。随之而来又出生了任何前端领域的模块化方案,像
requireJS、SeaJS 等,但是那些模块化方案并不是十分适用
,并不曾从根本上解决模块化的标题。

规范化

当组织职员不断扩充时,大家须要制定统风度翩翩的科班来对日常的费用工作做出一定约束和指引。统豆蔻梢头的正规化包罗前端的代码标准,依照专门的职业定义好生龙活虎套代码检查的平整,在代码提交的时候进行检讨,让开荒人员知道自身的代码意况。

与此同期,根据以后的开支经历,大家拟订了联合的类型框架,依照工效分歧,将三个档期的顺序(app)拆分成分歧的职业模块(module),而每三个模块都包涵作者的页面(page)以至结合页面所急需的组件(widget),每一个品类涉嫌到app、module、page、widget那个早就约定好的定义,那样让项目结构越发清楚,何况让集体内区别专门的职业的人口期间切换无障碍。

永利402com官方网站 2

前端模块化并不等于 JavaScript 模块化

前端开垦相对其余语言来讲比较优质,因为我们贯彻二个页面效果总是需要JavaScript、CSS 和 Template 二种语言互相社团才行,假使贰个意义仅仅独有JavaScript 完结了模块化,CSS 和 Template
照旧处于原始状态,那大家调用这一个作用的时候并不可能一心通过模块化的办法,那么这么的模块化方案并不是全部的,所以大家实在须求的是大器晚成种能够将
JavaScript、CSS 和 Template 同一时间都考虑进来的模块化方案,而非仅仅
JavaScript 模块化方案。

组件化

在品种中引进组件化的概念,这里的零部件对应上文讲到的widget,每三个零部件都会蕴藏组件本人的模板、css、js、图片以至表明文件,大家运用组件来拼装页面,像搭积木相似来拼装我们的页面,同一时间五个零部件内足以调用另贰个零件。

永利402com官方网站 3

在得到设计稿后,我们先是要求鲜明如何必要做成公共组件,那么些是要做成独立组件,以致组件间怎么着进行通讯。在页面中调用这一个组件后,会活动加载组件的模版以致组件的静态财富,而当组件不再须求时,只要移除掉组件引用,那么相应的沙盘和静态能源也会不再加载。

组件化的利润重要有诸有此类几点

  • 拘留有助于,大家能够把贰个独立功效有关的公文在工程目录中位居一块儿,那样代码管理起来会极其常有益
  • 零件复用,通过收取公共组件,能够兑现组件复用,进而减弱专门的学业量,成立价值
  • 分而治之,那是组件化最重大的有个别,将页面组件化,就是对页面效果的拆分,将叁个大的工程拆成小的零件,大家只供给关切每二个零部件的成效,十分大地减弱了页面包车型地铁开垦与维护的难度

JavaScript 模块化并不等于异步模块化

主流的 JavaScript
模块化方案都应用“异步模块定义”的办法,这种方法给支付推动了大而无当的劳苦,全数的一路代码都亟待矫正为异步的法门,我们是或不是能够在前端开荒中央银行使“
CommonJS
”的章程,开拓者能够行使当然、轻便通晓的模块定义和调用方式,没有须求关怀模块是或不是异步,无需转移开拓者的付出作为。

自动化编写翻译

在前端开拓中,大家连年会去接受过多工具、花招来优化代码、提高开垦效能,比方,大家会动用sass、less等CSS预管理工科具来编排更加好保卫安全的体裁代码,大家也会使用CSSLint、eslint等代码检查工具来检查代码的语法错误,使用文件合并压缩等手段来减弱财富大小,除却大家还恐怕会去做Sprite图合并、多倍图管理、字体压缩管理、代码发表等等。

早就有大神说过,当先90s的干活都应有自动化掉。而以上全部的这几个职业,贯穿大家不论什么事开荒流程,不过不一致工具的切换不但显得指皁为白,何况影响开采功效。在自动化、工程编写翻译的思维已经美名天下的立时,我们本来也要紧跟洋气,所以大家思量通过自动化手腕来进步大家的频率,让全数操作能够大器晚成键式开速履行完。

我们将透过定义好一种种的编写翻译职分,遵照一定顺序依次对大家的连串活动进行编写翻译操作,最终发生出可上线的代码。

前端模块化带来的性指责题

不菲主流的模块化实施方案经过 JavaScript
运营时来帮衬“无名闭包”、“信任深入分析”和“模块加载”等效果,比如“正视解析”需求在
JavaScript
运维时通过正则相称到模块的凭借关系,然后沿着注重链(相当于沿着模块申明的注重层层进入,直到未有依靠结束)把具有要求加载的模块按顺序依次加载完结,当模块超多、注重关系头晕目眩的状态下会严重影响页面质量。

晋升品质

作者们最首要从以下多少个地方来做好品质优化。

模块化为打包布署带来的庞大困难

历史观的模块化方案更加的多的假造是什么将代码进行拆分,可是当大家布署上线的时候必要将静态能源开展统生机勃勃(打包),那个时候会意识险象环生,各样文件里只可以有四个模块,因为模块使用的是“无名定义”,经过生龙活虎番研讨,大家会意识有的缓和方案,无论是“
combo 插件”依然“ flush
插件”,都亟待我们修正模块化调用的代码,那如实是困难重重,开荒者不独有需求在当地开辟关切模块化的拆分,在调用的时候还须要关切在三个呼吁里面加载哪些模块比较适度,模块化的初心是为着加强花费成效、收缩维护资金财产,但大家开采这么的模块化方案实际上并不曾下落维护资金,某种程度上来讲使得整个项目尤为复杂了。

首屏优化

页面包车型大巴开辟速度一直是豪门丰富关心的一个指标,三个页面张开太慢会让让顾客失去等待的耐烦,为了让客户更快地收看页面,大家着想将页面中有些静态能源代码间接嵌入页面中,大家透过工具管理,在工程编写翻译阶段,将内定的静态财富代码内停放页面中,那样能够减去HTTP哀告,提高首屏加载速度,相同的时间收缩页面裸奔风险。

全体的前端模块化施行方案

写到这里,其实我们的“前端工程之块化”才正式启幕,本文面向对前面二个模块化开荒具备实行或有所商量的同窗,接下去大家所介绍的前端模块化建设方案,
有别于 JavaScript 模块化方案或 CSS
模块化方案,它是黄金年代种能够回顾管理前端各类财富的模块化方案;它能够非常大提高开垦者的花费体验,并为品质优化提供不错的支撑。下边让大家来更是来询问如何是“生龙活虎体化”的模块化试行方案。

首先我们来看一下三个 web
项目是哪些通过“大器晚成体化”的模块化方案来划分目录结构:

永利402com官方网站 4

  • 站点(site):常常指能独立提供服务,具备独自二级域名的产品线。如旅游产品线或许特大站点的子站点(lv.baidu.com)。
  • 子系统(module):具有较清晰业务逻辑关系的效果与利益业务集结,日常也叫系统子模块,多少个子系统组成三个站点。子系统(module)包罗两类:
    common 子系统,
    为任何业务子系统提供正规、能源复用的通用模块;业务子系统:,遵照专门的工作、UENCOREI
    等将站点进行分割的子系统站点。
  • 页面(page): 具备独立 U库罗德L 的出口内容,多个页面日常可组成子系统。
  • 模块(widget):能独立提供功用且能够复用的模块化代码,依照复用的点子各异分为
    Template 模块、JS 模块、CSS 模块三连串型。
  • 静态能源(static):非模块化财富目录,包含模板页面援引的静态财富和别的静态财富(favicon,crossdomain.xml
    等)。

前面一个模块(widget),是能独立提供成效且能够复用的模块化代码,依照复用的艺术各异分为
Template 模块、JS 模块、CSS 模块三种类型,CSS 组件,日常的话,CSS
模块是最轻易易行的模块,它只涉及 CSS 代码与 HTML 代码; JS
模块,稍为复杂性,涉及 JS 代码,CSS 代码和 HTML 代码。日常,JS
组件可以封装 CSS 组件的代码; Template 模块,涉及代码最多,能够综合管理HTML、JavaScript、CSS 等各样模块化财富,日常景况,Template 会将 JS
财富封装成私有 JS 模块、CSS 能源封装成本身的私有 CSS
模块。上面大家来挨门逐户介绍那三种模块的模块化方案。

按需加载

而且,我们着想通过尽量减小页面体量来进步页面张开速度,在事情上大家将页面划分为二个个大楼组件,以京东美妆馆为例,页面中从上而下分为首焦、至IN尖货、明天心急吃不了热水豆腐、风尚前线、口碑榜单这么多少个楼宇组件,其实那个页面还应该有非常长,内容超级多且复杂。

永利402com官方网站 5

前边我们的做法是百分百页面直出,那样一遍性加载的故事情节会相当的多,为了进步张开速度,大家思考通过按需加载的诀窍来优化页面包车型大巴加载。大家在页面中只放每三个楼房的框架性代码,楼层的模版和数目都因而异步的艺术去拉取,来落实楼层组件的按需加载,同偶然间大家可以对模板甚至数额开展缓存,以此来压缩诉求,做更极端的优化。在支付中大家以健康组件的法子去付出总体页面,随后通过编写翻译工具,在代码编译阶段活动将楼房的模板分离成二个单身的JS文件,并给楼层容器打上标识位,通过页面加载逻辑去按需拉取模板,再开展渲染。

通过给楼层容器和模板分别增进暗记位 o2-out-tpl-wrapper o2-out-tpl

永利402com官方网站 6

在编写翻译时自动将点名的沙盘代码抽离成独立js文件

永利402com官方网站 7

再者给楼层容器打上标识

永利402com官方网站 8

何况在逻辑脚本适当地方自动步入模板的版本

永利402com官方网站 9

经过上述手续,完结按需加载的自动化生成,在升级品质的还要,很好地解放大家生产力。

模板模块

大家能够将其他豆蔻年华段可复用的沙盘代码放到贰个 smarty
文件中,这样就足以定义四个模板模块。在 widget 目录下的 smarty
模板(本文仅以 Smarty 模板为例)即为模板模块,比如 common 子系统的
widget/nav/ 目录

├── nav.css
├── nav.js
└── nav.tpl

下 nav.tpl 内容如下:

<nav id="nav" class="navigation" role="navigation">
    <ul>
        <%foreach $data as $doc%>
        <li class="active">
            <a href="#section-{$doc@index}">
                <i class="icon-{$doc.icon} icon-white"></i>{$doc.title}
            </a>
        </li>
        <%/foreach%>
    </ul>
</nav>

然后,大家只需求后生可畏行代码就足以调用那几个带有 smarty、JS、CSS
财富的沙盘模块,

// 调用模块的路径为 子系统名称:模板在 widget 目录下的路劲
{widget name="common:widget/nav/nav.tpl" }

其一模板模块(nav)目录下有与模板同名的 JS、CSS
文件,在模板被实施渲染时这几个能源会被活动加载。如上所示,定义 template
模块的时候,只需求将 template 所依赖的 JS 模块、CSS
模块存放在同等目录(暗许 JavaScript 模块、CSS 模块与 Template
模块同名)下就能够,调用者调用 Template
模块只供给写生机勃勃行代码就能够,无需关心所调用的 template
模块所依据的静态财富,模板模块会扶持我们机关管理注重关系以至财富加载。

基于能源表加载

依赖页面组件化,通过工具深入分析,我们将获得页面与组件的信赖性关系表,同一时间也能认可页面所引述能源的正视关系,例如,我们在页面hello中同步援引组件topbar,那么信任关系表少将会记录同步引用关系hello援用topbar.tpl、topbar.css、topbar.js,那么页面hello将会活动加载组件topbar的CSS与JS,同一时候借助表会记录异步引用的涉及,假诺大家在组件C中经过API异步援引了组件D的js,那么会在依靠表中记录C异步引用D.js那贰个依附关系,那样D.js这些财富将会在使用的时候被异步调用。

永利402com官方网站 10

永利402com官方网站 11

风度翩翩道援引的能源通过生成combo格局链接,在服务端进行理文件件归并,那样在页面加载的时候,页面只会加载本人必要的联合签名能源,异步的财富将会在接纳的时候再加载,有效制止财富冗余。同期删除、扩大组件也丰盛方便,只需改换模板中对组件调用,通过编写翻译工具会自动重新生成模板以致combo链接。

咱俩能够将能源加载的操作分离出来,产生生龙活虎套统黄金时代的财富加载框架设计,这样我们利用的模版能够变得特别灵敏,无论是纯html模板,依旧PHP或Java之类的后端模板都能管用支持。编写翻译工具扫描代码后只生成能源重视表,我们经过落成各语言平台的能源加载框架,让不相同语言的沙盘都能依靠同多少个能源信赖表进行能源加载。

同有的时候间,对财富进行MD5重命名管理,文件md5重命名也是后生可畏种升高质量的卓有成效花招,使用文件md5后张开服务器强缓存,能够进级缓存的利用率并制止无需的缓存推断处理。但文件md5重命名后会现身开拓时援用的公文名对不上的主题材料,那就要求在能源表中记录原著件名与md5重命名后之间的相应关系,当大家援用二个财富时,就能够透过查表获取重命名后的能源名,然后使用代码中援引能源一定的技能来进展能源名活动替换。

永利402com官方网站 12

JavaScript 模块

下边大家介绍了一个模板模块是怎么样定义、调用以致管理信任的,接下去大家来介绍一下模板模块所注重的
JavaScript 模块是怎么样来拍卖模块交互的。我们得以将别的风姿浪漫段可复用的
JavaScript 代码放到二个 JS 文件中,那样就能够定义为二个 JavaScript
类型的模块,我们绝不关怀“ define ”闭包的标题,大家能够收获“ CommonJS
”雷同的费用体验,上边是 nav.js 中的源码.

// common/widget/nav/nav.js
var $ = require('common:widget/jquery/jquery.js');

exports.init = function() {
    ...
};

大家得以经过 require、require.async 的法子在此外四个地点(包涵html、JavaScript 模块内部)来调用大家供给的 JavaScript 类型模块,require
提供的是黄金时代种类似于后端语言的联合调用情势,调用的时候暗许所急需的模块都早就加载成功,解决方案会负责实现静态财富的加载。require.async
提供的是风华正茂种异步加载格局,首要用来满足“按需加载”的光景,在 require.async
被实行的时候才去加载所需求的模块,当模块加载回来会奉行相应的回调函数,语法如下:

// 模块名: 文件所在 widget 中路径
require.async(["common:widget/menu/menu.js"], function( menu ) {
    menu.init();
});

貌似 require 用于拍卖页面首屏所急需的模块,require.async
用于拍卖首屏外的按需模块。

永利402com官方网站,静态能源预加载

所谓静态能源预加载,正是当客户在拓宽浏览页面包车型地铁时候,大家能够在当前页面静默加载下二个页面包车型地铁静态能源,那样当顾客步入到下一个页面时就会便捷展开页面,进而在潜意识中升高页面包车型大巴展开速度。

永利402com官方网站 13

我们会在静态财富预加载平台上配置每二个页面id对应供给预加载页面能源的id,然后系统经过读取能源信任表获取到所要求预加载的静态能源,生成预加载财富列表文件,再将文件推送到线上服务器,通过页面挂载js央浼获取预加载能源列表,随后静默加载财富。在有了能源信赖表后,大家能够准确地剖判到每三个页面援引财富的伸手,就足以很好地落到实处静态财富预加载的法力。

永利402com官方网站 14

CSS 模块

在模板模块中以至 JS 模块中对应同名的 CSS 模块会自动与模板模块、JS
模块增加注重关系,举办加载管理,客户不供给展现进行调用加载。那么怎么样在二个CSS 模块中宣示对另多个 CSS
模块的注重关系啊,大家得以因此在批注中的@require
字段标志的正视性关系,那么些深入分析管理对 html 的 style 标签内容相同有效,

/**
 * demo.css
 * @require reset.css
 */

Athena

工欲善其事,必现利其器。为了兑现大家对升官开辟效能和成品天性的央求,大家提出了比较完好的工程化设计方案以至相应的工具Athena。

Athena是由京东【凹凸实验室】(aotu.io)
推出的黄金年代套花色流程工具,通过Athena,我们得以很流程地跑完全数开采流程。Athena分成两某个,一是本土自动化编写翻译工具,二是财富管理平台,其框架结构如下

永利402com官方网站 15

非模块化能源

在实际开垦进程中也许存在一些不相符做模块化的静态能源,那么大家照旧得以通过申明信任关系来托管给静态财富管理类别来统风姿罗曼蒂克管理和加载,

{require name="home:static/index/index.css" }

万一通过如上语法能够在页面表明对四个非模块化财富的依赖,在页面运营时方可自行加载相关能源。

本土自动化学工业具

Athena本地编写翻译工具是三个基于NodeJs的命令行工具,通过试行命令的章程来优化大家的支出流程,近期Athena的第黄金时代功效有

  • 活动创设项目、模块、页面、组件结构
  • 轻量组件化功用,依照组件加载意况生成能源重视表
  • Sass/less 编译
  • 代码检查
  • CSS prefix等处理
  • CSS归总压缩,JS合併压缩
  • 自动生成Sprite图,自动多倍图,图片压缩
  • 字体文件减少
  • 自定义图片转base64
  • 文本内联,能够内联样式及JS代码
  • 文件MD5戳,将文件举行应用MD5实行重命名
  • 当地预览,直接查看全数项目
  • 财富一定(图片等能源路线替换)
  • 生成CSS页面片,提供将页面引用的CSS/JS分离成页面片的款型,方便处理CSS财富
  • 安顿到预览机和开荒机

类型实例

下边大家来看一下在三个实际项目中,如若在通过页面来调用各个类型的
widget,首先是目录结构:

├── common
│   ├── fis-conf.js
│   ├── page
│   ├── plugin
│   ├── static
│   └── widget
└── photo
    ├── fis-conf.js
    ├── output
    ├── page
    ├── static
    ├── test
    └── widget

小编们有三个子系统,叁个 common 子系统(用作通用),多个业务子系统,page
目录用来贮存页面,widget 目录用来存放各类别型的模块,static
用于存放非模块化的静态财富,首先大家来看一下 photo/page/index.tpl
页面包车型客车源码,

{extends file="common/page/layout/layout.tpl"}
{block name="main"}
    {require name="photo:static/index/index.css"}
    {require name="photo:static/index/index.js"}
    <h3>demo 1</h3>
    <button id="btn">Button</button>
    {script type="text/javascript"}
        // 同步调用 jquery
        var $ = require('common:widget/jquery/jquery.js');

        $('#btn').click(function() {
            // 异步调用 respClick 模块
            require.async(['/widget/ui/respClick/respClick.js'], function() {
                respClick.hello();
            });
        });
    {/script}

    // 调用 renderBox 模块
    {widget name="photo:widget/renderBox/renderBox.tpl"}
{/block}

先是处代码是对非模块化能源的调用方式;第二处是用 require 的方式调用二个JavaScript 模块;第三处是透过 require.async 通过异步的方法来调用叁个JavaScript 模块;最终风度翩翩处是因此 widget 语法来调用多少个模板模块。 respclick
模块的源码如下:

exports.hello = function() {
    alert('hello world');
};

renderBox 模板模块的目录结构如下:

└── widget
    └── renderBox
        ├── renderBox.css
        ├── renderBox.js
        ├── renderBox.tpl
        └── shell.jpeg

即便 renderBox 下边包蕴 renderBox.js、renderBox.js、renderBox.tpl
等各类模块,我们再调用的时候只必要生机勃勃行代码就能够了,并无需关切在那之中的依据,以至各样模块的领头化难题。

创造项目布局

在奉行创制命令时,Athena会从管理平台下载自定义好的类型模板,能够依据模板创造项目、模块、页面、和零部件。Athena有四个创设命令:

通过实行 $ ath app demo 命令就能够转移定义好目录结构的品类。

永利402com官方网站 16

随后能够经过 $ ath module home来创制四个事务模块;

通过 $ ath page index 来创制页面;

通过 $ ath widget widgetName 来创制组件。

模块化基础架构

付出使用

全部架构

为了促成意气风发种自然、便捷、高品质、风度翩翩体化的模块化方案,大家需求消除以下一些难点,

  • 模块静态能源管理,平日模块总会富含 JavaScript、CSS
    等别的静态能源,须要记录与管理这一个静态能源
  • 模块正视关系管理,模块间存在各类信赖关系,在加载模块的时候要求管理好这个信赖关系
  • 模块加载,在模块伊始化此前须要将模块的静态能源以致所注重的模块加载并预备好
  • 模块沙箱(模块闭包),在 JavaScript
    模块中大家须求活动对模块加多闭包用于消除功效域难点

** 使用编写翻译工具来治本模块 **

咱俩得以因而编写翻译工具(自动化学工业具)
对模块实行编写翻译管理,包罗对静态能源开展预管理(对 JavaScript
模块增多闭包、对 CSS 进行 LESS
预管理等)、记录每一种静态财富的配置路线以至依赖关系并生成能源表(resource
map)。我们能够经过编译工具来托管全部的静态能源,这样能够帮大家化解模块静态财富管理、模块依赖关系、模块沙箱难点。

** 使用静态财富加载框架来加载模块 **

那么什么样解决模块加载难题,大家得以由此静态资源加载框架来缓慢解决,主要蕴含前端模块加载框架,用于
JavaScript 模块化扶持,调节财富的异步加载。后端模块化框架,用于消弭JavaScript 同步加载、CSS
和模板等模块能源的加载,静态财富加载框架能够用于对页面实行持续的自适应的前端品质优化,自动对页面包车型大巴例外情状投递差异的能源加载方案,协理开荒者管理静态能源,抹平本地开垦到安顿上线的习性沟壑。
编写翻译工具和静态财富加载框架的流程图如下:

永利402com官方网站 17

组件化

Athena中贯彻组件化首纵然分为三种,一是指向纯HTML模板,通过扩充模板引擎方法达成,提供了组件化API
widget.load,它能够方法选择四个参数,第二个参数是widget的名目,前面多少个参数是可选参数,第叁个是向widget传递的有个别参数,第三个是widget所属的模块,要是是本模块,能够不传例如

JavaScript

<%= widget.load(‘user’) %> <%= widget.load(‘user’, { param:
‘test’ }) %> <%= widget.load(‘user’, null, ‘gb’) %>

1
2
3
4
5
6
7
<%= widget.load(‘user’) %>
<%=
widget.load(‘user’, {
param: ‘test’
})
%>
<%= widget.load(‘user’, null, ‘gb’) %>

透过沙盘引擎编写翻译,试行widget.load方法,能够兑现加载模板,记录注重关系的目标。

永利402com官方网站 18

二是目的性差异语言的后端模板,通过贯彻各自的组件化框架来实行零部件的加载,比如
PHP 下使用
<?= $widget->load('user', NULL, 'gb') ?>来张开零部件加载,再经过代码扫描得出组件正视关系。

编写翻译工具

自动化学工业具会扫描目录下的模块举办编写翻译管理并出口产出文件:

静态财富,经过编写翻译管理过的 JavaScript、CSS、Image 等文件,安插在 CDN
服务器自动加多闭包,大家愿意程序员在付出 JavaScript
模块的时候无需关爱” define
”闭包的思想政治工作,所以选取工具自动帮程序员加多闭包援助,举例如上定义的 nav.js
模块在经过自动化学工业具管理后变为如下,

define('common:widget/nav/nav.js', function( require, exports, module ) {
    // common/widget/nav/nav.js
    var $ = require('common:widget/jquery/jquery.js');

    exports.init = function() {
        ...
    };
});

模板文件,经过编写翻译管理过的 smarty 文件,自动安插在模板服务器

资源表,记录各种静态资源的布局路线以至凭仗关系,用于静态能源加载框架
静态能源加载框架(S奥迪Q7 Management System)会加载 source maps
获得页面所必要的装有模块以至静态财富的 url,然后组织能源输出最后页面。

Athena中的API

Athena针对模板提供了大器晚成层层的API来扩充丰裕的遵从,比方前边提到的
<%= widget.load() %> 来完成组件化。

再正是Athena中还提供了别样API:

<%= getCSS() %><%= getJS() %>
用来引用CSS/JS文件,传入文件名和模块名;

<%= uri() %>
提供了财富一定功用,能够在模板中标志能源,编译进度中会进行交流,何况在JS中也可以有能源一定API
__uri()

<%= inline() %>
提供了内联能源的效率,传入文件名和模块名,能够在模板中内联合国大会肆财富,比如图片以致JS脚本;并且
inline
也能够内联朝气蓬勃段互连网能源,比方线上的JS文件,相通的在JS中也会有内联能源API
__inline()

百事可乐图标志 ?__sprite ,在CSS中引用图片最终加上标志 ?__sprite
可以自动生成自定义名称7-Up图,同一时候扶持自定义生成多张七喜图,只供给要标志前边带上贰个文书名,就可以生成一张以那么些文件名来定名的七喜图,比如
?__sprite=icons ,那样全数带雷同标记的图样就能生成一张以
icons为文件名的七喜图。

静态能源加载框架

上边大家会详细批注怎么着加载模块,如下所示,

永利402com官方网站 19

在流程最初前大家须求未雨策画四个数据结构:

  • uris = [],数组,顺序寄存要出口财富的 uri
  • has = {},hash 表,存放已搜求的静态能源,幸免重复加载
  1. 加载财富表(resource map):

    {
        "res": {
            "A/A.tpl": {
                "uri": "/templates/A.tpl",
                "deps": ["A/A.css"]
            },
            "A/A.css": {
                "uri": "/static/css/A_7defa41.css"
            },
            "B/B.tpl": {
                "uri": "/templates/B.tpl",
                "deps": ["B/B.css"]
            },
            "B/B.css": {
                "uri": "/static/css/B_33c5143.css"
            },
            "C/C.tpl": {
                "uri": "/templates/C.tpl",
                "deps": ["C/C.css"]
            },
            "C/C.css": {
                "uri": "/static/css/C_6a59c31.css"
            }
        }
    }
    
  2. 执行 {widget name=”A”}

    • 在表中寻觅 id 为 A/A.tpl 的财富,猎取它的能源路径/template/A.tpl,记为 tplpath,加载并渲染 tplpath
      所指向的模版文件,即 /template/A.tpl,并出口它的 html 内容
    • 翻开 A/A.tpl 能源的 deps 属性,开采它依靠能源 A/A.css,在表中搜索id 为 A/A.css 的能源,获得它的能源路线为
      /static/css/A7defa41.css_,存入 uris 数组 中,并在 has 表
      里标志已加载 A/A.css 财富,大家获得:

      urls = [

      '/static/css/A_7defa41.css'
      

      ];

      has = {

      "A/A.css": true
      

      }

  3. 逐一实行 {widget name=”B”}、{widget name=”c”},步骤与上述手续 3
    相通,获得,

    urls = [
        '/static/css/A_7defa41.css',
        '/static/css/B_33c5143.css',
        '/static/css/C_6a59c31.css'
    ];
    
    has = {
        "A/A.css": true,
        "B/B.css": true,
        "C/C.css": true
    }
    
  4. 在要出口的 html 前边,我们读取 uris
    数组的数量,生成静态能源外链,大家收获终极的 html 结果:

    <html>
        <link rel="stylesheet" href="/static/css/A_7defa41.css">
        <link rel="stylesheet" href="/static/css/B_33c5143.css">
        <link rel="stylesheet" href="/static/css/C_6a59c31.css">
        <div>html of A</div>
        <div>html of B</div>
        <div>html of C</div>
    </html>
    

    上边讲的是对模板和 CSS
    财富的加载,用于描述静态能源加载的流水生产线,上面大家再来详细疏解下对于
    JavaScript 模块的管理,要想在前面一个落成相似“ commonJS
    ”相符的模块化开拓体验需求前端模块化框架和后端模块化框架一同效果来达成,

前面三个模块化框架,原理上海高校家可以筛选使用 requireJS 或 SeaJS
来作为模块化辅助,可是大家并不提出那样做,我们建议我们使用一个 mininal
AMD API,举个例子 requireJS 的 almond 版本或然此外的精练版本,requireJS
完整版有 二〇〇二 余行,而简洁明了版模块化框架只必要 100
行代码左右就能够完结,只须求完成以下职能:

  • 模块定义,只供给得以完结如下接口 define (id, factory),因为 define
    闭包是工具生成,所以大家没有必要思虑佚名闭包的得以达成,同有时间也无需思考“重视前置”的支撑,我们只须求扶持一种最简易间接的模块化定义就可以
  • 模块同步调用,require (id),静态能源管理连串会有限帮助所需的模块都已经先行加载,因此require 能够致时重回该模块
  • 模块异步调用,思虑到多少模块无需再运维时载入,由此我们必要提供二个方可在运维时加载模块的接口
    require.async (names, callback),names 能够是贰个id,也许是数组格局的 id 列表。当有着都加载都成功时,callback
    被调用,names 对应的模块实例将次第传入。
  • 模块自举办,即 英特尔 标准的提前履行,之所接受如此做的缘由是思虑到
    Template 模块的特殊性,平时 Template 模块都会依据 JavaScript
    模块来做开端化职业,选拔模块自实施的措施我们就没有要求显式的在
    Template 页面上挥洒 require 注重,静态财富系统会自动加载 Template
    模块的依赖,当模块并行加载甘休后会贰回自奉行。我们只怕会认为只要页面存在部分用不到的模块那都自进行岂不会浪费财富,这里我们能够绝不操心,静态能源系统投放到前端的模块都以页面起头化所急需的,不设有浪费财富的情状。
  • Resource map 前端援助,首要用感到异步模块调用提供 uri
    辅助,resourceMap
    为静态能源管理体系自动生成,没有需求人工调用,用于查询二个异步模块的确实
    url,用于机动管理异步模块的
    CDN、财富打包归拢、强缓慰藉题,格式如下,

    require.resourceMap({
        "res": {
            "common:widget/sidebar/sidebar.async.js": {
                "url": "/static/common/widget/sidebar/sidebar.async_449e169.js"
            }
        }
    });
    
  • 拍卖循环援用,参照 nodeJS 管理循环援引的方法,在导致循环依赖的
    require 在此之前把要求的事物 exports 出去,举例

    // a.js
    console.log('a string');
    exports.done = false;
    var b = require('./b.js');
    console.log('in a, b.done = ' + b.done);
    exorts.done = true;
    console.log('b done');
    
    // b.js
    console.log('b starting');
    exports.done = false;
    
    var a = require('./a.js');
    console.log('in b, a.done = ' + a.done);
    exports.done = true;
    console.log('b done');
    
    // main.js
    console.log('main starting');
    var a = require('./a.js');
    var b = require('./b.js');
    console.log('in main. a.done = ' + a.done + ', b.done = ' + b.done);
    

    若是在加载 a 的进程中,有其余的代码(倘诺为 b)require a.js
    的话,那么 b 能够从 cache 中一贯取到 a 的
    module,进而不会挑起重复加载的死循环。但推动的代价正是在 load
    过程中,b 看见的是缺损的 a。

后端模块加载框架,主要用来拍卖模块的依赖并生成模块静态财富外链,下边大家将以实例讲明静态财富管理种类是何许对
JavaScript 模块进行加载的,如下大家有一个 sidebar 模块,目录下有如下财富

├── sidebar.async.js
├── sidebar.css
├── sidebar.js
└── sidebar.tpl

sidebar.tpl 中的内容如下,

<a id="btn-navbar" class="btn-navbar">



</a>

{script}
    $('a.btn-navbar').click(function() {
        require.async('./sidebar.async.js', function( sidebar ) {
            sidebar.run();
        });
    });
{/script}

对项目编写翻译后,自动化学工业具会解析模块的重视关系,并生成 map.json,如下

"common:widget/sidebar/sidebar.tpl": {
    "uri": "common/widget/sidebsr/sidebar.tpl",
    "type": "tpl",
    "extras": {
        "async": [
            "common:widget/sidebar/sidebar.async.js"
        ]
    },
    "deps": [
        "common:widget/sidebar/sidebar.js",
        "common:widget/sidebar/sidebar.css"
    ]
}

在 sidebar 模块被调用后,静态能源管理体系经过询问 map.json
能够查出,当前 sidebar 模块同步信任 sidebar.js、sidebar.css,异步信任sdebar.async.js,在要出口的 html 后面,大家读取 uris
数组的多寡,生成静态财富外链,大家获取最后的 html

<script type="text/javascript">
    require.resourceMap({
        "res": {
            "common:widget/sidebar/sidebar.async.js": {
                "url": "/satic/common/widget/sidebar/sidebar.async_449e169.js"
            }
        }
    });
</script>
<script type="text/javascript" src="/static/common/widget/sidebar/sidebar_$12cd4.js"></script>

如上可以知道,后端模块化框架将一齐模块的 script url 统生平成到页面尾部,将
css url 统平生成在 head 中,对于异步模块(require.async)注册 resourceMap
代码,框架会由此{script}标签搜罗到页面全体 script,统意气风发保管并按顺序输出
script 到对应岗位。

编写翻译预览

自适应的习性优化

前日,当大家想对模块实行打包,该如哪里理啊,大家首先使用三个 pack
配置项(上边是 fis
的包裹配置项),对网址的静态能源开展包装,配置文件差不多为,

fis.config.merge({
    pack: {
        'pkg/aio.css': '**.css'
    }
});

小编们编写翻译项目看一下现身的 map.json(resource map),有什么变动,

{
    "res": {
        "A/A.tpl": {
            "uri": "/template/A.tpl",
            "deps": ["A/A.css"]
        },
        "A/A.css": {
            "uri": "/static/csss/A_7defa41.css",
            "pkg": "p0"
        },
        "B/B.tpl": {
            "uri": "/template/B.tpl",
            "deps": ["B/B.css"]
        },
        "B/B.css": {
            "uri": "/static/csss/B_33c5143.css",
            "pkg": "p0"
        },
        "C/C.tpl": {
            "uri": "/template/C.tpl",
            "deps": ["C/C.css"]
        },
        "C/C.css": {
            "uri": "/static/csss/C_ba59c31.css",
            "pkg": "p0"
        },
    },
    "pkg": {
        "p0": {
            "uri": "/static/pkg/aio_0cb4a19.css",
            "has": ["A/A.css", "B/B.css", "C/C.css"]
        }
    }
}

世家只顾到了么,表里多了一张 pkg 表,全数被打包的财富会有一个 pkg 属性
指向该表中的财富,而以此能源,正是大家布置的打包政策。那样静态能源管理连串在表中检索
id 为 A/A.css 的财富,大家开掘该能源有 pkg
属性,注明它被备份在了二个封装文件中。

咱俩应用它的 pkg 属性值 p0 作为 key,在 pkg
表里读取新闻,取的那些包的能源路线为 /static/pkg/aio0cb4a19.css_ 存入
uris 数组 中校 p0 包的 has 属性所注明的财富投入到 has 表,在要出口的
html 前边,我们读取 uris 数组 的多少,生成静态财富外链,咱们获取终极的
html 结果:

<html>
    <link href="/static/pkg/aio_0cb4a19.css">
    <div>html of A</div>
    <div>html of B</div>
    <div>html of C</div>
</html>

静态财富管理体系可以非常灵活的适应种种质量优化场景,大家还足以总计{widget}
插件的调用意况,然后自动生成最优的包裹配置,让网址能够自适应优化,那样程序猿不用关怀财富在哪,怎么来的,怎么没的,全部能源一定的业务,都交由静态财富管理种类就好了。静态能源路线都带
md5
戳,那个值只跟内容关于,静态财富服务器从此未来能够放心开启强缓存了!还是能促成静态能源的各自宣布,轻易回滚!大家还是可以承继商讨,比如依据国际化、身体发肤,终端等音讯约定黄金年代种能源路线规范,当后端适配到一定地区、特定机型的拜候时,静态财富管理种类帮你送达分歧的能源给不一样的客商。提及那边,大家应该相比较清楚整个“大器晚成体化”的模块化实施方案了,有人恐怕会问,那样做岂不是扩展了后端品质费用?对于那几个主题素材,咱们实践过的经验是,这十二分值得!其实那几个后端开支非常少,算法特别简单直白,但他所换到的前端工程化水平增进非常的大!

编写翻译职责

在编写制定完项目,就足以因而命令来对品种实行编写翻译了,施行编写翻译命令
$ ath build,会针对钦点模块实践当已定义好的编写翻译职务,遵照项目需求,最近编写翻译都以依据业务模块去编译,编写翻译职分的非常的小施行单位是页面,每一回编写翻译都会进行以下编写翻译列表

永利402com官方网站 20

永利402com官方网站 21

总结

正文是 fis
前端工程体系文章中的生龙活虎部分,其实在前端开垦工程管理世界还会有为数不菲细节值得商量和开采,进步前端团队生产力水平并不是一句空话,它必要大家能对前端开采及代码运维有越来越深厚的认知,对质量优化原则有更细心的分析与商量。fis
团队直接致力于从架构而非经验的角度达成质量优化原则,解决前端技术员开辟、调试、安插中相见的工程难点,提供组件化框架,升高代码复用率,提供开垦工具集,进步技术员的付出效能。在前者工业化开采的有着环节均有可节省的人力财力,这么些资金极其可观,相信以往数不胜数特大型互连网公司也皆有了那样的共鸣。

正文只是将以此圈子中十分小的一片段文化的展开研讨,一得之见,希望能为产业界相关领域的工笔者提供部分不相似的思路。款待关切fis品种,对本文有其余意见或建议都足以在fis开源项目中展开报告和商议。

作者:walter
(http://weibo.com/u/1916384703) – F.I.S 

本地预览

实践预览命令 $ath serve
会实行精短版编写翻译职责来编写翻译项目,编写翻译完项目后会生成风度翩翩份站点地图,随后张开一个本地服务器来预览项目,使用那一个命令能够很有益于地拓张开拓,在预览时会同不常候watch目录和文件的改动,况且提供了livereload成效,我们得以在预览时自便修改文件,都将实时地呈现到页面中,同期能够新建另一个窗口进行新添组件和页面包车型客车操作,让任何开荒进度丰富顺畅,大家只需关心开拓本身就好,没有要求再关心别的事。

永利402com官方网站 22

奉行完编写翻译任务后,暗中同意自动张开浏览器,预览站点地图

永利402com官方网站 23

Mock server

在展开项目预览的还要,Athena同不常候提供了mock
data的服务,我们能够配备相应的路由,以致路由接口对应的假数据,全部的接口央求会发送到mock
server上,在mock
server中能够选拔将呼吁代理到假数据平台依旧代理到线上接口,那样就能够脱离后端实行付出联调了,以此达成多少的内外端抽离。

永利402com官方网站 24

花色布署

在付出预览完后,通过命令 $ ath publish
就能够将品种揭穿到陈设好的测验机上,发表同一时候帮忙ftp、sftp以致http格局。

零件维护

大家经过组件化的招数已经将大家的门类开展组件化了,那样大家透过专门的学问迭代积攒,产出相当多政工公共组件,但在过去的品种支付中,公共组件的翻新与保卫安全向来异常受节制,并且有怎么样公共组件、公共组件长什么体统,只可以借助口传心授大概手工维护的文书档案。所以在Athena中大家进入了组件平台,在组件平台上联合展览种种业务的公共组件,而得益于本地下工作具,组件平台无需人工干预维护,我们得以在本土通过命令
$ ath widget-publish [widgetName]
命令来公布一个零部件到零部件平台,那样别的人就足以立即在组件平台举行零部件的预览,而别的人若想使用该零件时,在该地通过命令ath widget-load [widgetId]
就足以下载该零件到自个儿的模块目录下了。

那样组件的掩护越发自动化,公共组件的应用也进一步方便了。

零件发表

永利402com官方网站 25

组件下载

永利402com官方网站 26

自己优化

为了升高开垦功用,Athena做了有的优化操作

简短项目预览时的天职

在付出时张开项目预览时,会推行洗练版的编写翻译职分,剔除了相近文件收缩、Sprite图生成、模板分离管理等耗费时间的操作,只保留主旨、必需的编写翻译职分,那样能够大幅地回降编写翻译时间,提高开辟的频率。

预览时监听细化

在支付进行预览时,会对持有文件的改正实行监听,而针对每生龙活虎类公事皆有比比较细化的操作,当文件改造时只会实施改文件所要求的编写翻译任务,而不会开展风姿浪漫体化编写翻译,那样能够很好地晋级开荒功用。比方改换某大器晚成零件的CSS文件,则只会指向该公文施行一些连锁的CSS操作。

再就是得益于全体文件重视关系的记录,在监听时会依照应重关系张开文件编写翻译,比如某sass文件中引进了另三个sass库文件,更改那个sass库文件的时候,会依靠援用关系表同有时候更新到具备援引到这一个sass文件的公文,这样项目文件更新及时,让开拓流程进一步通畅。

编写翻译缓存

在图片压缩和sass编写翻译时,开启文件缓存,将曾经编写翻译过且未有改观的文本过滤掉,不再编写翻译,小幅度提高编写翻译速度。

布告缓存

安装发布过滤,依照文件md5过滤掉已经发表过的文书,升高发表速度。

才能选型

Athena本地下工作具初期技艺选型是 Yeoman + Gulp
的秘籍,但新兴由于设置、更新非凡劳碌,命令太长很难打地铁来由,大家改成了和睦支付三个大局安装包的艺术,编写翻译主题使用的还是
Gulpvinyl-fs 来完成公文流管理,通过 ES6 Promise
来进行编写翻译流程序调节制,最小以页面为单位,经过意气风发雨后苦笋编写翻译职分,最后现身编译好的文本。

永利402com官方网站 27

拘禁平台

质量优化一贯是前边三个技术员探究的课题,超多时候正是能源的分配难题,也正是能源处理。为了越来越好地宽容地面营造筑工程具来治本财富,大家搭建了管制平台。我们来看下,结合本地营造筑工程具和治本平台,专门的工作流程形成了何等?

事业流程

  1. 在治本平台上创制项目,输入项目名称和预览机,以致选拔相应的模版等;
  2. 在极限施行ath
    app指令,工具会事先拉取远程服务器的门类新闻来起头化项目,若无获取到相关新闻,就能够在地点转移项目,并将品种音信反映给服务器;
  3. 花色初步化后,就足以创立模块、页面、组件了;
  4. 在编码进程中,可经过ath server预览页面;
  5. 在当地通过后,可奉行ath publish将代码发表到开垦机也许预览机。

在地方的publish指令中,工具会扫描全数文件,实施代码检查,扫描页面文件,获取组件依赖关系,依据组件依赖关系张开文件归拢,然后会进行体制管理、js管理以致图片的管理,根据配置是还是不是举行md5重命名文件,组装html,插入样式、js和图片,最终将编写翻译好的公文揭露到相应的机器。在整个经过之中,会生成能源事关信任表,最后会将能源事关表及编写翻译后的文本上传至管理平台。

除此而外,每一个指令的操作都会上报给管理平台。管理平台选拔数量后,会对数据开展拍卖,最后能够在平台上观看项目有关的新闻。

完全专门的学业流程图如下:

永利402com官方网站 28

从上边的职业流程中,大家得以看来,管理平台供给有数据计算、财富管理以至项目管理的功用。全体架构图如下:

永利402com官方网站 29

多少总括

多少总结包括项目操作日志,主假诺用以总括共青团和少先队每种成员具体的操作,方便项目成员查看项目代码改造;另后生可畏都部队份是总计样式表、脚本以至图片的减少数量,用于展示工具给大家项目推动的晋升。

以下是操作日志总计:

永利402com官方网站 30

能源管理

财富处理是处理平台的基本,重要分为4个部分:模块显示、信任关系、组件预览和权杖决定。这有的成效首要透过地面构建筑工程具提供的能源事关表来完结。

模块体现

模块浮现,用于记录项目具体包罗怎样模块以致模块具体的新闻。在平凡开辟中,我们的种类会分为许多模块,分歧的模块有例外的人来开拓和保卫安全。当项目越大的时候,能够通过拘押平台清晰地观察模块具体的新闻。

永利402com官方网站 31

信任关系

信任关系,主借使html、css、js和图片互相之间的涉嫌。通过深入分析财富事关重视表,能够赢获得各样财富被引述的意况以至线上版本的情景。当线上情状接受md5来做能源管理时,大家不是很清楚地掌握静态财富对应线上哪个版本的财富,而有了这一个依靠关系表,当现身难题时,大家能够更加快地稳住到具体的财富。

永利402com官方网站 32

组件处理

大家利用组件来拼凑页面,当项目越大时,组件越多,那么哪些保管组件成为了三个困难的难点。比方说,有局地相比老的冗余组件,我们不明显是还是不是为别的页面所引述,那么就不能够高欢悦兴地删除它。有了组件管理,能够清晰地理解组件的被调用意况,就足以对组件做相应的操作。

组件管理,结合组件平台来行使,在拘留平台上援用组件地址预览组件,同时能够博获得零部件被引用以至援引财富(如css、js、图片)的相关意况。

永利402com官方网站 33

我们的机件分为二种,后生可畏类是经过ath w自动创设的,通过ath
pu提交随处理平台的,在关押平台上拓宽零部件的相关深入分析和编写翻译,获得组件的新闻,那类组件主假诺跟职业绑定的;另意气风发类是经过ath
widget-publish提交到零部件平台的,由组件平台张开连锁处理,那类组件是通用组件,与事务非亲非故,用于体现给支付以致相关专业方看的。

永利402com官方网站 34

在组件平台上得以预览与编写制定相关的零部件,通过与设计员约定相关的设计规范来促使组件到达尽或然地复用,进而减少设计员的专门的工作量,升高我们的工效。

永利402com官方网站 35

零件提交到零部件平台

透过ath
widget-publish指令将零件提交到零部件平台,组件平台会对组件源码实行编写翻译,将零件名称md5、组件归类以至组件版本记录等等。

永利402com官方网站 36

从组件平台上下载组件

通过ath
widget-load指令将零件下载到本地,当本地创设筑工程具向组件平台发起号令时,会带上组件名称,组件平台会将源码实行编译,将零件名称重命名,况兼相应地更迭源码中的组件名称,同有的时候候记录组件的被引述记录。

永利402com官方网站 37

权力决定

权力决定,项目中存在公共组件模块,公共组件相比较牢固,比如说轮播组件、选项卡组件等等,那有的代码平常少之又少变动,可由少部分人来更新和维护,所以参加了权力决定机制,保险集体组件的平安。

品种管理

大家在使用本地构建筑工程具时,必要配置多个参数,举例主机音讯、接收模版等,在指令市价况下有个别不直观。为了简化那些操作,管理平台提供了连串开创的效果与利益,同期提供了模版创造的意义。

永利402com官方网站 38

在品种新闻、模块消息以至组件音讯发出转移的时候,为了第不通常间能够布告项目成员更新,参预了音信公告的功能,方今经过发送邮件的艺术,中期能够走入微信提示的功力。

本事选型

管理平台前端选取React+Redux的点子,后端采纳Express+MongoDB,全体手艺选型如下:

永利402com官方网站 39

假数据服务

留存的主题素材

在平凡的支出中,日常索要前后端联调,可是在品种上马之初,非常多接口并未提供,在早前的开荒方式下,须求翘首以待后端提供接口大概本身先定义接口,前端开辟的进程或许会受影响。

Mock数据平台

为了不影响前端开垦的速度,大家搭建了Mock数据平台,通过与后端协商数据格式,自定义数据接口,那样子就能够变成前后端分离,让后边一个独立于后端进行支付。

Mock数据平台基于mockjs搭建而成,通过轻易的mock语法来生成多少。

Mock数据平台近些日子犹如下效果:

  1. 创建模拟数据,使之相符各类情形;
  2. 生成json数据接口,协助COLANDS以致jsonp。

永利402com官方网站 40

写在终极

此番分享首先汇报了俺们在事情膨胀、职员不断扩充的背景下蒙受的种类支出上的标题,并提议了我们自个儿对于那几个难题思索总计后得出的技术方案与思路,最终现身符合我们团队、业务的开荒工具——
Athena。希望我们的方案能给大家带来一定的借鉴意义。

1 赞 14 收藏
评论

永利402com官方网站 41

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图