菜单

前端工程之模块化

2019年2月22日 - 金沙前端

写在结尾

这次分享首先讲述了我们在作业膨胀、人士不停加码的背景下蒙受的品类开发上的难点,并提议了大家和好对此那么些题材思考总计后得出的消除方案与思路,最终出现适合大家集团、业务的开发工具——
Athena。希望大家的方案能给大家带来一定的借鉴效用。

1 赞 14 收藏
评论

图片 1

自适应的属性优化

今日,当大家想对模块进行包装,该怎么处理吧,我们率先应用2个 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
戳,这些值只跟内容有关,静态财富服务器从此可以放心开启强缓存了!还是能促成静态能源的各自发表,轻松回滚!大家还是能继承讨论,比如根据国际化、皮肤,终端等音信约定一种财富路径规范,当后端适配到一定地段、特定机型的拜访时,静态能源管理体系帮您送达差距的财富给差距的用户。说到此地,我们应该相比清楚整个“一体化”的模块化解决方案了,有人或许会问,这样做岂不是伸张了后端质量开支?对于这几个难点,我们执行过的经历是,那十分值得!其实那么些后端开销很少,算法非常简单直白,但他所换成的前端工程化水平升高一点都不小!

Model View Controller

Free马克尔(Free马克尔 Template Language) 一个java类库 view层完全独立
突显逻辑和事务逻辑分离 轻量级框架 不需求Servlet环境
HTML静态化
Template + DataModel = HTML
Java代码控制读取哪一个Template
Free马克尔模板不编译成类,不可以写任何java代码,严俊的MVC分离
属性优越JSP 协理JSP标签

宏定义是怎样?

前端框架首要为了化解哪些难题?怎么着消除?
1.能源一定
工程路径 –> 布署路径,
相对路径 –> 相对路径 + md5戳 + 域名 –>
消除版本迭代后静态能源缓存在客户端的标题,
完结模块独立,职责文件间都足以展开内嵌
2.模块化开发
着力难点:倚重管理和加载
创设工具只担负生成依赖关系表 框架本身相对什么日期加载哪些能源

模块化框架

CSS
Sprites
【在国内广大人叫css天使,是一种网页图片应用处理格局。它同意你将一个页面涉及到的兼具零星图片都蕴含到一张大图中去,那样一来,当访问该页面时,载入的图样就不会像以前那么一幅一幅地逐步突显出来了。对于当下互连网流行的速度而言,不超过200KB的单张图片的所需载入时间基本是大抵的,所以无需顾忌那么些题材】

编译缓存

在图片压缩和sass编译时,开启文件缓存,将曾经编译过且没有改变的文本过滤掉,不再编译,小幅升级编译速度。

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

前端开发相对其他语言来说相比较非凡,因为我们已毕2个页面效果总是需要JavaScript、CSS 和 Template 两种语言相互协会才行,假诺1个成效仅仅唯有JavaScript 完结了模块化,CSS 和 Template
照旧处于原始状态,那大家调用这些效应的时候并不可以完全通过模块化的艺术,那么这么的模块化方案并不是完整的,所以我们真正要求的是一种可以将
JavaScript、CSS 和 Template 同时都考虑进来的模块化方案,而非仅仅
JavaScript 模块化方案。

先来看看以后一般前端团队的做法:

Mock数据平台

为了不影响前端开发的速度,大家搭建了Mock数据平台,通过与后端协商数据格式,自定义数据接口,这样子就足以做到前后端分离,让前者独立于后端举行付出。

Mock数据平台基于mockjs搭建而成,通过不难的mock语法来生成数据。

Mock数据平台近日有如下效果:

  1. 始建模拟数据,使之符合种种场所;
  2. 生成json数据接口,辅助CO奥迪Q3S以及jsonp。

图片 2

编译工具

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

静态能源,经过编译处理过的 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福特Explorer Management System)会加载 source maps
得到页面所急需的有着模块以及静态能源的 url,然后协会财富输出最后页面。

属性优化趋势分类

技术选型

Athena本地工具早期技术选型是 Yeoman + Gulp
的章程,但后来由于设置、更新万分麻烦,命令太长很难打的由来,我们改成了投机支付二个大局安装包的不二法门,编译主旨使用的如故
Gulpvinyl-fs 来达成文件流处理,通过 ES6 Promise
来进展编译流程控制,最小以页面为单位,经过一连串编译职分,最终现身编译好的文本。

图片 3

模板模块

大家可以将其余一段可复用的模版代码放到三个 smarty
文件中,那样就足以定义四个模板模块。在 widget 目录下的 smarty
模板(本文仅以 斯马特y 模板为例)即为模板模块,例如 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
模块所依靠的静态能源,模板模块会拉扯大家机关处理看重关系以及财富加载。

采取query更新缓存的点子实在要覆盖线上文件的,index.html和a.js总有一个顺序的种种,从而中间现身一段或大或小的年华间隔。特别是当页面是后端渲染的模版的时候,静态财富和模板是布署在不一样的机械集群上的,上线的进程中,静态资源和页面文件的配置时间间隔大概会那1个长,对于1个重型互连网采纳来说就是在3个相当小的光阴距离内,都有大概出现新用户访问。在那么些时刻距离中,访问了网站的用户会生出什么样动静吗?

自个儿优化

为了提高开发功用,Athena做了有个别优化操作

静态财富加载框架

下边大家会详细讲解如何加载模块,如下所示,

图片 4

在流水线初阶前大家必要预备多个数据结构:

  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
来作为模块化帮助,但是大家并不指出如此做,咱们提议我们使用1个 mininal
英特尔 API,例如 requireJS 的 almond 版本或许别的的精简版本,requireJS
完整版有 两千 余行,而精简版模块化框架只须要 100
行代码左右就足以完成,只须求达成以下职能:

后端模块加载框架,首要用来拍卖模块的依靠并生成模块静态财富外链,上边大家将以实例讲解静态财富管理系列是如何对
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 到对应岗位。

答案是:静态财富依赖关系表。##\

考虑这么的目录结构:

![]CI6%_0FBW4.png](http://upload-images.jianshu.io/upload\_images/1058258-e4067324e4a4c04e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

万一大家得以采纳工具扫描整个project目录,然后创造一张财富表,同时记录各个财富的布署路径,拿到如此的一张表:

依照那张表,我们就很简单已毕require_static(file_id),load_widget(widget_id)
这五个模板接口了。以load_widget为例:

选拔查表来消除md5戳的题材,这样,大家的页面最后送达给用户的结果就是如此的:

接下去,大家商讨基于表的安排性思想上是哪些兑现静态财富集合的。可能有个别团队利用过combo服务,也等于我们在最终拼接生成页面财富引用的时候,并不是生成八个独立的link标签,而是将能源地址拼接成三个url路径,请求一种线上的动态财富统一服务,从而落成收缩HTTP请求的必要,比如前边的事例,稍作调整即可获取这么的结果:

其一 /??file1,file2,file3,…
的url请求响应就是动态combo服务提供的,它的规律很简单,就是依照url找到呼应的七个文本,合并成三个文本来响应请求,并将其缓存,以加速访问速度。
那种方法很巧妙,有个别服务器甚至一贯集成了这类模块来方便的打开此项服务,那种做法也是超越5/10巨型web应用的能源统一做法。但它也设有部分败笔:

对此上述第1条缺陷,可以举个例子来看表明:

很强烈,倘若combo服务能精晓的知道A页面使用的财富引用为 /??a,b
和 /??c,d
,而B页面使用的能源引用为 /??a,b
和 /??e,f
就好了。这样当用户在访问A页面之后再拜访B页面时,只需要下载B页面的第③个combo文件即可,第1个公文已经在访问A页面时缓存好了的。基于那样的想想,大家在能源表上增产了一个字段,取名为
pkg,就是能源统一生成的新财富,表的构造会成为:

对照以前的表,可以看出新表中多了二个pkg字段,并且记下了包装后的文件所包蕴的独门财富。那样,我们再一次规划一下
require_static、load_widget 那多个模板接口,完结那样的逻辑:
在查表的时候,如若三个静态资源有pkg字段,那么就去加载pkg字段所指向的打包文件,否则加载财富本身。

例如执行require_static(‘bootstrap.js’),查表得知bootstrap.js被打包在了p1中,因而取出p1包的url
/pkg/lib_cef213d.js,并且记下页面已加载了 jquery.js 和 bootstrap.js
多个财富。那样一来,从前的模版代码执行之后得到的html就改为了:

![]6PSM{F1%%UED4R.png](http://upload-images.jianshu.io/upload\_images/1058258-8bc134c681a0d7f2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

虽说这种政策请求有5个,不如combo格局的呼吁少,但只怕在总计上是性质更好的方案。由于五个lib打包的文件修改的只怕性非常的小,由此那两个请求的缓存利用率会相当高,每一趟项目揭破后,用户须要再行下载的静态能源或者要比combo请求节省数不胜数带宽。

当地预览

履行预览命令 $ath serve
会执行精简版编译义务来编译项目,编译完项目后会生成一份站点地图,随后打开一个当地服务器来预览项目,使用这些命令可以很有利地拓展付出,在预览时会同时watch目录和文书的改动,并且提供了livereload功效,我们得以在预览时任意修改文件,都将实时地展示到页面中,同时可以新建另三个窗口举行新增组件和页面的操作,让任何开发进度卓殊顺畅,大家只需关切开发本人就好,不需求再关怀其余事。

图片 5

实施完编译职务后,暗中认同自动打开浏览器,预览站点地图

图片 6

模块化是一种处理盘根错节系统分解变成更好的可管制模块的形式,它能够把系统代码划分为一星罗棋布义务单一,高度解耦且可替换的模块,系统中某一局地的变通将何以影响其余一些就会变得显著,系统的可维护性尤其简约易得。

<script type="text/javascript" src="a_8244e91.js"></script>

数量计算

数码计算包涵项目操作日志,紧假设用于计算团队各种成员具体的操作,方便项目成员查看项目代码变更;另一部份是计算样式表、脚本以及图片的滑坡数量,用于体现工具给我们项目牵动的提拔。

以下是操作日志计算:

图片 7

总结

正文是 fis
前端工程连串作品中的一有的,其实在前端开发工程管理世界还有好多细节值得探讨和钻井,提高前端团队生产力水平并不是一句空话,它须求大家能对前端开发及代码运营有更深厚的认识,对质量优化原则有更密切的解析与研商。fis
团队直接从事于从架构而非经验的角度落成品质优化原则,化解前端工程师开发、调试、布置中遇见的工程难点,提供组件化框架,升高代码复用率,提供开发工具集,提高工程师的支付功用。在前者工业化开发的全体环节均有可节省的人力资本,这个资产分外可观,相信今后不乏先例大型网络集团也都有了这么的共识。

正文只是将以此领域中相当的小的一有些文化的展开啄磨,引玉之砖,希望能为业界相关领域的工小编提供部分不相同的思路。欢迎关心fis体系,对本文有任何意见或提议都足以在fis开源项目中展开申报和座谈。

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

名词解释

从组件平台上下载组件

因此ath
widget-load指令将零件下载到本地,当本地打造工具向组件平台发起呼吁时,会带上组件名称,组件平台会将源码举行编译,将零件名称重命名,并且相应地更迭源码中的组件名称,同时记录组件的被引用记录。

图片 8

模块化基础架构

<h1>hello world</h1>
<script type="text/javascript" src="a.js?t=201404231123"></script>
<script type="text/javascript" src="b.js?t=201404231123"></script>
<script type="text/javascript" src="c.js?t=201404231123"></script>
<script type="text/javascript" src="d.js?t=201404231123"></script>
<script type="text/javascript" src="e.js?t=201404231123"></script>
从简项目预览时的职务

在支付时举办项目预览时,会执行精简版的编译任务,剔除了看似文件收缩、Sprite图生成、模板抽离处理等耗时的操作,只保留宗旨、必须的编译职分,那样可以极大地减作者译时间,提高开发的功效。

模块化为打包计划带来的石破天惊不便

观念的模块化方案越来越多的设想是怎样将代码举行拆分,然而当大家陈设上线的时候要求将静态能源进行统一(打包),那一个时候会发现困难重重,每种文件里只好有一个模块,因为模块使用的是“匿名定义”,经过一番研商,大家会发现一些缓解方案,无论是“
combo 插件”依然“ flush
插件”,都需求大家修改模块化调用的代码,这无疑是雪上加霜,开发者不仅仅必要在地面开发关心模块化的拆分,在调用的时候还须要关心在多少个呼吁里面加载哪些模块比较确切,模块化的初衷是为了进步支付作用、下落维护资产,但大家发现这么的模块化方案实际上并从未降低维护费用,某种程度上来说使得整个项目进一步错综复杂了。

把以上那些曾经成熟应用到骨子里生产中的优化手段去除掉,留下那几个还尚无很好已毕的优化原则。再来回想一下此前的性质优化分类:

首屏优化

页面的开辟速度一贯是我们充足关爱的四个目的,多个页面打开太慢会让让用户失去等待的耐心,为了让用户更快地看到页面,大家考虑将页面中部分静态能源代码直接嵌入页面中,大家经过工具处理,在工程编译阶段,将点名的静态能源代码内停放页面中,那样能够减去HTTP请求,进步首屏加载速度,同时降低页面裸奔危机。

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

主流的 JavaScript
模块化方案都接纳“异步模块定义”的措施,那种措施给支付带来了偌大的孤苦,全体的一块代码都需求修改为异步的不二法门,大家是不是足以在前端开发中行使“
CommonJS
”的方法,开发者可以动用当然、不难领悟的模块定义和调用格局,不需求关心模块是不是异步,不需求转移开发者的成本作为。

编译预览

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
用于拍卖首屏外的按需模块。

<header>hello world</header>
<script type="text/javascript" src="a.js?t=201404231826"></script>
<script type="text/javascript" src="b.js?t=201404231826"></script>
<script type="text/javascript" src="c.js?t=201404231826"></script>
<script type="text/javascript" src="d.js?t=201404231826"></script>
<script type="text/javascript" src="e.js?t=201404231826"></script>

相关文章

发表评论

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

网站地图xml地图