您的位置:澳门402永利com > 前端技术 > 致我们终将组件化的Web

致我们终将组件化的Web

发布时间:2019-11-26 14:07编辑:前端技术浏览(199)

    致大家必然组件化的Web

    2015/11/25 · HTML5 · 1 评论 · 组件化

    最先的小说出处: AlloyTeam   

    那篇小说将从七年前的一次手艺争论起来。争辨的聚集就是下图的五个目录分层结构。小编说按模块划分好,他说你傻逼啊,当然是按财富划分。

    图片 1 《=》图片 2

    ”按模块划分“目录结构,把当下模块下的有所逻辑和能源都放一块了,那对于六个人独立开垦和有限协理个人模块不是很好啊?当然了,这争辩的结果是本人婴孩地改回主流的”按能源划分“的目录结构。因为,未遂JS模块化和能源模块化,仅仅物理地点上的模块划分是绝非意义的,只会增加营造的基金而已。

    虽说他说得好有道理小编理屈词穷,然则本身心不甘,等待他多年来端组件化成熟了,再来世界一战!

    而后日就是本人注重建议正义的日子!只是那个时候拾分跟你撕逼的人不在。

    模块化的供应不能满足须要

    模块平常指能够单独拆分且通用的代码单元。由于JavaScript语言自个儿并未有放置的模块机制(ES6有了!!卡塔 尔(英语:State of Qatar),我们日常会动用CMD或ADM创立起模块机制。今后半数以上有一点大型一点的类型,都会利用requirejs或许seajs来兑现JS的模块化。五人分工同盟开拓,其各自定义正视和暴露接口,维护功用模块间独立性,对于项指标开支效用和等级次序前期扩展和维护,都是是有很大的推推搡搡效用。

    但,麻烦我们不怎么略读一下底下的代码

    JavaScript

    require([ 'Tmpl!../tmpl/list.html','lib/qqapi','module/position','module/refresh','module/page','module/net' ], function(listTmpl, QQapi, Position, Refresh, Page, NET){ var foo = '', bar = []; QQapi.report(); Position.getLocaiton(function(data){ //... }); var init = function(){ bind(); NET.get('/cgi-bin/xxx/xxx',function(data){ renderA(data.banner); renderB(data.list); }); }; var processData = function(){ }; var bind = function(){ }; var renderA = function(){ }; var renderB = function(data){ listTmpl.render('#listContent',processData(data)); }; var refresh = function(){ Page.refresh(); }; // app start init(); });

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    require([
        'Tmpl!../tmpl/list.html','lib/qqapi','module/position','module/refresh','module/page','module/net'
    ], function(listTmpl, QQapi, Position, Refresh, Page, NET){
        var foo = '',
            bar = [];
        QQapi.report();
        Position.getLocaiton(function(data){
            //...
        });
        var init = function(){
            bind();
            NET.get('/cgi-bin/xxx/xxx',function(data){
                renderA(data.banner);
                renderB(data.list);
            });
        };
        var processData = function(){
        };
        var bind = function(){
        };
        var renderA = function(){
        };
        var renderB = function(data){
            listTmpl.render('#listContent',processData(data));
        };
        var refresh = function(){
            Page.refresh();
        };
        // app start
        init();
    });

    上边是切实可行某些页面的主js,已经封装了像Position,NET,Refresh等作用模块,但页面的主逻辑仍然是”面向进度“的代码结构。所谓面向进度,是指依据页面包车型客车渲染进度来编排代码结构。像:init -> getData -> processData -> bindevent -> report -> xxx 。 方法之间线性跳转,你大约也能心得那样代码破绽。随着页面逻辑更是复杂,那条”进程线“也会愈加长,况且进一层绕。加之贫乏职业节制,其余类型成员根据各自需求,在”进度线“加插各自逻辑,最后那个页面包车型大巴逻辑变得难以维护。

    图片 3

    开采必要严慎,生怕影响“进度线”后边平常逻辑。并且每一回加插或改换都以bug泛滥,无不令产物有关人口无不心有余悸。

     页面结构模块化

    依靠上边的面向进度的难题,行当内也会有成都百货上千施工方案,而作者辈团队也计算出大器晚成套成熟的缓和方案:Abstractjs,页面结构模块化。我们得以把大家的页面想象为三个乐高机器人,须要区别零件组装,如下图,如若页面划分为tabContainer,listContainer和imgsContainer四个模块。最后把这一个模块add到最后的pageModel里面,最后使用rock方法让页面运营起来。

    图片 4
    (原经过线示例图卡塔尔

    图片 5
    (页面结构化示例图卡塔尔

    上面是伪代码的落实

    JavaScript

    require([ 'Tmpl!../tmpl/list.html','Tmpl!../tmpl/imgs.html','lib/qqapi','module/refresh','module/page' ], function(listTmpl, imgsTmpl, QQapi, Refresh, Page ){ var tabContainer = new RenderModel({ renderContainer: '#tabWrap', data: {}, renderTmpl: "<li soda-repeat='item in data.tabs'>{{item}}</li>", event: function(){ // tab's event } }); var listContainer = new ScrollModel({ scrollEl: $.os.ios ? $('#Page') : window, renderContainer: '#listWrap', renderTmpl: listTmpl, cgiName: '/cgi-bin/index-list?num=1', processData: function(data) { //... }, event: function(){ // listElement's event }, error: function(data) { Page.show('数据重返至极[' + data.retcode + ']'); } }); var imgsContainer = new renderModel({ renderContainer: '#imgsWrap', renderTmpl: listTmpl, cgiName: '/cgi-bin/getPics', processData: function(data) { //... }, event: function(){ // imgsElement's event }, complete: function(data) { QQapi.report(); } }); var page = new PageModel(); page.add([tabContainer,listContainer,imgsContainer]); page.rock(); });

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    require([
        'Tmpl!../tmpl/list.html','Tmpl!../tmpl/imgs.html','lib/qqapi','module/refresh','module/page'
    ], function(listTmpl, imgsTmpl, QQapi, Refresh, Page ){
     
        var tabContainer = new RenderModel({
            renderContainer: '#tabWrap',
            data: {},
            renderTmpl: "<li soda-repeat='item in data.tabs'>{{item}}</li>",
            event: function(){
                // tab's event
            }
        });
     
        var listContainer = new ScrollModel({
            scrollEl: $.os.ios ? $('#Page') : window,
            renderContainer: '#listWrap',
            renderTmpl: listTmpl,
            cgiName: '/cgi-bin/index-list?num=1',
            processData: function(data) {
                //...
            },
            event: function(){
                // listElement's event
            },
            error: function(data) {
                Page.show('数据返回异常[' + data.retcode + ']');
            }
        });
     
        var imgsContainer = new renderModel({
            renderContainer: '#imgsWrap',
            renderTmpl: listTmpl,
            cgiName: '/cgi-bin/getPics',
            processData: function(data) {
                //...
            },
            event: function(){
                // imgsElement's event
            },
            complete: function(data) {
               QQapi.report();
            }
        });
     
        var page = new PageModel();
        page.add([tabContainer,listContainer,imgsContainer]);
        page.rock();
     
    });

    大家把那几个常用的伸手CGI,管理多少,事件绑定,上报,容错管理等大器晚成俯拾都已经逻辑方式,以页面块为单位封装成一个Model模块。

    像这种类型的一个硕庞然大物层Model,我们得以清楚地看看该页面块,诉求的CGI是何等,绑定了怎么风云,做了何等上报,出错怎么管理。新添的代码就相应放置在对应的模块上相应的气象方法(preload,process,event,complete…卡塔尔国,杜绝了过去的不可能则乱增代码的小说。并且,根据不一致职业逻辑封装不相同类其余Model,如列表滚动的ScrollModel,滑块功用的SliderModel等等,能够举行高度封装,聚焦优化。

    今后依照Model的页面结构开荒,已经包罗一点”组件化“的暗意。各样Model都饱含各自的多寡,模板,逻辑。已经算是二个大器晚成体化的功力单元。但相差真正的WebComponent依旧有后生可畏段间隔,起码满意不断笔者的”理想目录结构“。

     WebComponents 标准

    我们回想一下应用几个datapicker的jquery的插件,所必要的步奏:

    1. 引进插件js

    2. 引进插件所需的css(假使有卡塔 尔(英语:State of Qatar)

    3. copy 组件的所需的html片段

    4. 增加代码触发组件运维

    一时的“组件”基本上只好落得是有个别功用单元上的聚众。他的财富都是松散地分散在二种财富文件中,并且组件功能域拆穿在全局意义域下,缺少内聚性超轻松就能够跟别的零零器件发生冲突,如最简便易行的css命名冲突。对于这种“组件”,还不及上边的页面结构模块化。

    于是乎W3C按耐不住了,拟定三个WebComponents标准,为组件化的前途带领了明路。

    下边以较为轻巧的点子介绍那份正经,力求大家能够飞速明白完毕组件化的剧情。(对这有个别打探的同室,能够跳过这一小节卡塔 尔(英语:State of Qatar)

    1. <template>模板本事

    模板那东西厦高校家最纯熟不过了,二零一八年见的超多的沙盘质量战争artTemplate,juicer,tmpl,underscoretemplate等等。而近年来又有mustachejs无逻辑模板引擎等新入选手。然而大家有没有想过,这么功底的技艺,原生HTML5是不帮忙的(T_T)。

    而明天WebComponent将在提供原生的模版技巧

    XHTML

    <template id="datapcikerTmpl"> <div>作者是原生的沙盘</div> </template>

    1
    2
    3
    <template id="datapcikerTmpl">
    <div>我是原生的模板</div>
    </template>

    template标签钦命义了myTmpl的沙盘,须要接收的时候将在innerHTML= document.querySelector('#myTmpl').content;能够看出这一个原生的模版够原始,模板占位符等职能都还没,对于动态数据渲染模板工夫只可以自力更新。

    2. ShadowDom 封装组件独立的内部结构

    ShadowDom能够清楚为意气风发份有独立成效域的html片段。这么些html片段的CSS蒙受和主文档隔开分离的,各自小编保护持内部的独立性。也便是ShadowDom的独门个性,使得组件化成为了只怕。

    JavaScript

    var wrap = document.querySelector('#wrap'); var shadow = wrap.createShadowRoot(); shadow.innerHTML = '<p>you can not see me </p>'

    1
    2
    3
    var wrap = document.querySelector('#wrap');
    var shadow = wrap.createShadowRoot();
    shadow.innerHTML = '<p>you can not see me </p>'

    在现实dom节点上行使createShadowRoot方法就可以生成其ShadowDom。有如在整份Html的室内面,新建了叁个shadow的屋企。房间外的人都不知底房间内有何,保持shadowDom的独立性。

    3. 自定义原生标签

    首先接触Angularjs的directive指令作用,设定好组件的逻辑后,一个<Datepicker />就会引进整个组件。如此狂酷炫炸碉堡天的机能,实在令人人心大快,跃地三尺。

    JavaScript

    var tmpl = document.querySelector('#datapickerTmpl'); var datapickerProto = Object.create(HTMLElement.prototype); // 设置把大家模板内容大家的shadowDom datapickerProto.createdCallback = function() { var root = this.createShadowRoot(); root.appendChild(document.importNode(tmpl.content, true)); }; var datapicker = docuemnt.registerElement('datapicker',{ prototype: datapickerProto });

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var tmpl = document.querySelector('#datapickerTmpl');
    var datapickerProto = Object.create(HTMLElement.prototype);
     
    // 设置把我们模板内容我们的shadowDom
    datapickerProto.createdCallback = function() {
        var root = this.createShadowRoot();
        root.appendChild(document.importNode(tmpl.content, true));
    };
     
    var datapicker = docuemnt.registerElement('datapicker',{
        prototype: datapickerProto
    });

    Object.create方式持续HTMLElement.prototype,获得二个新的prototype。当拆解剖析器开采大家在文书档案中标记它将检查是或不是贰个名称叫createdCallback的措施。假如找到这么些法子它将即时运营它,所以我们把克隆模板的剧情来创设的ShadowDom。

    末尾,registerElement的办法传递大家的prototype来注册自定义标签。

    地点的代码先河略显复杂了,把前边多个力量“模板”“shadowDom”结合,产生组件的里边逻辑。最终通过registerElement的方法注册组件。之后方可愉悦地<datapicker></datapicker>的使用。

    4. imports消除组件间的凭借

    XHTML

    <link rel="import" href="datapciker.html">

    1
    <link rel="import" href="datapciker.html">

    以此类php最常用的html导入功效,HTML原生也能支撑了。

    WebComponents标准内容差少之又少到那边,是的,小编那边未有啥样德姆o,也未尝施行经历分享。由于webComponents新特色,基本三巳了高版本的Chrome扶植外,别的浏览器的扶植度甚少。即使有polymer扶持带动webcompoents的仓库储存在,然而polymer自个儿的供给版本也是十二分高(IE10+卡塔尔。所从前不久的台柱并非她。

    大家简要来回看一下WebCompoents的四有个别机能:

    1 .<template>定义组件的HTML模板技术

    1. Shadow Dom封装组件的内部结构,况兼维持其独立性

    2. Custom Element 对外提供组件的价签,实现自定义标签

    3. import消除组件结合和借助加载

     组件化施行方案

    官方的行业内部看完了,大家考虑一下。风流浪漫份真正成熟笃定的组件化方案,必要具备的技巧。

    “能源高内聚”—— 组件能源内部高内聚,组件财富由自身加载调节

    “功用域独立”—— 内部结构密闭,不与全局或此外零零件发生耳濡目染

    “自定义标签”—— 定义组件的使用方式

    “可交互作用结合”—— 组件正在有力之处,组件间组装整合

    “接口规范化”—— 组件接口有联合标准,恐怕是生命周期的田间管理

    个体认为,模板工夫是功底力量,跟是不是组件化未有强联系,所以未有提议二个大点。

    既是是实施,现阶段WebComponent的匡助度还不成熟,不可能相提并论方案的招式。而别的大器晚成套以高品质虚构Dom为切入点的构件框架React,在facebook的造势下,社区获取了大力发展。此外一名骨干Webpack,担负化解组件财富内聚,同有的时候间跟React非常适合造成补充。

    所以【Webpack】+【React】将会是那套方案的宗旨本事。

    不知底你以往是“又是react+webpack”以为失望图片 6,依然“太好了是react+webpack”不用再学三次新框架的欢愉图片 7。无论怎么着上面的剧情不会令你大失所望的。

    后生可畏,组件生命周期

    图片 8

    React天生正是强制性组件化的,所以可以从根性子上解决面向进度代码所带来的劳动。React组件自个儿有生命周期方法,能够满足“接口典型化”技能点。何况跟“页面结构模块化”的所封装分离的几个章程能挨个对应。其它react的jsx自带模板效能,把html页面片直接写在render方法内,组件内聚性特别严密。

    出于React编写的JSX是会先生成虚构Dom的,须要机遇才真的插入到Dom树。使用React一定要驾驭组件的生命周期,其生命周期八个情形:

    Mount: 插入Dom

    Update: 更新Dom

    Unmount: 拔出Dom

    mount那单词翻译扩大,嵌入等。作者倒是提出“插入”越来越好驾驭。插入!拔出!插入!拔出!默念贰遍,懂了没?别少看黄段子的力量,

    图片 9

    组件状态正是: 插入-> 更新 ->拔出。

    然后种种组件状态会有三种管理函数,生龙活虎前意气风发后,will函数和did函数。

    componentWillMount()  筹算插入前

    componentDidlMount()  插入后

    componentWillUpdate() 筹算更新前

    componentDidUpdate()  更新后

    componentWillUnmount() 寻思拔出前

    因为拔出后为主都以贤者形态(作者说的是组件卡塔 尔(英语:State of Qatar),所以并未有DidUnmount这几个格局。

    除此以外React别的二个为主:数据模型props和state,对应着也可以有自个状态方法

    getInitialState()     获取初叶化state。

    getDefaultProps() 获取暗中认可props。对于这些还没父组件传递的props,通过该措施设置暗中认可的props

    componentWillReceiveProps()  已插入的构件收到新的props时调用

    再有二个非正规情形的管理函数,用于优化管理

    shouldComponentUpdate():剖断组件是不是必要update调用

    加上最关键的render方法,React自己带的点子刚正巧拾个。对于初读书人的话是相比较麻烦消食。但其实getInitialStatecomponentDidMountrender八个意况方法都能不负职责大多数组件,不必惧怕。

    归来组件化的宗旨。

    一个页面结构模块化的组件,能独立包装整个组件的进度线

    图片 10

    我们换算成React生命周期方法:

    图片 11

     

    组件的气象方法流中,有两点需求极其表达:

    1,一遍渲染:

    出于React的诬捏Dom天性,组件的render函数不需和煦触发,依据props和state的改造自个通过差别算法,得出最优的渲染。

    恳请CGI经常都是异步,所以无可置疑带来贰次渲染。只是空数据渲染的时候,有十分大希望会被React优化掉。当数码回来,通过setState,触发三遍render

     

    2,componentWiillMount与componentDidMount的差别

    和大超级多React的科目小说不平等,ajax恳求小编建议在WillMount的情势内试行,并不是组件伊始化成功未来的DidMount。那样能在“空数据渲染”阶段以前央浼数据,尽早地减少三回渲染的时日。

    willMount只会实践二次,特别相符做init的工作。

    didMount也只会实践一遍,何况那个时候真实的Dom已经形成,非常符合事件绑定和complete类的逻辑。

     

     二,JSX极难看,不过组件内聚的要害!

    WebComponents的正规化之生机勃勃,需求模板本事。本是感到是大家熟稔的模版技能,但React中的JSX这样的怪人依然令人信口雌黄。React还没火起来的时候,大家就早已在和讯上狠狠地嘲讽了“JSX写的代码这TM的丑”。那事实上只是Demo阶段JSX,等到实战的大型项目中的JSX,富含多情况超级多据多事件的时候,你会发觉………….JSX写的代码依然极丑。

    图片 12
    (就算用sublime-babel等插件高亮,逻辑和渲染耦合一齐,阅读性依旧略差卡塔 尔(英语:State of Qatar)

    为何大家会以为丑?因为我们早已经对“视图-样式-逻辑”分离的做法潜濡默化。

    借助维护性和可读性,以至质量,大家都不提议直接在Dom上边绑定事件仍旧直接写style属性。我们会在JS写事件代理,在CSS上写上classname,html上的便是明显的Dom结构。我们很好地保证着MVC的设计格局,一切平安。直到JSX把她们都夹杂在联合,所守护的才具栈受到入侵,难免存有抗拒。

     

    然而从组件化的指标来看,这种高内聚的做法未尝不可。

    上面包车型地铁代码,早先的“逻辑视图抽离”形式,我们供给去找相应的js文件,相应的event函数体内,找到td-info的class所绑定的事件。

    比较起JSX的高度内聚,所有的事件逻辑就是在自家jsx文件内,绑定的就是本身的showInfo方法。组件化的特点能立时显示出来。

    (注意:即便写法上大家好疑似HTML的内联事件微电脑,不过在React底层并未实际赋值相通onClick属性,内层依旧接受肖似事件代理的办法,高效地维护着事件微型机卡塔 尔(英语:State of Qatar)

    再来看豆蔻年华段style的jsx。其实jsx未有对体制有硬性规定,大家完全可比照早前的定义class的逻辑。任何大器晚成段样式都应有用class来定义。在jsx你也全然可以那样做。不过由于组件的独立性,笔者提议部分唯有“一回性”的体制直接使用style赋值更加好。减弱冗余的class。

    XHTML

    <div className="list" style={{background: "#ddd"}}> {list_html} </div>

    1
    2
    3
    <div className="list" style={{background: "#ddd"}}>
       {list_html}
    </div>

    或然JSX内部有担当繁杂的逻辑样式,可JSX的自定义标签本事,组件的黑盒性立马能体会出来,是否一差二错美好了过多。

    JavaScript

    render: function(){ return ( <div> <Menus bannerNums={this.state.list.length}></Menus> <TableList data={this.state.list}></TableList> </div> ); }

    1
    2
    3
    4
    5
    6
    7
    8
    render: function(){
        return (
          <div>
             <Menus bannerNums={this.state.list.length}></Menus>
             <TableList data={this.state.list}></TableList>
          </div>
       );
    }

    尽管如此JSX本质上是为着虚构Dom而绸缪的,但这种逻辑和视图中度合风度翩翩对于组件化未尝不是风流倜傥件善事。

     

    读书完React那一个组件化框架后,看看组件化技巧点的完毕意况

    “能源高内聚”—— (33%卡塔尔国  html与js内聚

    “成效域独立”—— (八分之四卡塔尔国  js的功效域独立

    “自定义标签”—— (百分百卡塔 尔(英语:State of Qatar)jsx

    “可交互作用结合”—— (十分之五卡塔 尔(英语:State of Qatar)  可组合,但贫乏有效的加载方式

    “接口标准化”—— (百分百卡塔 尔(阿拉伯语:قطر‎组件生命周期方法

     

    Webpack 财富组件化

    对此组件化的能源独立性,平常的模块加载工具和创设流程视乎变得艰辛。组件化的创设筑工程程化,不再是前边大家广阔的,css合二,js合三,而是体验在组件间的重视于加载关系。webpack赶巧符合须求点,一方面抵补组件化技能点,另一方支持我们完备组件化的完整创设情况。

    率先要说多美滋(Dumex卡塔尔点是,webpack是一个模块加载打包工具,用于管理你的模块能源正视打包难题。那跟大家听得多了就能说的清楚的requirejs模块加载工具,和grunt/gulp营造筑工程具的定义,多多少少有些出入又稍稍肖似。

    图片 13

    率先webpak对于CommonJS与英特尔同期帮忙,满意我们模块/组件的加载格局。

    JavaScript

    require("module"); require("../file.js"); exports.doStuff = function() {}; module.exports = someValue;

    1
    2
    3
    4
    require("module");
    require("../file.js");
    exports.doStuff = function() {};
    module.exports = someValue;

    JavaScript

    define("mymodule", ["dep1", "dep2"], function(d1, d2) { return someExportedValue; });

    1
    2
    3
    define("mymodule", ["dep1", "dep2"], function(d1, d2) {
        return someExportedValue;
    });

    本来最精锐的,最特出的,当然是模块打包作用。那正是那意气风发效果,补充了组件化财富重视,以至完整工程化的力量

    基于webpack的陈设性意见,全部能源都以“模块”,webpack内部得以完结了风流倜傥套财富加载机制,能够把想css,图片等能源等有依赖关系的“模块”加载。那跟咱们使用requirejs这种只有管理js大大差异。而那套加运载飞机制,通过多个个loader来完结。

     

    JavaScript

    // webpack.config.js module.exports = { entry: { entry: './index.jsx', }, output: { path: __dirname, filename: '[name].min.js' }, module: { loaders: [ {test: /.css$/, loader: 'style!css' }, {test: /.(jsx|js)?$/, loader: 'jsx?harmony', exclude: /node_modules/}, {test: /.(png|jpg|jpeg)$/, loader: 'url-loader?limit=10240'} ] } };

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // webpack.config.js
    module.exports = {
        entry: {
         entry: './index.jsx',
        },
        output: {
            path: __dirname,
            filename: '[name].min.js'
        },
        module: {
            loaders: [
                {test: /.css$/, loader: 'style!css' },
                {test: /.(jsx|js)?$/, loader: 'jsx?harmony', exclude: /node_modules/},
                {test: /.(png|jpg|jpeg)$/, loader: 'url-loader?limit=10240'}
            ]
        }
    };

    地方风流倜傥份轻易的webpack配置文件,在意loaders的布署,数组内二个object配置为意气风发种模块财富的加运载飞机制。test的正则为配合文件法则,loader的为相称到文件将由哪些加载器管理,多个计算机之间用相隔,管理顺序从右到左。

     

    style!css,css文件通过css-loader(管理css卡塔尔,再到style-loader(inline到html卡塔 尔(英语:State of Qatar)的加工管理流。

    jsx文件通过jsx-loader编写翻译,‘?’开启加载参数,harmony协助ES6的语法。

    图表财富通过url-loader加载器,配置参数limit,调整少于10KB的图样将会base64化。

     财富文件怎么着被require?

    JavaScript

    // 加载组件自个儿css require('./slider.css'); // 加载组件信赖的模块 var Clip = require('./clipitem.js'); // 加载图片能源 var spinnerImg = require('./loading.png');

    1
    2
    3
    4
    5
    6
    // 加载组件自身css
    require('./slider.css');
    // 加载组件依赖的模块
    var Clip = require('./clipitem.js');
    // 加载图片资源
    var spinnerImg = require('./loading.png');

    在webpack的js文件中大家除了require大家健康的js文件,css和png等静态文件也足以被require进来。大家通过webpack命令,编写翻译之后,看看输出结果怎么着:

    JavaScript

    webpackJsonp([0], { /* 0 */ /***/ function(module, exports, __webpack_require__) { // 加载组件自己css __webpack_require__(1); // 加载组件重视的模块 var Clip = __webpack_require__(5); // 加载图片能源 var spinnerImg = __webpack_require__(6); /***/ }, /* 1 */ /***/ function(module, exports, __webpack_require__) { /***/ }, /* 2 */ /***/ function(module, exports, __webpack_require__) { exports = module.exports = __webpack_require__(3)(); exports.push([module.id, ".slider-wrap{rn position: relative;rn width: 100%;rn margin: 50px;rn background: #fff;rn}rnrn.slider-wrap li{rn text-align: center;rn line-height: 20px;rn}", ""]); /***/ }, /* 3 */ /***/ function(module, exports) { /***/ }, /* 4 */ /***/ function(module, exports, __webpack_require__) { /***/ }, /* 5 */ /***/ function(module, exports) { console.log('hello, here is clipitem.js') ; /***/ }, /* 6 */ /***/ function(module, exports) { module.exports = "data:image/png;base64,iVBORw0KGg......" /***/ } ]);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    webpackJsonp([0], {
    /* 0 */
    /***/ function(module, exports, __webpack_require__) {
              // 加载组件自身css
              __webpack_require__(1);
              // 加载组件依赖的模块
              var Clip = __webpack_require__(5);
              // 加载图片资源
              var spinnerImg = __webpack_require__(6);
    /***/ },
    /* 1 */
    /***/ function(module, exports, __webpack_require__) {
     
    /***/ },
    /* 2 */
    /***/ function(module, exports, __webpack_require__) {
              exports = module.exports = __webpack_require__(3)();
              exports.push([module.id, ".slider-wrap{rn position: relative;rn width: 100%;rn margin: 50px;rn background: #fff;rn}rnrn.slider-wrap li{rn text-align: center;rn line-height: 20px;rn}", ""]);
     
    /***/ },
    /* 3 */
    /***/ function(module, exports) {
     
    /***/ },
     
    /* 4 */
    /***/ function(module, exports, __webpack_require__) {
    /***/ },
     
    /* 5 */
    /***/ function(module, exports) {
              console.log('hello, here is clipitem.js') ;
    /***/ },
    /* 6 */
    /***/ function(module, exports) {
              module.exports = "data:image/png;base64,iVBORw0KGg......"
    /***/ }
    ]);

    webpack编写翻译之后,输出文件视乎乱糟糟的,但实在每八个财富都被封装在一个函数体内,何况以编号的情势标志(注释卡塔尔。那个模块,由webpack的__webpack_require__中间方法加载。入口文件为编号0的函数index.js,能够看出__webpack_require__加载其余编号的模块。

    css文件在编号1,由于选拔css-loader和style-loader,编号1-4都是管理css。当中编号2大家可以看大家的css的string体。最后会以内联的方法插入到html中。

    图形文件在号码6,能够看出exports出base64化的图片。

     组件意气风发体输出

    JavaScript

    // 加载组件本人css require('./slider.css'); // 加载组件信任的模块 var React = require('react'); var Clip = require('../ui/clipitem.jsx'); // 加载图片能源 var spinnerImg = require('./loading.png'); var Slider = React.createClass({ getInitialState: function() { // ... }, componentDidMount: function(){ // ... }, render: function() { return ( <div> <Clip data={this.props.imgs} /> <img className="loading" src={spinnerImg} /> </div> ); } }); module.exports = Slider;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // 加载组件自身css
    require('./slider.css');
    // 加载组件依赖的模块
    var React = require('react');
    var Clip = require('../ui/clipitem.jsx');
    // 加载图片资源
    var spinnerImg = require('./loading.png');
    var Slider = React.createClass({
        getInitialState: function() {
            // ...
        },
        componentDidMount: function(){
            // ...
        },
        render: function() {
            return (
                <div>
                   <Clip data={this.props.imgs} />
                   <img className="loading" src={spinnerImg} />
                </div>
            );
        }
    });
    module.exports = Slider;

    假使说,react使到html和js合为紧凑。

    那便是说丰盛webpack,两个结合一同的话。js,css,png(base64),html 全部web财富都能合成三个JS文件。那多亏这套方案的主导所在:组件独立风流浪漫体化。倘诺要援引一个构件,仅仅require('./slider.js') 就能够实现。

     

    参加webpack的模块加载器之后,大家组件的加载难题,内聚难点也都成功地消弭掉

    “能源高内聚”—— (百分百卡塔 尔(英语:State of Qatar) 全数财富能够风华正茂js输出

    “可互相结合”—— (百分百卡塔尔国  可组合可依附加载

     

     CSS模块化实行

    很欢快,你能读书到这边。近些日子大家的组件实现度特其他高,能源内聚,易于组合,功能域独立互不污染。。。。等等图片 14,视乎CSS模块的达成度有欠缺。

    那么近期组件完毕度来看,CSS功效域其实是全局性的,并不是组件内部独立。下一步,大家要做得就是怎么让大家组件内部的CSS作用域独立。

    那会儿恐怕有人立即跳出,大喊一句“德玛西亚!”,哦不,应该是“用sass啊傻逼!”。不过花色组件化之后,组件的内部封装已经很好了,此中间dom结构和css趋向轻便,独立,以致是创痍满指标。LESS和SASS的风姿洒脱体式样式框架的安插,他的嵌套,变量,include,函数等充分的效果对于全部大型项指标样式管理特别实惠。但对于一个功用单风华正茂组件内部样式,视乎就变的略微冲突。“不能为了框架而框架,合适才是最棒的”。视乎原生的css技巧已经满意组件的体裁供给,唯独正是地点的css功能域难题。

     

    这里小编付诸思索的方案: classname随意写,保持原生的办法。编写翻译阶段,依照组件在类型路径的唯意气风发性,由【组件classname+组件独一路线】打成md5,生成全局唯意气风发性classname。正当自己要写三个loader达成作者的主见的时候,开掘歪果仁已经早在先走一步了。。。。

    这里具体方案参考作者事先博客的译文:

    前边我们钻探过JS的模块。今后经过Webpack被加载的CSS能源叫做“CSS模块”?我感到依然有题指标。以后style-loader插件的落到实处精气神上只是创办link[rel=stylesheet]要素插入到document中。这种行为和平常性引进JS模块特别分化。引进另一个JS模块是调用它所提供的接口,但引入二个CSS却并不“调用”CSS。所以引进CSS本人对于JS程序来讲并空头支票“模块化”意义,纯粹只是表明了后生可畏种财富信任——即该器件所要完结的职能还索要一些asset。

    据此,那位歪果仁还扩展了“CSS模块化”的定义,除了上面的大家必要部分作用域外,还有为数不菲作用,这里不详述。具体参谋原著 

    那些赞的一点,就是cssmodules已经被css-loader收纳。所以大家不须要依赖额外的loader,基本的css-loader开启参数modules就能够

    JavaScript

    //webpack.config.js ... module: { loaders: [ {test: /.css$/, loader: 'style!css?modules&localIdentName=[local]__[name]_[hash:base64:5]' }, ] } ....

    1
    2
    3
    4
    5
    6
    7
    8
    //webpack.config.js
    ...  
        module: {
            loaders: [
                {test: /.css$/, loader: 'style!css?modules&localIdentName=[local]__[name]_[hash:base64:5]' },
            ]  
        }
    ....

    modules参数代表开启css-modules作用,loaclIdentName为设置大家编写翻译后的css名字,为了便利debug,大家把classname(local卡塔 尔(英语:State of Qatar)和构件名字(name卡塔尔输出。当然能够在终极输出的版本为了省去提交,仅仅使用hash值就可以。别的在react中的用法差没有多少如下。

    JavaScript

    var styles = require('./banner.css'); var Banner = new React.createClass({ ... render: function(){ return ( <div> <div className={styles.classA}></div> </div> ) } });

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var styles = require('./banner.css');
    var Banner = new React.createClass({
        ...
        render: function(){
            return (
                <div>
                    <div className={styles.classA}></div>
                </div>
            )
        }
    });

    最后这里关于出于对CSS一些构思,

    至于css-modules的任何功能,小编并不策画采取。在里头分享【大家竭忠尽智地让CSS变得复杂】中聊到:

    咱俩项目中山大学部的CSS都不会像boostrap那样要求变量来设置,身为一线开荒者的大家大意能够心得到:设计员们改版UI,相对不是简轻松单的换个色或改个间隔,而是面目一新的全新UI,那纯属不是叁个变量所能消除的”维护性“。

    反而项目实战进度中,真正要减轻的是:在本子迭代进程中那多少个淘汰掉的晚点CSS,多量地积聚在档案的次序个中。大家像极了家中的欧巴酱不舍得丢弃没用的东西,因为那但是大家选用sass或less编写出具有莫斯科大学的可维护性的,分明有复用的一天。

    那几个堆放的超时CSS(or sass卡塔 尔(英语:State of Qatar)之间又有点信任,豆蔻梢头部分超时失效了,后生可畏部分又被新的体裁复用了,招致没人敢动那叁个历史样式。结果现网项目迭代还带着大量六年前没用的体裁文件。

    组件化之后,css的布局相似被修改了。或许postcss才是你现在手上最相符的工具,而不在是sass。

     

    到那边,大家好不轻便把组件化最终叁个主题素材也消除了。

    “作用域独立”—— (百分百卡塔尔 好似shadowDom效能域独立

     

    到这边,我们得以开生机勃勃瓶82年的Sprite,好好庆祝一下。不是吗?

    图片 15

     

     组件化之路还在持续

    webpack和react还可能有相当多新特别关键的性状和法力,介于本文仅仅围绕着组件化的为主干,没有种种解说。别的,配搭gulp/grunt补充webpack创设才干,webpack的codeSplitting,react的零件通信难题,开采与生育遭遇布署等等,都是全部大型项目方案的所必需的,限于篇幅难题。能够等等作者更新下篇,或大家能够自动查阅。

    然则,必须要再安利一下react-hotloader神器。热加载的付出方式相对是下一代前端开拓必备。严厉说,若无了热加载,笔者会很坚决地吐弃那套方案,即便那套方案再怎能够,我都讨厌react供给5~6s的编写翻译时间。然而hotloader可以在自身不刷新页面包车型地铁景况下,动态校订代码,并且不单单是样式,连逻辑也是即时生效。

    图片 16

    如上在form表单内。使用热加载,表单无需再行填写,更正submit的逻辑马上见到效果。那样的开销功用真不是增加仅仅叁个品位。必需安利一下。

     

    也许你发觉,使用组件化方案今后,整个本事栈都被更新了生机勃勃番。学习成本也不菲,并且可以预见到,基于组件化的前端还也许会众多相差的主题材料,比如品质优化方案要求重新考虑,以至最基本的组件可复用性不必然高。前边十分短生机勃勃段时间,须要咱们不住磨砺与优化,索求最优的前端组件化之道。

    起码大家得以想像,不再忧郁自己写的代码跟有些什么人何人冲突,不再为找某段逻辑在八个公文和格局间持续,不再copy一片片逻辑然后改改。大家每回编写都以可选拔,可构成,独立且内聚的零件。而各类页面将会由多少个个嵌套组合的构件,互相独立却相互影响。

     

    对于如此的前端现在,有所指望,不是很好啊

    至此,谢谢您的翻阅。

    1 赞 6 收藏 1 评论

    图片 17

    生机勃勃、什么是webpack:webpack是生龙活虎款模块加载兼打包工具,它能够将js、jsx、coffee、样式sass、less,图片等作为模块来利用和处理。
    二、优势:1、以commonJS的样式来书写脚本,对英特尔、CMD的支撑也很周详,方便旧项指标迁徙。2、能被模块化的反复是JS了。3、能取代部分grunt/gulp的做事,举个例子打包,压缩混淆,图片转base64等。3、扩大性强,插件机制完备,扶持React热拔插(react-hot-loader卡塔 尔(英语:State of Qatar)
    三、安装和安插:
    1、安装:直接运用npm来进展安装
    $ npm install webpack -g
    将依靠写入package.json包
    $ npm init
    $ npm install webpack --save-dev
    2、配置:
    各样门类必得布置四个webpack.config.js,功用就好像gulpfile.js/Gruntfile.js,叁个配置项,告诉webpack要做怎么着。
    示例:
    var webpack = require('webpack');
    var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js');
    module.exports = {
    //插件项
    plugins: [commonsPlugin],
    //页面入口文件配置
    entry: {
    index : './src/js/page/index.js'
    },
    //入口文件输出配置
    output: {
    path: 'dist/js/page',
    filename: '[name].js'
    },
    module: {
    //加载器配置
    loaders: [
    { test: /.css$/, loader: 'style-loader!css-loader' },
    { test: /.js$/, loader: 'jsx-loader?harmony' },
    { test: /.scss$/, loader: 'style!css!sass?sourceMap'},
    { test: /.(png|jpg)$/, loader: 'url-loader?limit=8192'}
    ]
    },
    //其余技术方案陈设
    resolve: {
    root: 'E:/github/flux-example/src', //相对路径
    extensions: ['', '.js', '.json', '.scss'],
    alias: {
    AppStore : 'js/stores/AppStores.js',
    ActionType : 'js/actions/ActionType.js',
    AppAction : 'js/actions/AppAction.js'
    }
    }
    };
    (1)plugins是插件项,这里运用了多少个CommonsChunkPlugin的插件,它用于提取四个入口文件的国有脚本有的,然后生成贰个common.js来方便多页面之间的复用。
    (2)entry是页面包车型客车进口文件配置,output是呼应的输出项配置
    {
    entry: {
    page1: "./page1",
    //支持数组方式,将加载数组中的全部模块,但以最终八个模块作为出口
    page2: ["./entry1", "./entry2"]
    },
    output: {
    path: "dist/js/page",
    filename: "[name].bundle.js"
    }
    }
    该代码会变卦二个page1.bundle.js和page2.bundle.js,并存放在./dist/js/page文件夹下。
    (3)module.loaders,告知webpack每生机勃勃种文件都供给哪些加载器来管理
    module: {
    //加载器配置
    loaders: [
    //.css 文件使用 style-loader 和 css-loader 来管理
    { test: /.css$/, loader: 'style-loader!css-loader' },
    //.js 文件使用 jsx-loader 来编写翻译处理
    { test: /.js$/, loader: 'jsx-loader?harmony' },
    //.scss 文件使用 style-loader、css-loader 和 sass-loader 来编写翻译管理
    { test: /.scss$/, loader: 'style!css!sass?sourceMap'},
    //图片文件使用 url-loader 来管理,小于8kb的直接转为base64
    { test: /.(png|jpg)$/, loader: 'url-loader?limit=8192'}
    ]
    }
    -loader可以不写,多个loader之间用“!”连接起来。全数的加载器都亟需通过npm来加载。
    诸如最终二个url-loader,它会将样式中援引到的图样转为模块来拍卖。使用前行行设置:
    $ npm install url-loader -save-dev
    布置音讯的参数:“?limit=8192”表示将具有小于8kb的图形都转为base64形式(超越8kb的才使用url-loader来映射到文件,不然转为data url方式)
    (4)resolve配置,
    resolve: {
    //查找module的话从这里起头查找
    root: 'E:/github/flux-example/src', //相对路径
    //自动扩张文件后缀名,意味着我们require模块能够省略不写后缀名
    extensions: ['', '.js', '.json', '.scss'],
    //模块外号定义,方便后续直接引用小名,无须多写长长的地址
    alias: {
    AppStore : 'js/stores/AppStores.js',//后续直接 require('AppStore') 就可以
    ActionType : 'js/actions/ActionType.js',
    AppAction : 'js/actions/AppAction.js'
    }
    }
    四、运转webpack,直接实施:
    $ webpack --display-error-details
    末尾的参数 “-display-error-details”推荐加上,方便出错开上下班时间能精通到更详实的音信。别的首要参数:
    $ webpack --config XXX.js //使用另豆蔻梢头份配置文件(比如webpack.config2.js卡塔尔来打包
    $ webpack --watch //监听变动并自行打包
    $ webpack -p //压缩混淆脚本,那些非常非常主要!
    $ webpack -d //生成map映射文件,告知哪些模块被最终包装到何地了
    -p是很关键的参数,曾经叁个未压缩的 700kb 的文件,压缩后一贯降到180kb(首要是样式那块一句就攻克风华正茂行脚本,诱致未压缩脚本变得非常的大卡塔尔国。
    五、模块引进:
    1、在HTML页面引进:引进webpack最后生成的剧本就能够:
    <!DOCTYPE html>
    <html>
    <head lang="en">
    <meta charset="UTF-8">
    <title>demo</title>
    </head>
    <body>
    <script src="dist/js/page/common.js"></script>
    <script src="dist/js/page/index.js"></script>
    </body>
    </html>
    能够看来我们连样式都休想引进,终归脚本实施时会动态生成style并标签打到head里。
    2、JS引入:各脚本模块能够采取common.js来书写,并得以一向引进未经编写翻译的模块,比如:jsx,coffee,sass,只要在webpack.config.js中配置好了对应的加载器就行。
    编写翻译页面包车型大巴输入文件:
    require('../../css/reset.scss'); //加载早先化样式
    require('../../css/allComponent.scss'); //加载组件样式
    var React = require('react');
    var AppWrap = require('../component/AppWrap'); //加载组件
    var createRedux = require('redux').createRedux;
    var Provider = require('redux/react').Provider;
    var stores = require('AppStore');
    var redux = createRedux(stores);
    var App = React.createClass({
    render: function() {
    return (
    <Provider redux={redux}>
    {function() { return <AppWrap />; }}
    </Provider>
    );
    }
    });
    React.render(
    <App />, document.body
    );

    其他:
    1、shimming :
    在 英特尔/CMD 中,我们须要对不符合标准的模块(比如一些一向回到全局变量的插件卡塔尔国实行shim 处理,那时我们供给使用 exports-loader 来辅助:
    { test: require.resolve(“./src/js/tool/swipe.js”), loader: “exports?swipe”}
    尔后在本子中要求援引该模块的时候,这么轻松地来采用就能够了:
    require(‘./tool/swipe.js’);
    swipe();
    2、自定义公共模块提取:
    在篇章初始大家利用了 CommonsChunkPlugin 插件来领取八个页面之间的共用模块,并将该模块打包为 common.js 。
    但有的时候候我们希望能进一层特性化一些,大家得以如此安顿:
    var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
    module.exports = {
    entry: {
    p1: "./page1",
    p2: "./page2",
    p3: "./page3",
    ap1: "./admin/page1",
    ap2: "./admin/page2"
    },
    output: {
    filename: "[name].js"
    },
    plugins: [
    new CommonsChunkPlugin("admin-commons.js", ["ap1", "ap2"]),
    new CommonsChunkPlugin("commons.js", ["p1", "p2", "admin-commons.js"])
    ]
    };
    // <script>s required:
    // page1.html: commons.js, p1.js
    // page2.html: commons.js, p2.js
    // page3.html: p3.js
    // admin-page1.html: commons.js, admin-commons.js, ap1.js
    // admin-page2.html: commons.js, admin-commons.js, ap2.js
    3、独立包装样式:
    一时候也许希望项指标体制能不用被打包到脚本中,而是独立出来作为.css,然后在页面中以标签引进。那时候大家需要extract-text-webpack-plugin 来扶持:
    var webpack = require('webpack');
    var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js');
    var ExtractTextPlugin = require("extract-text-webpack-plugin");
    module.exports = {
    plugins: [commonsPlugin, new ExtractTextPlugin("[name].css")],
    entry: {
    //...省略其它配置
    提起底 webpack 实行后会乖乖地把体制文件提收取来:
    4、使用CDN远程文件:
    一时候我们意在有个别模块走CDN并以<script>的样式挂载到页面上来加载,但又希望能在 webpack 的模块中选拔上。
    此刻大家能够在配备文件里选用 externals 属性来协理:
    {
    externals: {
    // require("jquery") 是援用自外界模块的
    // 对应全局变量 jQuery
    "jquery": "jQuery"
    }
    }
    供给注意的是,得保证 CDN 文件必需在 webpack 打包文件引进以前先引进。
    咱俩倒也得以运用 script.js 在剧本中来加载我们的模块:
    var $script = require("scriptjs");
    $script("//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js", function() {
    $('body').html('It works!')
    });
    5、与grunt/gulp相结合:
    gulp.task("webpack", function(callback) {
    // run webpack
    webpack({
    // configuration
    }, function(err, stats) {
    if(err) throw new gutil.PluginError("webpack", err);
    gutil.log("[webpack]", stats.toString({
    // output options
    }));
    callback();
    });
    });
    本来我们只供给把安顿写到 webpack({ … }) 中去就可以,无须再写 webpack.config.js 了。

    本文由澳门402永利com发布于前端技术,转载请注明出处:致我们终将组件化的Web

    关键词:

上一篇:没有了

下一篇:没有了