《前端架构从入门到微前端》 Chapter5. 架构设计.多页面应用

128 阅读5分钟

单页面,通常在第一次加载时,便加载全部的静态资源,而多页面则是在每次加载新页面时会重新加载(http缓存会使大部分资源不会重新加载)

本章核心: MVC框架的一些核心要素: 双向绑定、模版引擎、路由映射

5.1 virtual-dom 生成dom的过程:

    var h = require('virtual-dom/h');
    var diff = require('virtual-dom/diff!);
    var patch =, require ('virtual-dom/patch' ) ;var cereateBlement = require('virtual-dom/create-element');
    //1:创建一个函数来声明DOM对应的属性
    function render(count) {
        return h('a', (href: 'https://aofe.phodal.com/'), count);
   }
    渲染前
    //2:初始化document
    var count=0;
    //创建应用 data,比如用于存储的count
    var tree = render(count);
    //初始化树
    var rootNode = createElement(tree);
    document.body.appendChild(rootNode); //将DOM节点添加到document
    //创建一个初始化的DOM节点
    //3:整合起来来更新逻辑
    setInterval(function (){
        count++;
        var newTree= render(count);
        varpatches = diff(tree,newTree);
        rootNode =patch(rootNode,patches);
        tree = newTree;
    ,1000);
我们将上述代码分为三步:

第一步,将 a 标签的文本变成了 cout变量;

第二步,将count变量赋值为0,并初始化 DOM 节点,添加到页面中;

第三步,生成cunt值变化后的虚拟 DOM 树,并通过 diff 方法来对比新旧虚拟 DOM 树,再通过 patch方法将变化后的DOM -一修改到原始的 rootNode 节点上。

我们可以发现整个过程依赖于虚拟的 DOM 树,它实际上相当于一个 DSL(领域特定语言),提供了一个模板与真实的 DOM 节点的代理,所有关的修改在这层 DSL 数据进行优化,以此来应对复杂的 DOM 操作的性能问题。

5.2 双向绑定的原理和实践:

实现双向绑定有以下几种不同的实现方式:

  1. 手动绑定:即两个单身绑定的结合,通过手动set和get数据来触发UI或数据变化
  2. 脏检查机制:即发生指定的事件(如http请求,dom事件)时,遍历数据相应的元素,然后进行数据比较,对变化的数据进行操作
  3. 数据劫持:通过hack的方式(object.defineProperty())对数据的setter和getter进行劫持,在数据变化时,通知相应的数据订阅者,以触发相应的监听回调,现在比较火的vue2则是使用这种方式,所以vue2只支持IE9及以上的浏览器
   
 var scope ={};
  var eiements = document.queryselectorAll("idata-tw-bindy")
  var value;
  object.defineProperty(scope, prop,
    set: function (newValue) {
    value =newValue;
    elements.forEach(function (element) (
        if (element.getAttribute('data-tw-bind') === prop) {
            if (element.type && (element.type === itext')) {
                element.value = newValue;
            }
       }
    }
    else if (!element.type) {
        element.innerHTML = newValue; 
    },
    get: function (){
        return value;
    },
    enumerable: true
    

这段代码与前一步中代码的主要差异在 set 函数部分。

首先,我们更新了对象的值;然后,从已有的元素里寻找是否有绑定的值,即通过eelement.getAttribute(data-tw-bind)==prop 来判断是否有绑定的值;最后,根据元素的类型来进行相应的处理。为了示例方便我们只处理类型是text的值,如果类型是 text,就改变它的value 为新的值。

这样,我们就可以在更新 model值的时候,同时更新到UI上,并在UI的值发生变化的时候,将值更新到 model 上。由于Obiect.defineProperty 只支持IE9 及以上的浏览器

有了这样一个双向绑定的功能,我们就可以很方便地处理大量数据与 UI 的交互,而不是手动去编写更新值的方法。

5.3 前端路由原理及实战:

传统的多应用,路由和相关状态都是后端控制,发生变化也是后端处理,当应用中心发生转变,发展至单页面时,都是由前端控制路由,它用于连接分散在各处的控制逻辑。

从前端路由的基本原理来看,要实现一个前端路由并不复杂。在实现之前了解一下两种类型的前端路由。

基于Hisory 的路由和传统的路由基本一样, 区别是它可以通过JavaScript中的History API 无更新更改地址栏链接,配合 Ajax可以做到无刷新提供的几个基本方法,我们可以自动操作单页面应用的所有逻辑:

back,返回前一页。

forward,在浏览器记录中前往下一页。

go,在当前页面的相对位置从浏览器历史记录加载页面

opushState,按指定的名称和URL将数据push进会话历史栈

oreplaceState,指定的数据、名称和URL,更新历史栈上最新的入口

由于HistoryAPI是基于真实的URL操作的,如果没有配置好相应的HTTP服务那么在刷新页面的时候就找不到相应的页面。

基于Hash的路由在浏览器地址栏是通过#号及后面的部分来代表 URL,其背后原理要从DOMAPI中的location讲起,location.href会获取当前页面的URL,而locationh将会获取#后面的内容,如我们的URL是www.phodal.com/#test=true, 那么我们可以通过location.hash来获取#test=true的值。当一个窗口的Hash(URL中#后面的部分改变时就会触发 hashchange 事件。

监听相应的事件: window.addEventListener (hashchangc"改funRef,false)。绑定路由与函数的映射关系,就可以完成一个基础的路由系统。

此外,路由的 Hash 还具有以下一些特点:

(1)Hash 值的改变不会导致页面重新加载。

(2)Hash 值由浏览器控制,不会发送到服务器端

(3)Hash 值的改变会记录在浏览器的访问历史中,因此可以在浏览器中前进和后退,还有一点需要注意:使用浏览器访问网页时,如果网页 URL 中带有 Hash,页面就会定位到id(或 name)与 Hash 值一样的元素的位置。

它的这些性质适合用于前端路由。同时,由后端控制路由的多页面应用,往往只会采用 Hash 的方式来进行路由。那么,让我们来探索一下如何自制一个 Hash 路由器以便于让我们进行页面间的参数传递,简化复杂应用的开发流程。

   const appRoutes: Routes = [ 
       { path: 'crisis-center', component: CrisisListComponent },
       { path:'hero/:id',component: HeroDetailComponent },
       { path: '**'component: PageNotFoundComponent  }    
   ]

在这个示例中,我们清晰地看到路由和组件之间的映射。在 appRoutes 数组中的对象里: path 参数使用正则表达式定义了要跳转的路由;component 则是对应的要调用的组件