页面布局进阶,大型单页面应用的进阶挑战

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

2015/09/30 · HTML5页面布局进阶,大型单页面应用的进阶挑战。,
JavaScript ·
单页应用

原著出处: 林子杰(@Zack__lin)   

阅读须知:此地的巨型单页面应用(SPA Web
App)是指页面和法力组件在2个某部量级以上,举个栗子,比如
30+个页面100+个零件,同时伴随着大批量的数据交互操作和多个页面包车型客车数量同步操作。并且那里涉及的页面,均属于
hash 页面,而多页面概念的页面,是二个独门的 html
文书档案。基于这么些前提,大家再来探究,不然小编怕咱们 Get 不到同3个 G 点上去。

后天给我们写三个页面布局的艺术,接济大家更显著的认识页面布局,方式仅供参考。

正是指二个种类只加载贰次财富,之后的操作交互、数据交互是通过路由、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>

姣好效果

挑衅一:前端组件化

依照咱们所说的前提,第②个面对的挑衅是组件化。那里依旧要强调的是组件化根本指标不是为着复用,很五人历来没想领悟那一点,总是觉得造的车轱辘其余事情能够用,说不定以后也能够用。

实则前端发展迭代这么快,交互变化也快,种种适配更新不乏先例。前几日造的轮子,过阵子别人造了个高级轮子,大家都会选更高级的车轮,所以今后前端界有1个情景就是为着让外人用本人的车轮,自个儿拼命不停地造。

在前端工业化生产趋势下,假使要拉长生产功效,就不能够不让组件规范化标准化,达到怎么着的水平呢?一辆车除了底盘和车身框架须要本身安排成立之外,别的条件零件都能够购买组装(专业学得差,有吗谬误请指正)。也等于说,除了
UI
和前端框架结构须要自个儿消除之外,其余的机件都以能够普及拿来主义的,假若打算让车子跑得更稳更安全,能够对组件举行打磨优化完善。

说了如此说,倒不比看看徐飞的篇章《二〇一五前端组件化框架之路》 里面写的始末都是通过一定实践得出的想法,所以抢先三分之二剧情我是同情而且深有体会的。

首先给我们看一下自笔者的文书夹及文件

特色是加载次数少,加载今后品质较高, 不便利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
很友善吧,当然这是后话了,跟大家今日说的不是1回事。那种目录结果,不用我们去给
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

挑衅三:领域数据宗旨化

对此单向数据流循环和数量双向绑定哪个人优何人劣是永远也钻探没结果的难题,要看是什么事情场景什么工作逻辑,如若那一个前提没统一好说吗都以徒劳无益。当然,那个挑衅的前提是非后台的单页面应用,后台的前端根本就不须要考虑前端内部存款和储蓄器缓存多少的拍卖,间接跟接口数据库交互就行了。明显了这些前提,我们跟着斟酌哪些叫世界数据中央化。

日前议论到二种多少绑定的法门,不过如果频仍跟接口交互:

  • 内部存款和储蓄器数据销毁了,重新请求数据耗费时间浪费流量
  • 一旦多少个接口字段部分不平等只是利用意况一样
  • 多少个页面一贯有一部分的多寡一致,不过先来后到导致某个计数字段区别
  • 七个页面包车型的士数码一致,当中一些数据发生用户操作行为造成数据产生改变

故此,大家必要在工作视图逻辑层和数码接口层中间扩张1个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

  • 叁拾5个赞地点可点赞或许撤回,八个页面包车型大巴赞数须要联合,按钮点赞与裁撤的境况也要一起。
  • 条目是还是不是已收藏,废除收藏后 Page B 必要删除数据,Page A+C
    须要一起状态,假设在 Page C 又有收藏操作,Page B
    必要相应增减数据,Page A 状态须要一块。
  • 发评论,Page C 需求更新评论列表和评论数,Page A+B
    必要创新评论数。假设 Page B 没有被加载过,那时候 Page B
    获得的数据应该是前卫的,要求一块给 A+C 页面对应的数量开始展览立异。

为此,store
干的活正是多少状态读写和一道,假使把数据状态的操作放到种种页面本人去处理,页面一旦多了或许复杂起来,就会发生种种页面数据和景色可能分裂等,页面从前双向引用(业务耦合严重)。store
还有另一个功能就是数据的输入输出格式化,容易举个栗子:图片 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文件重庆大学用来存放在一些足以重复使用的样式。

发表评论

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

网站地图xml地图