事件的应用,页面布局进阶

巨型单页面应用的进阶挑衅

2015/09/30 · HTML5,
JavaScript ·
单页应用

最初的小说出处: 林子杰(@Zack__lin)   

开卷须知:此间的特大型单页面应用(SPA Web
App)是指页面和功效组件在1个某部量级以上,举个栗子,比如
30+个页面100+个零部件,同时伴随着多量的数据交互操作和三个页面包车型地铁数量同步操作。并且那里提到的页面,均属于
hash 页面,而多页面概念的页面,是一个单独的 html
文档。基于那么些前提,大家再来斟酌,不然本人怕大家 Get 不到同3个 G 点上去。

前天给大家写一个页面布局的法门,协助我们更清楚的认识页面布局,格局仅供参考。

正是指二个系统只加载2次能源,之后的操作交互、数据交互是经过路由、ajax来开始展览,页面并没有刷新。

1: 完毕如下图Tab切换的功能

葡京娱乐网上娱乐 1

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>tab切换</title>
  <style>
    * {
      box-sizing: border-box;
    }

    ul,
    li {
      margin: 0;
      padding: 0;
      list-style: none;
    }

    .tab-ct .header>li {
      width: 50px;
      height: 30px;
      line-height: 30px;
      text-align: center;
      border: 1px solid #ccc;
      display: inline-block;
      margin-right: -5px;
      border-right: none;
      cursor: pointer;
    }

    .tab-ct .header>li:last-child {
      border-right: 1px solid #ccc;
    }

    .tab-ct .header>li.active {
      background: #a5daa6;
    }

    .tab-ct .content>li {
      width: 149px;
      height: 100px;
      border: 1px solid #ccc;
      background: #f5daa6;
      border-top: none;
      padding: 10px;
      display: none;
    }

    .tab-ct .content>li.active {
      display: block;
    }
  </style>
</head>

<body>
  <div class="tab-ct">
    <ul class="header">
      <li class="active">tab1</li>
      <li>tab2</li>
      <li>tab3</li>
    </ul>
    <ul class="content">
      <li class="active">我是tab1内容</li>
      <li>我是tab2内容</li>
      <li>我是tab3内容</li>
    </ul>
  </div>

  <script>
    var tabs = document.querySelectorAll('.tab-ct .header>li')
    var panels = document.querySelectorAll('.tab-ct .content>li')

    // for(var i = 0; i < tabs.length; i++){       // 使用for循环去遍历也可以
    //   tabs[i].addEventListener('click', function(){
    //     console.log(this)
    //   })
    // }

    tabs.forEach(function(tab){ //使用遍历,给tabs 里的 每一个tab (li) 绑定事件
      tab.addEventListener('click', function(){
        tabs.forEach(function(node){ //遍历的时候,每一次,先再来个遍历,去除所有的active class
          node.classList.remove('active')
        }) // this之外, 还可以是e.target  window.event.target
        this.classList.add('active') // 然后给当前的tab添加active

        // 接下来,点击header 里面的li,如何对应上 content里面的li
        // 这就需要知道点击的是第几个li,有两种方法:
        // this 看索引号 , 获得数组循环对比
        var index = [].indexOf.call(tabs, this) //找到当前li的index
        panels.forEach(function(node){ //同上,先去除content里面所有li的active class
          node.classList.remove('active')
        })
        panels[index].classList.add('active') //给与tab对应的content li添加active
      })
    })

    // 注意点
    // 1. 数组遍历要熟练, 2. classList操作要熟练 3. 绑定事件
  </script>
</body>
</html>

形成效果

挑衅一:前端组件化

听他们说大家所说的前提,第3个面对的挑衅是组件化。那里照旧要强调的是组件化根本指标不是为着复用,很多个人一直没想了然那点,总是觉得造的轮子其余事情能够用,说不定现在也足以用。

事件的应用,页面布局进阶。实质上前端发展迭代这么快,交互变化也快,各样适配更新不乏先例。后天造的车轮,过阵子别人造了个高级轮子,我们都会选更高级的车轱辘,所以未来前端界有二个光景正是为着让旁人用本身的车轮,本身拼命不停地造。

在前端工业化生产趋势下,若是要增加生产效能,就亟须让组件规范化标准化,达到什么的水平呢?一辆车除了底盘和车身框架要求协调统一筹划创立之外,别的规格零件都得以购置组装(专业学得差,有甚谬误请指正)。相当于说,除了
UI
和前端架构需求协调消除之外,别的的组件都以能够普及拿来主义的,借使打算让车子跑得更稳更安全,能够对组件进行打磨优化完善。

说了这么说,倒不如看看徐飞的稿子《2014前端组件化框架之路》 里面写的剧情都以透过一定实践得出的想法,所以超过二分一内容自个儿是赞成而且深有体会的。

先是给我们看一下自家的文件夹及文件

个性是加载次数少,加载今后质量较高, 不便于seo
假若页面辅助h5能够用h5格局+服务器路由rewrite+h5 history
api去掉路由的锚点,和寻找软件优化lib举行seo优化。

葡京娱乐网上娱乐,2. 落实下图的模态框作用,点击模态框不隐藏,点击关闭以及模态框以外的区域模态框隐藏

葡京娱乐网上娱乐 2

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    .modal-dialog {
      display: none;
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background:rgba(0,0,0,0.7);
    }

    .modal-dialog .bt {
      display: inline-block;
      padding: 3px 6px;
      border: 1px solid #ccc;
      border-radius: 3px;
      font-size: 14px;
    }

    .modal-dialog a {
      text-decoration: none;
      color:deepskyblue;
    }
    /* 下面的cover,是为了展示整个modal,所添加的cover,上面的区别在于,没有display: none; 
    这里提供参考,在css最末尾用了个较为简单明了的方法,可以满足目前的简单需求 */
    /* .modal-dialog .cover {
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: #000;
      opacity: 0.5;
      z-index: 99;
    } */
    .modal-dialog .modal-ct {
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: 300px;
      border: 1px solid #ccc;
      border-radius: 5px;
      background: #fff;
      z-index: 100;
    }
    .modal-dialog .modal-ct .header {
      position: relative;
      height: 36px;
      line-height: 36px;
      border-bottom: 1px solid #ccc;
    }
    .modal-dialog .modal-ct .header h3 {
      margin: 0;
      padding-left: 10px;
      font-size: 16px;
    }
    .modal-dialog .modal-ct .header .close {
      position: absolute;
      right: 10px;
      top: 10px;
      line-height: 1;
    }
    .modal-dialog .modal-ct .content {
      padding: 10px;
    }
    .modal-dialog .modal-ct .footer {
      padding: 10px;
      border-top: 1px solid #eee;
    }
    .modal-dialog .modal-ct .footer:after {
      content: '';
      display: table;
      clear: both;
    }
    .modal-dialog .modal-ct .footer .btn {
      float: right;
      margin-left: 10px;
    }

    .open {
      display: block;
    }
  </style>
</head>
<body>
  <div class="btn-group">
    <button id="btn-modal">点我1</button>
  </div>

  <div id="modal-1" class="modal-dialog">
    <div class="modal-ct">
      <div class="header">
        <h3>我是标题1</h3>
        <a href="#" class="close">x</a>
      </div>
      <div class="content">
        <p>我是第一段</p>
        <p>我是第二段</p>
      </div>
      <div class="footer">
        <a href="#" class="btn btn-confirm">确定</a>
        <a href="#" class="btn btn-cancel">取消</a>
      </div>
    </div>
  </div>

  <script>
    var btn = document.querySelector('#btn-modal'),
      modal = document.querySelector('#modal-1'),
      modalCt = document.querySelector('#modal-1 .modal-ct'),
      close = document.querySelector('#modal-1 .close'),
      btnConfirm = document.querySelector('.btn-confirm'),
      btnCancel = document.querySelector('.btn-cancel')

    btn.addEventListener('click', function(){ //默认模态框隐藏,现在要展示 加clas或操作style(最好前者 比如加个open)
      modal.classList.add('open')
    })

    // 声明一个事件处理函数,以便 x  确定 取消, 都能绑定相同的操作
    function handler(){
      modal.classList.remove('open')
    }
    close.addEventListener('click', handler)
    btnConfirm.addEventListener('click', handler)
    btnCancel.addEventListener('click', handler)

    // 现在希望点击模态框以外的,也能消失
    modal.addEventListener('click', function(){
      modal.classList.remove('open')
    })
    // 但是点击到里面的 ct容器,会向上冒泡,从而也会隐藏modal,所以需要取消ct的事件冒泡
    modalCt.addEventListener('click',function(e){
      e.stopPropagation()
    })
  </script>

</body>
</html>

成功能果

挑衅二:路由去中央化

听他们说我们所说的前提,中央化的路由维护起来很坑爹(假如做一四个页面 DEMO
的就没要求出来现眼了)。MV*
架构就是存在这么个坑爹的题目,需求注解中央化 route(angular 和 react
等都要求先评释页面路由协会),针对分裂的路由加载哪些组件模块。一旦页面多起来,甚至只要有人偷懒直接在某些路由写了部分工作耦合的逻辑,这一个route 的可维护性就变得有点不好了。而且用户访问的第三个页面,都急需加载
route,就算其余路由的代码跟当前页面无关。

大家再回过头来思考静态页面简单的加载形式。大家假设把 nginx 搭起来,把
html 页面放在对应的静态能源目录下,运行 nginx 服务后,在浏览器地址栏输入
127.0.0.1:8888/index.html
就足以访问到那几个页面。再复杂一点,大家把目录整成下边包车型地铁花样:

/post/201509151800.html /post/201509151905.html /post/201509152001.html
/category/js_base_knowledge.html /category/css_junior_use.html
/category/life_is_beautiful.html

1
2
3
4
5
6
/post/201509151800.html
/post/201509151905.html
/post/201509152001.html
/category/js_base_knowledge.html
/category/css_junior_use.html
/category/life_is_beautiful.html

那种目录结构很熟吧,对 SEO
很和睦吧,当然那是后话了,跟大家明日说的不是3遍事。那种目录结果,不用大家去给
Web Server 定义一堆路由规则,页面存在即重临,不然再次回到404,完全不须要多余的申明逻辑。

基于那种目录结构,大家得以抽象成那规范:

/{page_type}/{page_name}.html

1
/{page_type}/{page_name}.html

实则还足以更简便:

/p/{name}.html

1
/p/{name}.html

从组件化的角度出发,还是能那样子:

/p/{name}/name.js /p/{name}/name.tpl /p/{name}/name.css

1
2
3
/p/{name}/name.js
/p/{name}/name.tpl
/p/{name}/name.css

之所以,根据大家简化后的逻辑,大家只要求二个 page.js
那样四个路由加载器,依照大家约定的财富目录结构去加载相应的页面,大家就不须要去干证明路由并且中央化路由那种蠢事了。具体来看代码。咱也无意去分析了,里面有注释。

葡京娱乐网上娱乐 3

挑衅三:领域数据焦点化

对于单向数据流循环和数据双向绑定什么人优什么人劣是世代也研究没结果的标题,要看是怎么事情场景什么事情逻辑,倘若那么些前提没统一好说吗都是对牛弹琴。当然,那么些挑战的前提是非后台的单页面应用,后台的前端根本就不供给考虑前端内存缓存多少的拍卖,直接跟接口数据库交互就行了。显著了那个前提,大家随后琢磨什么叫世界数据核心化。

前方议论到两种多少绑定的不二法门,可是假设反复跟接口交互:

  • 内部存款和储蓄器数据销毁了,重新请求数据耗费时间浪费流量
  • 要是五个接口字段部分不雷同只是使用情况同样
  • 多少个页面一向有一部分的多寡一致,然则先来后到导致一些计数字段不平等
  • 四个页面包车型客车数码一致,其中一些数据产生用户操作行为致使数据产生转移

之所以,我们需求在事情视图逻辑层和多少接口层中间扩张三个store(领域模型),而以此 store 需求有多个联结的 内存缓存 cache,这几个cache 正是焦点化的多少缓存。那那一个 store 毕竟是用来弄啥勒?

葡京娱乐网上娱乐 4

Store 具有多形态,各种 store
好比某一类物品的存款和储蓄(领域,换个词不难明白),如蔬菜水果店 fruit-store,
衣服店
clothes-store,蔬菜水果店能够放苹果香蕉黑木耳,衣裳店能够放奶头布底裤人字拖。假诺品种过于繁多,大家得以把蔬菜水果店精细化运行变成香蕉专卖店,苹果专卖店(!==
appstore),甚至是木耳专卖店…( _
_)ノ|,蔬菜水果连串不一样,不过也都是称重按斤卖嘛。

var bannerStore = new fruitStore();

var appleStore = new fruitStore();

有了这个囤积之后,我们能够放心的把数据丢给视图逻辑层大胆去用。想修改数据?直接让
store 去改就行了,别的页面包车型客车 DOM
文本内容也得修改吧?这是别的页面包车型地铁事务逻辑做的事,大家把事件抛出去就好了,他们处不处理那是她们的事,咱别瞎操心(业务隔开)。

那就是说 store 具体弄啥勒?

葡京娱乐网上娱乐 5

  • 三贰10个赞地方可点赞恐怕撤回,多少个页面包车型地铁赞数须求一块,按钮点赞与撤废的图景也要共同。
  • 条目是或不是已收藏,撤消收藏后 Page B 要求删除数据,Page A+C
    需求一起状态,假使在 Page C 又有收藏操作,Page B
    需求相应增减数据,Page A 状态要求一块。
  • 发评论,Page C 要求更新评论列表和评价数,Page A+B
    须求立异评论数。假诺 Page B 没有被加载过,那时候 Page B
    得到的数量应该是风靡的,供给一块给 A+C 页面对应的数码进行立异。

所以,store
干的活正是数据状态读写和同步,若是把数量状态的操作放到种种页面本人去处理,页面一旦多了恐怕复杂起来,就会产生各样页面数据和景色只怕区别,页面从前双向引用(业务耦合严重)。store
还有另3个效用正是数据的输入输出格式化,不难举个栗子:葡京娱乐网上娱乐 6

  • 任何接口 API 重返的数目,都亟需通过 input format
    实行联合格式化,然后再写入
    cache,因为读取的多少已根据大家约定的规范实行的拍卖,所以大家应用的时候也不须要理会接口是重临怎样的数据类型。
  • 或多或少零部件须要的数码字段格式可能两样,若是把数量处理放在模板实行处理,会造成模板不可能越发从简通用(业务耦合),所以需要output format 实行拍卖。

从而,store
就是扮演着那样的角色——是多少状态读写和一块,以及数据输入输出的格式化处理。

文件

挑战四:Hybrid App 化

到现在 Hybrid App 架构应用非常的红啊 _
(:3」∠)_,不搞一下都倒霉意思说自身是做 H5的。那里所说的 Hybrid App
可不是那种内置打包的 html 源码那种,而是直接去服务端请求 html
文书档案那种,可能会接纳离线缓存。有的人觉着就算要运用 Hybrid
架构,就不能够采取 SPA 的艺术,其实 Hybrid 架构更应该运用 SPA。

相见的多少个难题,小编总结列举一下:

  • 客户端通过 url 传参

    假设经过 http get 请求的 query 参数实行传参,会导致命中不到 html
    文书档案缓存,所以经过 SPA 的 hash query 传参,能够规避那些难题。

  • 与其它 html 页面实行跳转

    那种现象下,进入新页面和再次回到旧页面导致 webview 会重新加载本地的
    html 文档缓存,视觉体验很不爽,即便页面使用了离线缓存,而 SPA
    能够规避那些标题。

  • 选拔了离线缓存的页面须求支持代码多版本化

    是因为接纳了非覆盖质量源发表办法,所以要求依旧保留旧的代码一段时间,以防患用户接纳旧的
    html
    文书档案访问一些按需加载功用或化解了地点缓存数据而拿不到旧版本代码。

  • js 和 css 资源 离线化

    是因为离线缓存的能源必要先在 manifest
    文件宣称,你也不或许总是手动去敬重须求引用的 js 和 css
    财富,并且那多少个按需加载的功能也会由此错过按需加载的意思。所以必要将
    js 和 css 缓存到
    localstorage,直接省去这一步维护操作。至于用户清除
    localstorage,参考第贰点化解方案。

  • 图标财富离线化

    将图标文件实行 base64 编码后存入 css 文件,方便离线使用。

此处有四个base.css文件,那些css文件根本用来存放在一些能够重复使用的体制。

相关文章