您的位置:新葡亰496net > 新葡亰官网 > 官方文书档案笔记,angular连串学习笔记

官方文书档案笔记,angular连串学习笔记

发布时间:2019-08-03 13:32编辑:新葡亰官网浏览(50)

    你一定是闲得蛋疼才重构的吧

    2018/07/25 · 基础技术 · 重构

    原文出处: 奇舞团 - hxl   

    随着“发布”进度条走到100%,重构的代码终于上线了。我露出了老母亲般的围笑……

    最近看了一篇文章,叫《史上最烂的开发项目长啥样:苦撑12年,600多万行代码》,讲的是法国的一个软件项目,因为各种奇葩的原因,导致代码质量惨不忍睹,项目多年无法交付,最终还有公司领导入狱。里面有一些细节让人哭笑不得:一个右键响应事件需要花45分钟;读取700MB的数据,需要花7天时间。足见这个软件的性能有多糟心。

    如果让笔者来接手这“坨”代码,内心早就飘过无数个敏感词。其实,笔者自己也维护着一套陈酿了将近7年的代码,随着后辈的添油加醋……哦不,添砖加瓦,功能逻辑日益复杂,代码也变得臃肿,维护起来步履维艰,性能也不尽如人意。终于有一天,我听见了内心的魔鬼在呼唤:“重构吧~~”

    重构是一件磨人的事情,轻易使不得。好在兄弟们齐心协力,各方资源也配合到位。我们小步迭代了大半年,最后一鼓作气,终于完成了。今天跟大家分享一下这次重构的经验和收益。

    • Vue2简单入门
    • Vue.js再入门
    • Vue.js使用vue-router构建单页应用
    • Vue.js状态管理工具Vuex快速上手

    现前端框架情况, angular、vue(比较火)、react。react因前段时间license协议,百度要求内部停止使用react。

    摘自前端农民工的博客

    让我们先来看几个网站:

    coding

    teambition

    cloud9

    注意这几个网站的相同点,那就是在浏览器中,做了原先“应当”在客户端做的事情。它们的界面切换非常流畅,响应很迅速,跟传统的网页明显不一样,它们是什么呢?这就是单页Web应用。

    所谓单页应用,指的是在一个页面上集成多种功能,甚至整个系统就只有一个页面,所有的业务功能都是它的子模块,通过特定的方式挂接到主界面上。它是AJAX技术的进一步升华,把AJAX的无刷新机制发挥到极致,因此能造就与桌面程序媲美的流畅用户体验。

    其实单页应用我们并不陌生,很多人写过ExtJS的项目,用它实现的系统,很天然的就已经是单页的了,也有人用jQuery或者其他框架实现过类似的东西。用各种JS框架,甚至不用框架,都是可以实现单页应用的,它只是一种理念。有些框架适用于开发这种系统,如果使用它们,可以得到很多便利。

    学习过程中突发奇想

    • 程序语言都是处理0和1 肯定很多地方是一样的,那么肯定有书教你怎么写程序
    • 学习编程的第一要素是要语文好,语文好理解能力,看文档才能懂
    • vue模板什么意思?
      //HTML 模板应该指的是原生HTML,通过 el 挂载到 Vue 实例上。如在 DOM-模版解析说明 章节提到的 HTML 模板解析的一些限制
      <table>
      <my-row>...</my-row>
      </table>
      //字符串模板,即JavaScript内联模版字符串
      Vue.component('my-component', {
      template: '<div>A custom component!</div>'
      })
    • 根据上面的知识点启发,貌似运行环境遇到不认识的语法,都默认把代码当成字符串
    • 镜像就是副本的意思,因为国内有墙,所以用npm下载东西有时候会太慢甚至挂掉,所以有的大公司会镜像也就是copy一模一样常用的框架和库提供给国内互联网开发者

    挑战

    此次重构的对象是一个大型单页应用。它实现了云端文件管理功能,共有10个路由页面,涉及文件上传、音视频播放、图片预览、套餐购买等几十个功能。前端使用QWrap、jQuery、RequireJS搭建,HTML使用PHP模板引擎Smarty编写。

    我们选择了Vue.js、vue-router、vuex来改造代码,用webpack完成模块打包的工作。仿佛一下子从原始社会迈向了新世纪,是不是很完美?

    新葡亰496net 1

    (图片来自网络)

    由于项目比较庞大,为了快速迭代,重构的过渡期允许新旧代码并存,开发完一部分就测试上线一部分,直到最终完全替代旧代码。

    然鹅,我们很快就意识到一个问题:重构部分跟新增需求无法保证一致。比如重构到一半,线上功能变了……产品不会等重构完再往前发展。难不成要在新老代码中并行迭代相同的需求?

    别慌,一定能想出更高效的解决办法。稍微分析一下,发现我们要处理三种情况:

    1. 产品需要新增一个功能。比如一个活动弹窗或路由页面。

    解决方法:新功能用vue组件实现,然后手动加载到页面。比如:

    JavaScript

    const wrap = document.createElement('div') document.body.appendChild(wrap) new Vue({ el: wrap, template: '<App />', components: { App } })

    1
    2
    3
    4
    5
    6
    7
    const wrap = document.createElement('div')
    document.body.appendChild(wrap)
    new Vue({
      el: wrap,
      template: '<App />',
      components: { App }
    })

    如果这个组件必须跟老代码交互,就将组件暴露给全局变量,然后由老代码调用全局变量的方法。比如:

    JavaScript

    // someApp.js window.someApp = new Vue({ ... methods: { funcA() { // do somthing } } })

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // someApp.js
    window.someApp = new Vue({
      ...
      methods: {
        funcA() {
          // do somthing
        }
      }
    })

    JavaScript

    // 老代码.js ... window.someApp.funcA()

    1
    2
    3
    // 老代码.js
    ...
    window.someApp.funcA()

    注意:全局变量名需要人工协调,避免命名冲突。PS:这是过渡期的妥协,不是最终状态

    新增一个路由页面时更棘手。聪明的读者一定会想到让新增的路由页面独立于已有的单页应用,单独分配一个URL,这样代码会更干净。

    假如新增的路由页面需要实现十几个功能,而这些功能已经存在于旧代码中呢?权衡了需求的紧急性和对代码整洁度的追求,我们再次妥协(PS:这也是过渡期,不是最终状态)。大家不要轻易模仿,如果条件允许,还是新起一个页面吧,心情会舒畅很多哦。

    2. 产品需要修改老代码里的独立组件。

    解决方法:如果这个组件不是特别复杂,我们会以“夹带私货”的方式重构上线,这样还能顺便让测试童鞋帮忙验一下重构后有没有bug。具体实现参考第一种情况。

    3. 产品需要修改整站的公共部分。

    我们的网站包含好几个页面,此次重构的单页应用只是其中之一。它们共用了顶部导航栏。在这些页面模板中通过Smarty的include语法加载:

    JavaScript

    {%include file="topPanel.inc"%}

    1
    {%include file="topPanel.inc"%}

    产品在一次界面改版中提出要给导航栏加上一些功能的快捷入口,比如导入文件,购买套餐等。而这些功能在单页应用中已经用vue实现了。所以还得将导航栏实现为vue组件。

    为了更快渲染导航栏,需要保留它原有的标签,而不是在JS里以组件的形式渲染。所以需要用到特殊手段:

    • 在topPanel.inc里写上自定义标签,对应到vue组件,比如下面代码里的``。当JS未加载时,会立即渲染导航栏的常规标签以及自定义标签。

    <div id="topPanelMountee"> <div id="topPanel"> <div>一些页面直出的内容</div> ... <import-button> <button class="btn-import"> 导入 </button> </import-button> ... </div> </div>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <div id="topPanelMountee">
      <div id="topPanel">
          <div>一些页面直出的内容</div>
          ...
          <import-button>
            <button class="btn-import">
              导入
            </button>
          </import-button>
          ...
      </div>
    </div>
    • 导航栏组件:topPanel.js,它包含了ImportButton等子组件(对应上面的<import-button>)。等JS加载后,ImportButton组件就会挂载到<import-button>上并为这个按钮绑定行为。另外,注意下面代码中的template并不是<App />,而是一个ID选择器,这样topPanel组件就会以#topPanelMountee里的内容作为模板挂载到#topPanelMountee元素中,是不是很机智~

    JavaScript

    // topPanel.js new Vue({ el: '#新葡亰496net,topPanelMountee', template: '#topPanelMountee', components: { ... ImportButton } })

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // topPanel.js
    new Vue({
      el: '#topPanelMountee',
      template: '#topPanelMountee',
      components: {
        ...
        ImportButton
      }
    })

    彻底重构后,我们还做了进一步的性能优化。

    传统的网页应用

    新的框架层出不穷:它难吗?它写得快吗?可维护性怎样?运行性能如何?社区如何?前景怎样?好就业吗?好招人吗?组建团队容易吗?不管哪个,选择一个实践就好。

    开发框架

    ExtJS可以称为第一代单页应用框架的典型,它封装了各种UI组件,用户主要使用JavaScript来完成整个前端部分,甚至包括布局。随着功能逐渐增加,ExtJS的体积也逐渐增大,即使用于内部系统的开发,有时候也显得笨重了,更不用说开发以上这类运行在互联网上的系统。

    jQuery由于偏重DOM操作,它的插件体系又比较松散,所以比ExtJS这个体系更适合开发在公网运行的单页系统,整个解决方案会相对比较轻量、灵活。

    但由于jQuery主要面向上层操作,它对代码的组织是缺乏约束的。如何在代码急剧膨胀的情况下控制每个模块的内聚性,并且适当在模块之间产生数据传递与共享,就成为了一种有挑战的事情。

    为了解决单页应用规模增大时候的代码逻辑问题,出现了不少MV*框架,他们的基本思路都是在JS层创建模块分层和通信机制。有的是MVC,有的是MVP,有的是MVVM,而且,它们几乎都在这些模式上产生了变异,以适应前端开发的特点。

    这类框架包括Backbone,Knockout,AngularJS,Avalon等。

    安装

    • 目前最稳定2.5.16版本/直接下载并用 <script> 标签引入,Vue 会被注册为一个全局变量。
      //感觉和之前学的jQuery一样,利用命名空间把所有东西封装进一个全局变量里面,源码应该return一个对象出来让我们使用,直接Vue.XXXX
    • 在开发环境下不要使用压缩版本,不然你就失去了所有常见错误相关的警告!
    • Vue有两个版本下载,开发和生产
    • Vue 提供了配套工具来开发单文件组件
    • Vue 提供一个官方命令行工具,(cli)
      //可快速搭建大型单页应用。
      //该工具为现代化的前端开发工作流提供了开箱即用的构建配置。
      //只需几分钟即可创建并启动一个带热重载、保存时静态检查以及可用于生产环境的构建配置的项目
      //vue与Cli的关系应该和git 与gitbash的关系类似
    # 全局安装 vue-cli
    $ npm install --global vue-cli
    # 创建一个基于 webpack 模板的新项目
    $ vue init webpack my-project
    # 安装依赖,走你
    $ cd my-project
    $ npm run dev
    
    • 官方说通读vue指南文档之后再用cli
    • 不同的构建版本应该就是不同版本的vue的意思....
      //也就是说vue团队不断优化自己的代码,可能会导致个别API的使用方法改变

    进一步优化

    单页应用

    一、Angular 基础

    组件化

    这些在前端做分层的框架推动了代码的组件化,所谓组件化,在传统的Web产品中,更多的指UI组件,但其实组件是一个广泛概念,传统Web产品中UI组件占比高的原因是它的厚度不足,随着客户端代码比例的增加,相当一部分的业务逻辑也前端化,由此催生了很多非界面型组件的出现。

    分层带来的一个优势是,每层的职责更专一了,由此,可以对其作单元测试的覆盖,以保证其质量。传统UI层测试最头疼的问题是UI层和逻辑混杂在一起,比如往往会在远程请求的回调中更改DOM,当引入分层之后,这些东西都可以分别被测试,然后再通过场景测试来保证整体流程。

    Vue术语

    • 完整版
      //Vue的完整版本,包括编译器和运行时的版本
    • 编译器:用来将模板字符串编译成为 JavaScript 渲染函数的代码。
      //编译器是代码,是将模板字符串编译成JavaScript渲染函数的代码,因为vue是写在html 和 JavaScript上面的,vue的语法是不符合JavaScript和html语法的,所以需要编译器来把Vue代码翻译成符合html,JavaScript代码,这样浏览器才能理解,并且操作页面
    • 运行时:用来创建 Vue 实例、渲染并处理虚拟 DOM 等的代码。基本上就是除去编译器的其它一切。
      //就是区别于编译器的Vue代码,因为编译器是内置在vue这个框架代码内的,用户是不用操作的,用户操作的vue代码都是运行时
      //简单来说,vue代码就是运行时,编译器就是翻译vue代码的用JavaScript写的一个库(一般来说是函数)
    • UMD(vue的UMD版本)
      UMD 版本可以通过 <script> 标签直接用在浏览器中。jsDelivr CDN 的https://cdn.jsdelivr.net/npm/vue 默认文件就是运行时 编译器的 UMD 版本 (vue.js)。
      //也就是说UMD有完整版和不完整版,他的特点在于可以直接通过script标签引入
    • 除了UMD还有commonJS,ESmodule 一共三个版本的vue,前者兼容webpack1.0后者兼容webpack2.0

    1. HTML瘦身

    在采用组件化开发之前,HTML中预置了许多标签元素,比如:

    JavaScript

    <button data-cn="del" class="del">删除</button> <button data-cn="rename" class="rename">重命名</button> ...

    1
    2
    3
    <button data-cn="del" class="del">删除</button>
    <button data-cn="rename" class="rename">重命名</button>
    ...

    当状态改变时,通过JS操作DOM来控制预置标签的内容或显示隐藏状态。这种做法不仅让HTML很臃肿,JS跟DOM的紧耦合也让人头大。改成组件化开发后,将这些元素统统删掉。

    之前还使用了很多全局变量存放服务端输出的数据。比如:

    <script> var SYS_CONF = { userName: {%$userInfo.name%} ... } </script>

    1
    2
    3
    4
    5
    6
    <script>
        var SYS_CONF = {
            userName: {%$userInfo.name%}
            ...
        }
    </script>

    随着时间的推移,这些全局变量越来越多,管理起来很费劲。还有一些已经废弃的变量,对HTML的体积做出了“贡献”。所以重构时只保留了必需的变量。更多数据则在运行时加载。

    另外,在没有模板字面量的年代,HTML里大量使用了script标签存放运行时所需的模板元素。比如:

    <script type="text/template" id="sharePanel"> <div class="share"> ... </div> </script>

    1
    2
    3
    4
    5
    <script type="text/template" id="sharePanel">
        <div class="share">
            ...
        </div>
    </script>

    虽然上线时会把这些标签内的字符串提取成JS变量,以减小HTML的体积,但在开发时,这些script标签会增加代码阅读的难度,因为要不停地切换HTML和JS目录查找。所以重构后删掉了大量的<script>标签,使用vue的<template>以及ES6的模板字面量来管理模板字符串。

    单页应用的优缺点

    每种技术都有其利弊,单页应用也是如此。

    • 无刷新体验,这个应该是最显著的有点,由于路由分发直接在浏览器端完成,页面是不刷新,对用户的响应非常及时,因此提升了用户体验;
    • 完全的前端组件化,前端开发不再以页面为单位,更多地采用组件化的思想,代码结构和组织方式更加规范化,便于修改和调整;
    • API 共享,如果你的服务是多端的(浏览器端、Android、iOS、微信等),单页应用的模式便于你在多个端共用 API,可以显著减少服务端的工作量。容易变化的 UI 部分都已经前置到了多端,只受到业务数据模型影响的 API,更容易稳定下来,便于提供鲁棒的服务;
    • 组件共享,在某些对性能体验要求不高的场景,或者产品处于快速试错阶段,借助于一些技术(Hybrid、React Native),可以在多端共享组件,便于产品的快速迭代,节约资源。

    缺点:

    • 首次加载大量资源,要在一个页面上为用户提供产品的所有功能,在这个页面加载的时候,首先要加载大量的静态资源,这个加载时间相对比较长;
    • 较高的前端开发门槛,MVC 前置,对前端工程师的要求提高了,不再是『切切图,画画页面这么简单』;同时工作量也会增加数倍,开发这类应用前端工程师的数量往往多于后端;
    • 不利于 SEO,单页页面,数据在前端渲染,就意味着没有 SEO,或者需要使用变通的方案。

    1、AngularJS核心

    AngularJS 通过 指令 扩展了 HTML,且通过 表达式 绑定数据到 HTML。AngularJS核心是:MVVM、模块化、自动化双向数据绑定、语义化标签、依赖注入等等。

    AngularJS更关注数据展示本身,更新时会尽可能减少对DOM的破坏和重构。
    注:jQuery是dom驱动,AngularJS是数据驱动。

    代码隔离

    与开发传统页面型网站相比,实现单页应用的过程中,有一些比较值得特别关注的点。

    从单页应用的特点来看,它比页面型网站更加依赖于JavaScript,而由于页面的单页化,各种子功能的JavaScript代码聚集到了同一个作用域,所以代码的隔离、模块化变得很重要。

    在单页应用中,页面模板的使用是很普遍的。很多框架内置了特定的模板,也有的框架需要引入第三方的模板。这种模板是界面片段,我们可以把它们类比成JavaScript模块,它们是另一种类型的组件。

    模板也一样有隔离的需要。不隔离模板,会造成什么问题呢?模板间的冲突主要存在于id属性上,如果一个模板中包含固定的id,当它被批量渲染的时候,会造成同一个页面的作用域中出现多个相同id的元素,产生不可预测的后果。因此,我们需要在模板中避免使用id,如果有对DOM的访问需求,应当通过其他选择器来完成。如果一个单页应用的组件化程度非常高,很可能整个应用中都没有元素id的使用。

    纠正上面的一些理解

    // 需要编译器
    new Vue({
      template: '<div>{{ hi }}</div>'
    })
    
    // 不需要编译器
    new Vue({
      render (h) {
        return h('div', this.hi)
      }
    })
    
    • vue有组件,运用组件要把定义标签写在html上面,那么就需要编译器了,因为html页面不认识JavaScript代码啊,因为vue是用JavaScript写的嘛
    • 运行时,就是指的是不涉及html页面上的vue代码,都叫运行时,因为不涉及html就不需要编译啊...
    • 因为浏览器只认识.js.css这些后缀的文件,所以你用到vue也是要编译成JavaScript代码浏览器才能理解,才能渲染页面,webpack这种打包工具就是将各种前端开发中遇到的难题打包处理的工具,不要太爽啊

    2. 渐进渲染

    首屏想要更快渲染,还要确保文档加载的CSS和JS尽量少,因为它们会阻塞文档加载。所以我们尽可能延迟加载非关键组件。比如:

    • 延迟非默认路由

    单页应用有很多路由组件。所以除了默认跳转的路由组件,将非默认路由组件打包成单独的chunk。使用import()的方式动态加载。只有命中该路由时,才加载组件。比如:

    JavaScript

    const AsyncComp = () => import(/* webpackChunkName: "AsyncCompName" */ 'AsyncComp.vue') const routes = [{ path: '/some/path', meta: { type: 'sharelink', isValid: true, listKey: 'sharelink' }, component: AsyncComp }] ...

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const AsyncComp = () => import(/* webpackChunkName: "AsyncCompName" */ 'AsyncComp.vue')
    const routes = [{
      path: '/some/path',
      meta: {
        type: 'sharelink',
        isValid: true,
        listKey: 'sharelink'
      },
      component: AsyncComp
    }]
    ...
    • 延迟不重要的展示型组件

    这些组件其实可以延迟到主要内容渲染完毕再加载。将这些组件单独打包为一个chunk。比如:

    JavaScript

    import(/* webpackChunkName: "lazy_load" */ 'a.js') import(/* webpackChunkName: "lazy_load" */ 'b.js')

    1
    2
    import(/* webpackChunkName: "lazy_load" */ 'a.js')
    import(/* webpackChunkName: "lazy_load" */ 'b.js')
    • 延迟低频的功能

    如果某些功能属于低频操作,或者不是所有用户都需要。则可以选择延迟到需要的时候再加载。比如:

    JavaScript

    async handler () { await const {someFunc} = import('someFuncModule') someFunc() }

    1
    2
    3
    4
    async handler () {
      await const {someFunc} = import('someFuncModule')
      someFunc()
    }

    使用vue-router构建单页应用

    vue-router.js是Vue.js官方的路由插件用于构建单页面应用。
    vue的单页应用是基于路由和组件的。传统的页面应用,是用一些超链接来实现页面切换和跳转的。在vue-router单面应用中,则是路径之间的切换,也就是组件的切换。

    先来看一下官方提供的最简单的例子:示例
    HTML

    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
    
    <div id="app">
        <h1>Hello App!</h1>
        <p>
            <!-- 使用 router-link 组件来导航. -->
            <!-- 通过传入 `to` 属性指定链接. -->
            <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
            <router-link to="/foo">Go to Foo</router-link>
            <router-link to="/bar">Go to Bar</router-link>
        </p>
        <!-- 路由出口 -->
        <!-- 路由匹配到的组件将渲染在这里 -->
        <router-view></router-view>
    </div>
    
    • router-link标签:跳转的链接,to=""是必须的属性,双引号中的内容是我们接下来在JS文件中定义的路由path。
    • router-view标签:展示我们匹配到的组件的区域。

    JavaScript

    // 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)
    
    // 1. 定义(路由)组件。
    // 也可以从其他文件 import 进来
    const Foo = { template: '<div>foo</div>' }
    const Bar = { template: '<div>bar</div>' }
    
    // 2. 定义路由
    // 每个路由应该映射一个组件。 其中"component" 可以是
    // 通过 Vue.extend() 创建的组件构造器,
    // 或者,只是一个组件配置对象。
    const routes = [
        { path: '/foo', component: Foo },
        { path: '/bar', component: Bar }
    ]
    
    // 3. 创建 router 实例,然后传 `routes` 配置
    // 你还可以传别的配置参数, 不过先这么简单着吧。
    const router = new VueRouter({
        routes // (缩写)相当于 routes: routes
    })
    
    // 4. 创建和挂载根实例。
    // 记得要通过 router 配置参数注入路由,
    // 从而让整个应用都有路由功能
    const app = new Vue({
        router
    }).$mount('#app')
    
    // 现在,应用已经启动了!
    

    JavaScript文件主要做的事情是:

    • 定义路由列表,即routes。
    • 创建router实例及router配置,即router。
    • 创建和挂载根实例。

    以上只是教我们用最简单的方法使用vue-router。但实际开发过程中,首先我们的vue组件显然不会只有一个template模板这么简单,会用到vue的单文件组件;
    其次我们通常会希望<router-view>的范围是整个页面,而不是像现在这样一直有几个碍眼的导航存在于页面上,这就需要先定义好默认状态下<router-view>显示的内容。

    既然是单页应用(SPA),那么整个项目有以下三个文件是必要的:

    • 一个html文件:index.html
    • 一个webpack打包时的入口js文件:main.js
    • 一个根vue组件,作为其他组件的挂载点:app.vue

    接下来 我们就创建两个自定义组件:index.vue和hello.vue。我们希望的结果是他们之间互相跳转。
    我们利用官方提供的脚手架vue-cli工具生成简单的一个基于webpack打包的vue项目
    准备工作:

    npm install webpack -g
    npm install vue-cli -g
    //打开要创建的项目路径目录,创建项目
    vue init webpack-simple <项目名>
    cd <项目名>
    //安装依赖
    npm install
    //安装vue-router 
    npm install vue-router --save
    npm run dev
    

    生成的vue项目如下图:

    2、AngularJS的MVVM模式

    angular中关于MVVM模式的运用,Model-View-ViewMode(模型-视图-视图模型)

    新葡亰496net 2

    MVVM模式

    在angular中MVVM模式主要分为四部分:

    1)View:它专注于界面的显示和渲染,在angular中则是包含一堆声明式Directive的视图模板。
    2)ViewModel:它是View和Model的粘合体,负责View和Model的交互和协作。
    3)Model:它是与应用程序的业务逻辑相关的数据的封装载体。
    4)Controller:这并不是MVVM模式的核心元素,但它负责ViewModel对象的初始化。

    代码合并与加载策略

    人们对于单页系统的加载时间容忍度与Web页面不同,如果说他们愿意为购物页面的加载等待3秒,有可能会愿意为单页应用的首次加载等待5-10秒,但在此之后,各种功能的使用应当都比较流畅,所有子功能页面尽量要在1-2秒时间内切换成功,否则他们就会感觉这个系统很慢。

    从这些特点来看,我们可以把更多的公共功能放到首次加载,以减小每次加载的载入量,有一些站点甚至把所有的界面和逻辑全部放到首页加载,每次业务界面切换的时候,只产生数据请求,因此它的响应是非常迅速的,比如青云的控制台就是这么做的。

    通常在单页应用中,无需像网站型产品一样,为了防止文件加载阻塞渲染,把js放到html后面加载,因为它的界面基本都是动态生成的。

    当切换功能的时候,除了产生数据请求,还需要渲染界面,这个新渲染的界面部件一般是界面模板,它从哪里来呢?来源无非是两种,一种是即时请求,像请求数据那样通过AJAX获取过来,另一种是内置于主界面的某些位置,比如script标签或者不可见的textarea中,后者在切换功能的时候速度有优势,但是加重了主页面的负担。

    在传统的页面型网站中,页面之间是互相隔离的,因此,如果在页面间存在可复用的代码,一般是提取成单独的文件,并且可能会需要按照每个页面的需求去进行合并。单页应用中,如果总的代码量不大,可以整体打包一次在首页载入,如果大到一定规模,再作运行时加载,加载的粒度可以搞得比较大,不同的块之间没有重复部分。

    vue介绍

    • 渐进式代表的含义是:也就是主张少,只关注视图层,便于与第三方库或既有项目整合。https://www.zhihu.com/question/51907207
      - 观看官方视频的时候有些疑惑
      1.它说这几年页面的代码大量地从后端搬到了前端,前端代码也是服务器响应给浏览器的啊!!!肯定也是保存在后端服务器上啊...奇怪了怎么说搬到前端?难道是说渲染页面的工作由后端转移到了前端的意思?

    3. 优化图片

    虽然代码做了很多优化,但是动辄几十到几百KB的图片瞬间碾压了辛苦重构带来的提升。所以图片的优化也是至关重要滴~

    1. PNG改成SVG

    由于项目曾经支持IE6-8,大量使用了PNG,JPEG等格式的图片。随着历史的车轮滚滚向前,IE6-8的用户占比已经大大降低,我们在去年放弃了对IE8-的支持。这样一来就能采取更优的解决方案啦~

    我们的页面上有各种大小的图标和不同种类的占位图。原先使用位图并不能很好的适配retina显示器。现在改成SVG,只需要一套图片即可。相比PNG,SVG有以下优点:

    1. 压缩后体积小
    2. 无限缩放,不失真
    3. retina显示器上清晰

    2. 进一步“压榨”SVG

    虽然换成SVG,但是还远远不够,使用webpack的loader可以有效地压缩SVG体积。

    • 用svgo-loader去除无用属性

    SVG本身既是文本也是图片。设计师提供的SVG大多有冗余的内容,比如一些无用的defstitle等,删除后并不会降低图片质量,还能减小图片体积。

    我们使用svgo-loader对SVG做了一些优化,比如去掉无用属性,去掉空格换行等。这里就不细数它能提供的优化项目。大家可以对照svgo-loader的选项配置。

    • 用svg-sprite-loader合并多个SVG

    另外,SVG有多种用法,比如:img,background,inline,inline  <use>。如果某些图反复出现并且对页面渲染很关键,可以使用svg-sprite-loader将多个图合并成一个大的SVG,避免逐个发起图片请求。然后使用内联或者JS加载的方式将这个SVG引入页面,然后在需要的地方使用SVG的<use>标签引用该图标。合并后的大SVG如下图:

    新葡亰496net 3

    使用时:

    <svg> <use xlink:href="#icon-add"></use> </svg>

    1
    2
    3
    <svg>
      <use xlink:href="#icon-add"></use>
    </svg>

    即可在使用的位置展现该图标。

    以上是一些优化手段,下面给大家分享一下重构后的收益。

    一、使用路由

    1. 首先在目录下创建components文件夹,然后再创建index.vuehello.vue文件
    //index.vue
    <template>
        <div>
            <h2>Index</h2>
            <hr>
            <p>{{sContent}}</p>
        </div>
    </template>
    <script>
        export default{
            data(){
                return {
                    sContent:"This is index components"
                }
            }
        }
    </script>
    
    //hello.vue
    <template>
        <div>
            <h2>Hello Vue.js</h2>
            <hr/>
            <p>{{sContent}}</p>
        </div>
    </template>
    <script>
        export default{
            data(){
                return {
                    sContent:"This is hello components"
                }
            }
        }
    </script>
    
    1. 修改main.js文件
    //引入并安装vue-router插件
    import Vue from 'vue';
    import VueRouter from 'vue-router';
    Vue.use(VueRouter);
    //引入index.vue和hello.vue组件
    import App from './App.vue';
    import index from './components/index.vue';
    import hello from './components/hello.vue';
    //定义路由
    const routes = [
        {path:'/',component:App},
        { path: '/index', component: index },
        { path: '/hello', component: hello }
    ]
    //创建 router 实例,然后传 routes 配置
    const router=new VueRouter({
      routes
    });
    //创建和挂载根实例。通过 router 配置参数注入路由,从而让整个应用都有路由功能
    new Vue({
      el:"#app",
      router
    });
    
    1. 修改App.vue
    <template>
      <div>
        ![](./assets/logo.png)
        <h1>{{msg}}</h1>
        <ul>
          <router-link to='/index' tag='li'><a href="/index">Index</a></router-link>
          <router-link to='/hello' tag='li'><a href="/hello">Hello</a></router-link>
        </ul>
      </div>
    </template>
    
    1. 修改index.html
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <title>vue-webpack-simple</title>
      </head>
      <body>
        <div id="app">
            <router-view></router-view>
        </div>
        <script src="/dist/build.js"></script>
      </body>
    </html>
    

    这样就会把渲染出来的页面挂载到这个id为app的div里了。

    修改后运行的效果如下:

    3、Angularjs执行流程:

    新葡亰496net 4

    路由与状态的管理

    我们最开始看到的几个在线应用,有的是对路由作了管理的,有的没有。

    管理路由的目的是什么呢?是为了能减少用户的导航成本。比如说我们有一个功能,经历过多次导航菜单的点击,才呈现出来。如果用户想要把这个功能地址分享给别人,他怎么才能做到呢?

    传统的页面型产品是不存在这个问题的,因为它就是以页面为单位的,也有的时候,服务端路由处理了这一切。但是在单页应用中,这成为了问题,因为我们只有一个页面,界面上的各种功能区块是动态生成的。所以我们要通过对路由的管理,来实现这样的功能。

    具体的做法就是把产品功能划分为若干状态,每个状态映射到相应的路由,然后通过pushState这样的机制,动态解析路由,使之与功能界面匹配。

    有了路由之后,我们的单页面产品就可以前进后退,就像是在不同页面之间一样。

    其实在Web产品之外,早就有了管理路由的技术方案,Adobe Flex中,就会把比如TabNavigator,甚至下拉框的选中状态对应到url上,因为它也是单“页面”的产品模式,需要面对同样的问题。

    当产品状态复杂到一定程度的时候,路由又变得很难应用了,因为状态的管理极其麻烦,比如开始的时候我们演示的c9.io在线IDE,它就没法把状态对应到url上。

    vue介绍视频

    • 视频里说,因为前端的发展,大量JavaScript代码由后端搬到了前端(估计是在浏览器运行JavaScript代码的意思),可是一个大项目JavaScript代码成千上万啊,所以会很乱,框架就是整理这些代码,管理代码的工具
      //这也印证了前端代码这几年的模块化之路
    • 视频里面说:如果你有一个现成的服务端应用,可以把vue当做应用中的一部分嵌入其中,带来更丰富的交互体验
    • vue也可以帮你把更多的业务逻辑代码放到前端来,如果用了vue生态的话
    • vue允许你将一个页面分割成多个可以复用的组件,每个组件都包含了自身的html/css/JavaScript来渲染页面响应的地府
      //父组件APP因为足够大,所以能渲染整个页面,子组件和孙组件比较小,可以引入APP中,供APP使用,积小成多最后撑起整个页面,整个页面都是VUE组件渲染的了
    • vue的特点:
      //响应式双向绑定,在vue实例中的data数据,只要更改都会自动刷新,因为双向绑定,这是vue来做的,换言之再也不用操作jQuery了
      //当然data数据,不仅仅是显示字符串那么简单,也可以是复杂的类型,前提是你要在vue实例里面的data属性,那里先声明注册
      //貌似data必须是数组或者对象或者字符串,不能是函数
      //其中还可以用create这个API引入外部数据库的数据
    create(){
      fetch('https://XXXX')
          .then(response=>response.json())
          .then(json=>{
        this.products = json.products
      })
    }
    
    • 构建工具
      //构建工具,我往往会在前面加「自动化」三个字,因为构建工具就是用来让我们不再做机械重复的事情,解放我们的双手的。
    • 安装教程给出了更多安装 Vue 的方式。请注意我们不推荐新手直接使用 vue-cli,尤其是在你还不熟悉基于 Node.js 的构建工具时。
      //这是官方原话

    重构的收益

    以下是重构带来的收益:

    收益项 重构前 重构后
    组件化 100%
    模块化 50% 100%
    规范化 ESLint 代码规范检查
    语法 ES5 ES6
    首屏有效渲染时间 1.59s 1.28s(提升19%)
    首次交互时间 2.56s 1.54s(提升39%)
    • 组件化:从0到100%老代码没有组件的概念,都是指令式的编程模式以及对DOM的直接操作。重构后,改为组件化以后,可以充分利用组件的高复用性,以及虚拟DOM的性能优化,带来更愉悦的开发体验。
    • 模块化:从50%到100%老代码中也用RequireJS做了一定程度的模块化,但是仅限于业务模块,没有解决第三方依赖的安装和升级问题。重构后,借助webpack和npm,只需要npm install安装第三方依赖,然后用import的方式加载。极大地提高了开发效率。
    • 规范化:从0到1老代码几乎没有代码规范,甚至连同一份文件里都有不同的代码缩进,强迫症根本无法忍受。重构后,使用ESLint对代码格式进行了统一,代码看起来更加赏心悦目。
    • ES6 语法:从0到大量使用老代码所使用的库因为历史悠久,加上没有引入转译流程,只能使用ES5语法。重构后,能够尽情使用箭头函数、解构、async/await等语言新特性来简化代码,从而提升开发体验。
    • 性能提升根据上线前后Lighthouse的性能检测数据,首次有效渲染时间(First Meaningful Paint,FMP)提升 19% 。该指标表示用户看到有用信息的时间(比如文件列表)。首次交互(First Interactive,FI)提升 39%。该指标表示用户可以开始跟网页进行交互的时间 。

    以上就是这次重构的总结。不要容忍代码里的坏味道,更不要容忍低效的开发模式。及时发现,勇敢改进吧~

    二、重定向 redirect

    const routes = [
        { path: '/', redirect: '/index'},     // 这样进/ 就会跳转到/index
        { path: '/index', component: index }
    ]
    

    4、单页面应用(使用ui-rounter)

    单页应用是指在浏览器中运行的应用,它们在使用期间不会重新加载页面。单页应用是一种从Web服务器加载的富客户端。

    单页Web应用,顾名思义,就是只有一张Web页面的应用。浏览器一开始会加载必需的HTML、CSS和JavaScript,之后所有的操作都在这张页面上完成,这一切都由JavaScript来控制。因此,单页Web应用会包含大量的JavaScript代码,复杂度可想而知,模块化开发和设计的重要性不言而喻。


    二、angular UI-Router路由

    通过 AngularJS 可以实现多视图的单页Web应用。

    AngularJS 路由 就通过 # 标记 帮助我们区分不同的逻辑页面并将不同的页面绑定到对应的控制器上。

    新葡亰496net 5

    路由流程图

    UI-Router被认为是AngularUI为开发者提供的最实用的一个模块。开发者可以创建嵌套分层的视图、在同一个页面使用多个视图、让多个视图控制某个视图等更多的功能。即使是非常复杂的web应用,UI-Router也可以极佳地驾驭。

    1、ui-sref 指令链接到特定状态
    <a ui-sref="contacts.list">Contacts</a>

    2、包含模块
    angular.module('uiRouter', ['ui.router']);

    3、ui-sref-active 查看当前激活状态并设置 Class
    <li ui-sref-active="active"><a ui-sref="about">About</a></li>

    4、ng-view
    该 div 内的 HTML 内容会根据路由的变化而变化。
    <div ui-view></div>  嵌套 View

    5、方便获得当前状态的方法,绑到根作用域
    app.run(['$rootScope', '$state', '$stateParams',
      function($rootScope, $state, $stateParams) {
      $rootScope.$state = $state;
      $rootScope.$stateParams = $stateParams;
    }]);

    6、abstract 抽象模板(虚拟路由abstract:true先执行user, 再进入controller)
    抽象模板不能被激活,但是它的子模板可以被激活。抽象模板可以提供一个包括了多个有名的视图的模板,或者它可以传递作用域变量$scope给子模板。

    7、路由重定向 $urlRouterProvider


    三、angular基础常见问题

    1、angular 的数据绑定采用什么机制?详述原理

    脏检查机制。

    双向数据绑定是 AngularJS 的核心机制之一。当 view 中有任何数据变化时,会更新到 model ,当 model 中数据有变化时,view 也会同步更新,显然,这需要一个监控。

    原理就是,Angular 在 scope 模型上设置了一个 监听队列,用来监听数据变化并更新 view 。每次绑定一个东西到 view 上时 AngularJS 就会往$watch队列里插入一条$watch,用来检测它监视的 model 里是否有变化的东西。当浏览器接收到可以被 angular context 处理的事件时,$digest循环就会触发,遍历所有的$watch,最后更新 dom。

    2、angularjs ng-if ng-show ng-hide区别

    在使用anularjs开发前端页面时,常常使用ng-show、ng-hide、ng-if功能来控制页面元素的显示或隐藏

    1、操作dom元素不同:
    1)ng-show/ng-hide是通过修改样式控制元素显示与隐藏,对应的DOM元素会一直存在于当前页面中。

    2)ng-if根据表达式的值动态的在当前的页面中添加删除页面元素。

    2、在作用域方面
    1)当一个元素被ng-if从DOM中删除时,与其关联的作用域也会被销毁。而且当它重新加入DOM中时,则会生成一个新的作用域,而ng-show和ng-hide则不会。

    注:
    ng-hide="true" 设置 HTML 元素不可见。
    ng-hide="false" 设置 HTML 元素可见。

    3、angularjs scope rootscope 区别(重要)

    scope是html和单个controller之间的桥梁,数据绑定就靠他了。
    rootscope是各个controller中scope的桥梁。

    如何产生$rootScope和$scope?
    1、Angular解析ng-app然后在内存中创建$rootScope。
    2、Angular会继续解析,找到{{}}表达式,并解析成变量。
    3、接着会解析带有ng-controller的div然后指向到某个controller函数。这个时候在这个controller函数变成一个$scope对象实例。
    例如:$scope.addServe = function () {}

    4、angular 应用路由库及区别

    1、Angular1.x 中常用 ngRoute 和 ui.router
    2、无论是 ngRoute(Angular 自带的路由模块) 还是 ui.router(第三方模块),都必须以 模块依赖 的形式被引入。
    3、ui.router 是基于 state (状态)的, ngRoute 是基于 url 的,ui.router模块具有更强大的功能,主要体现在视图的嵌套方面。(虚假路由)

    5、ng-include 和 ng-view区别:

    ng-include 指令用于包含外部的 HTML 文件
    <div ng-include="'views/apply_bl.html'"></div>
    使用ng-view.这个方法通过使用路由控制,可以方便的实现页面组合。
    ng-include就是将多个页面的公共页面提取出来,如header.html,footer.html等,在每个页面用来引入。


    四、现项目运用技术点

    一、A项目 (开发模式:前端 后端)

    1、angular 1.4.8 版本
    2、组件化:1)插件    2)自定义指令 directive 来做组件化
    3、Angular ocLazyLoad动态化加载脚本
    4、Angular-UI-Router 虚拟路由  [(homeconsole)虚拟路由来复用的]
    5、requirejs异步加载
    6、UI-bootstrap组件    bootstrap( url地址改变)
    7、restfulAPI 协议写相应接口
    8、golang、java语言开发

    二、B项目 (开发模式:前端 node)

    1、Yeoman Angular Gulp 环境搭建
    2、angular v1.5.11 版本
    3、Angular-UI-Router 虚拟路由  [(homeconsole)虚拟路由来复用的]
    4、gulpfile  主要
         1、babel  es6转es5
         2、gulp-uglify  JS文档压缩
         3、gulp-sass  编译sass文件至css文件
         4、……
    5、node做服务器执行过程:
          一、前端。取接口,展示数据
          二、后端。(接口协议要遵循 RESTful API )
                (1)node的框架Express。
                  node作用:
                  1)当服务器
                  2)接口设置
                  3)在node中,通过Sequelize操作mysql数据库之增删改查。
    三、mysql(数据库)
    数据库,设置具体数据。

    缓存与本地存储

    在单页应用的运作机制中,缓存是一个很重要的环节。

    由于这类系统的前端部分几乎全是静态文件,所以它能够有机会利用浏览器的缓存机制,而比如动态加载的界面模板,也完全可以做一些自定义的缓存机制,在非首次的请求中直接取缓存的版本,以加快加载速度。

    甚至,也出现了一些方案,在动态加载JavaScript代码的同时,把它们也缓存起来。比如Addy Osmani的这个basket.js,就利用了HTML5 localStorage作了js和css文件的缓存。

    在单页产品中,业务代码也常常会需要跟本地存储打交道,存储一些临时数据,可以使用localStorage或者localStorageDB来简化自己的业务代码。

    Vue介绍

    • 声明式渲染
      //vue.js的核心是一个系统,一个将数据渲染进dom,的系统(可以用模板语法来声明式操作)
      //dom是一个JavaScript对象,一个根据html结构产生了相应节点(可操作的JavaScript对象)的对象,这个对象封装了很多可以操作这些节点对象的函数(方法),可以说页面的展示离不开dom,dom可以操作html,可以修改html节点,文本,样式,所以Vue要把页面的数据交给dom,由dom来渲染页面
      //不仅如此,Vue还做到了极致,因为DOM是根据DOM渲染树来渲染页面的,而页面的代码加载是有顺序的,而且加载到哪里,页面就渲染到哪里;Vue不是直接交数据给DOM,而是先交给虚拟DOM,让虚拟DOM计算最终样式,才把最终样式交给真的DOM来渲染页面
      //所以Dom有文本和特性这些概念,dom文本实际上就是某个标签里面的文本内容,dom特性其实就是某个节点的特性,或者说是某个html标签的特性
      //没有vue的时候,我们用jQuery来做特效,需要用命令式的代码来操作DOM;现vue帮你操作dom,不用亲自写dom相关的代码,但是vue又不会干预你想亲自操作dom,这就是Vue声明式代码的好处
    • v-for 指令可以绑定数组的数据来渲染
      //很明显,用v-for的数据必须是一个数组,而且还可以亲自操作 app4.todos.push({ text: '新项目' }),在列表最后添加了一个新项目。
    • 模板是将一个事物的结构规律予以固定化、标准化的成果,它体现的是结构形式的标准化,简单来说就是一种格式
    • Vue应用的意思就是一个页面,对应SPA单页面应用,也就是说一个页面就是一个应用
    • 注意只有v-Module的数据才是双向绑定,其他只算是响应式,这里面是有区别的

    参考

    Chrome 中的 First Meaningful Paint

    Using SVG

    Modern JavaScript Explained For Dinosaurs

    1 赞 收藏 评论

    新葡亰496net 6

    三、嵌套路由

    const routes = [
        { path: '/index', component: index,
            children: [
                { path: 'info', component: {template:'<p>This is info component</p>'}}
            ]
         }
    ]
    

    服务端通信

    传统的Web产品通常使用JSONP或者AJAX这样的方式与服务端通信,但在单页Web应用中,有很大一部分采用WebSocket这样的实时通讯方式。

    WebSocket与传统基于HTTP的通信机制相比,有很大的优势。它可以让服务端很便利地使用反向推送,前端只响应确实产生业务数据的事件,减少一遍又一遍无意义的AJAX轮询。

    由于WebSocket只在比较先进的浏览器上被支持,有一些库提供了在不同浏览器中的兼容方案,比如socket.io,它在不支持WebSocket的浏览器上会降级成使用AJAX或JSONP等方式,对业务代码完全透明、兼容。

    组件化应用构建

    • 组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。
    • 在 Vue 里,一个组件本质上是一个拥有预定义选项的一个 Vue 实例。
      //组件就是一个vue实例,一个拥有预定义选项的vue实例,使用组件必须在vue注册
    • 终于搞清楚了,模板指的是组件模板,和组件相关的vue语法 就是模板语法,不管在html页面里还是JavaScript页面里,只要和组件模板相关的语法都是模板语法
    // 定义名为 todo-item 的新组件
    Vue.component('todo-item', {
      template: '<li>这是个待办项</li>'
    })
    
    现在你可以用它构建另一个组件模板:
    <ol>
      <!-- 创建一个 todo-item 组件的实例 -->
      <todo-item></todo-item>
    </ol>
    

    但是这样会为每个待办项渲染同样的文本,这看起来并不炫酷。我们应该能从父作用域将数据传到子组件才对。让我们来修改一下组件的定义,使之能够接受一个 prop:

    Vue.component('todo-item', {
      // todo-item 组件现在接受一个
      // "prop",类似于一个自定义特性。
      // 这个 prop 名为 todo。
      props: ['todo'],
      template: '<li>{{ todo.text }}</li>'
    })
    

    现在,我们可以使用 v-bind 指令将待办项传到循环输出的每个组件中

    <div id="app-7">
      <ol>
        <!--
          现在我们为每个 todo-item 提供 todo 对象
          todo 对象是变量,即其内容可以是动态的。
          我们也需要为每个组件提供一个“key”,稍后再
          作详细解释。
        -->
        <todo-item
          v-for="item in groceryList"
          v-bind:todo="item"
          v-bind:key="item.id">
        </todo-item>
      </ol>
    </div>
    
    Vue.component('todo-item', {
      props: ['todo'],
      template: '<li>{{ todo.text }}</li>'
    })
    
    var app7 = new Vue({
      el: '#app-7',
      data: {
        groceryList: [
          { id: 0, text: '蔬菜' },
          { id: 1, text: '奶酪' },
          { id: 2, text: '随便其它什么人吃的东西' }
        ]
      }
    })
    

    总结

    • Vue的实例就是开发SPA中的单页面应用,当然也可以是多页面应用,简单来说就是Vue实例绑定一个页面的body,页面变成了一个Vue实例
    • 根据Vue官方视频的说法,webapp是由多个组件组成的,所以Vue实例这个页面应用也有很多组件组成的,用法就是Vue.component来注册,然后在页面上写上组件的模板就行了
    • 其中vue的实例(当前页面app)的data就用来存总数据的,它可以给子组件使用,而子组件(vue.compenent声明的所有子组件都可以用props这个属性来接受父组件传的数据,)
      //也可以说props是子组件的data,只不过这是父亲那边传过来的
    • 最后如果想使用父亲的data 方法是在组件模板中, 用模板指令来建立桥梁 让子组件的props和父组件的data变量产生关联

    四、命名路由

    const routes = [
        { path: '/index', component: index,
          name:'index'
        }
    ]
    

    内存管理

    传统的Web页面一般是不需要考虑内存的管理的,因为用户的停留时间相对少,即使出现内存泄漏,可能很快就被刷新页面之类的操作冲掉了,但单页应用是不同的,它的用户很可能会把它开一整天,因此,我们需要对其中的DOM操作、网络连接等部分格外小心。

    五、<router-link>标签属性

    //to属性 string|object
    <!-- 字符串 -->
    <router-link to="home">Home</router-link>
    <!-- 渲染结果 -->
    <a href="home">Home</a>
    
    <!-- 使用 v-bind 的 JS 表达式 -->
    <router-link v-bind:to="'home'">Home</router-link>
    <!-- 同上 -->
    <router-link :to="{ path: 'home' }">Home</router-link>
    
    <!-- 命名的路由 -->
    <router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
    
    <!-- 带查询参数,下面的结果为 /register?plan=private -->
    <router-link :to="{ path: 'register', query: { plan: 'private' }}">Register</router-link>
    
    //replace属性 true|false 不留下 history 记录。
    <router-link to="home" replace>Home</router-link>
    
    //append属性 true|false 追加路径
    <router-link to="home" append >Home</router-link>
    
    //tag属性 string 设置渲染标签
    <router-link to="/foo" tag="li">foo</router-link>
    <!-- 渲染结果 -->
    <li>foo</li>
    
    //active-class 属性 string 激活时使用的 CSS 类名
    

    样式的规划

    在单页应用中,因为页面的集成度高,所有页面聚集到同一作用域,样式的规划也变得重要了。

    样式规划主要是几个方面:

    五、路由信息对象

    1. $route.path
      字符串,对应当前路由的路径,总是解析为绝对路径,如 "/foo/bar"。
    2. $route.params
      一个 key/value 对象,包含了 动态片段 和 全匹配片段,如果没有路由参数,就是一个空对象。
    3. $route.query
      一个 key/value 对象,表示 URL 查询参数。例如,对于路径 /foo?user=1,则有 $route.query.user == 1,如果没有查询参数,则是个空对象。
    4. $route.hash
      当前路由的 hash 值 (不带 #) ,如果没有 hash 值,则为空字符串。
    5. $route.fullPath
      完成解析后的 URL,包含查询参数和 hash 的完整路径。
    6. $route.matched
      一个数组,包含当前路由的所有嵌套路径片段的 路由记录 。路由记录就是 routes 配置数组中的对象副本(还有在 children 数组)。

    以上遍是vue-router基本使用方式了
    更详细的vue-router功能请参考文档:官方文书档案笔记,angular连串学习笔记。https://router.vuejs.org/zh-cn/

    基准样式的分离

    这里面主要包括浏览器样式的重设、全局字体的设置、布局的基本约定和响应式支持。

    组件样式的划分

    这里面是两个层面的规划,首先是各种界面组件及其子元素的样式,其次是一些修饰样式。组件样式应当尽量减少互相依赖,各组件的样式允许冗余。

    堆叠次序的管理

    传统Web页面的特点是元素多,但是层次少,单页应用会有些不同。

    在单页应用中,需要提前为各种UI组件规划堆叠次序,也就是z-index,比如说,我们可能会有各种弹出对话框,浮动层,它们可能组合成各种堆叠状态。新的对话框的z-index需要比旧的高,才能确保盖在它上面。诸如此类,都需要我们对这些可能的遮盖作规划,那么,怎样去规划呢?

    了解通信知识的人,应当会知道,不同的频率段被划分给不同的通信方式使用,在一些国家,领空的使用也是有划分的,我们也可以用同样的方式来预先分段,不同类型的组件的z-index落到各自的区间,以避免它们的冲突。

    单页应用的产品形态

    我们在开始的时候提到,存在着很多新型Web产品,使用单页应用的方式构建,但实际上,这类产品不仅仅存在于Web上。点开Chrome商店,我们会发现很多离线应用,这些产品都可以算是单页应用的体现。

    除了各种浏览器插件,借助node-webkit这样的外壳平台,我们可以使用Web技术来构建本地应用,产品的主要部分仍然是我们熟悉的单页应用。

    单页应用的流行程度正在逐渐增加,大家如果关注了一些初创型互联网企业,会发现其中很大一部分的产品模式是单页化的。这种模式能带给用户流畅的体验,在开发阶段,对JavaScript技能水平要求较高。

    单页应用开发过程中,前后端是天然分离的,双方以API为分界。前端作为服务的消费者,后端作为服务的提供者。在此模式下,前端将会推动后端的服务化。当后端不再承担模板渲染、输出页面这样工作的情况下,它可以更专注于所提供的API的实现,而在这样的情况下,Web前端与各种移动终端的地位对等,也逐渐使得后端API不必再为每个端作差异化设计了。

    部署模式的改变

    在现在这个时代,我们已经可以看到一种产品的出现了,那就是“无后端”的Web应用。这是一种什么东西呢?基于这种理念,你的产品很可能只需要自己编写静态Web页面,在某种BaaS(Backend as a Service)云平台上定制服务端API和云存储,集成这个平台提供的SDK,通过AJAX等方式与之打交道,实现注册认证、社交、消息推送、实时通信、云存储等功能。

    我们观察一下这种模式,会发现前后端的部署已经完全分离了,前端代码完全静态化,这意味着可以把它们放置到CDN上,访问将大大地加速,而服务端托管在BaaS云上,开发者也不必去关注一些部署方面的繁琐细节。

    假设你是一名创业者,正在做的是一种实时协同的单页产品,可以在云平台上,快速定制后端服务,把绝大部分宝贵的时间花在开发产品本身上。

    单页应用的缺陷

    单页应用最根本的缺陷就是不利于SEO,因为界面的绝大部分都是动态生成的,所以搜索引擎很不容易索引它。

    产品单页化带来的挑战

    一个产品想要单页化,首先是它必须适合单页的形态。其次,在这个过程中,对开发模式会产生一些变更,对开发技能也会有一些要求。

    开发者的JavaScript技能必须过关,同时需要对组件化、设计模式有所认识,他所面对的不再是一个简单的页面,而是一个运行在浏览器环境中的桌面软件。

     

    用JS渲染的单页面应用其实性能还是比较差的

    证明这个结论之前,要先阐述一下浏览器的渲染机制,这里先祭出这篇文章:《关键呈现路径》,文章主要介绍了浏览器渲染过程,其实大家也大概都了解过:

    新葡亰496net 7

    如上图,浏览器通过网络请求加载页面资源,在页面呈现之前无论如何都要经历以下过程:

    1. HTML→DOM
    2. CSS→CSSOM
    3. DOM CSSOM → Render Tree
    4. 对Render Tree进行布局计算(Layout)
    5. 对布局结果进行屏幕绘制(Paint)

    如果在JS渲染页面模式下,需要在前端用JS加载样式并组装数据生成HTML插入页面,以上浏览器渲染过程必须等到页面加载完CSS,并且JS加载完数据拼装好HTML之后才能开始进行,一般的网络时序如下:

    新葡亰496net 8

    大概阐述一下这个流程:

    1. 浏览器发起请求加载主文档
    2. 服务端响应一个基本骨架的主文档
    3. 浏览器加载主文档中外链的loader.js(根据路由控制资源加载的)
    4. 服务端响应loader.js
    5. loader.js执行,根据页面url判断用户访问到哪个虚拟页面,然后再发起请求加载对应页面的js和css
    6. 页面所需JS和CSS都加载完毕,JS执行,发起请求加载数据
    7. 数据加载完毕,JS执行前端模板拼装,插入DOM节点,然后浏览器开始前述渲染过程
    8. 最终页面呈现

    概括一下,加载时序大概是这样的:

     

    新葡亰496net 9

    以上加载过程均为串行,需要至少多付出3次RTT。如果把这种架构应用在高延迟的网络环境下(比如移动2G),那就是找死啊(其实国内现在的网络环境很好了,这样搞问题或许不太明显)。

    当然,上面的例子还是常规了一些,有些请求可以适当合并,进一步优化之后,大概可以搞成这个样子:

    新葡亰496net 10

    就是首次请求的主文档尽量多内嵌一些东西,除了HTML骨架之外,把loader.js内嵌,再加一个loading界面,让用户觉得没那么长时间白屏,另外如果前端路由切换是pusState控制的话,可以在服务端知道前端路由url,然后在主文档中直接内嵌数据,主文档体积大了不少,但是可以减少2次RTT,优化对比:

    新葡亰496net 11

    当然,如果你的单页面应用体量很小,完全不用按需加载,主文档内嵌一切可以再减少一次RTT,得到:

    新葡亰496net 12

    不过这么极端的做法其限制就是:你的应用千万不能太大!

    前端渲染模式我厂的代表产品:UC奇趣百科 ,其优化点:
    * 主文档loader.js内嵌、数据内嵌、loading界面内嵌
    * 页面资源按需加载,请求动态合并
    * localstorage存储JS/CSS

    在国内的网络环境下感觉还OK吧。。。

    兼顾性能、兼顾SEO,还是单页面应用,是可以做到的!

    很明显,前端JS渲染由于违背了浏览器的优化策略,总是存在一个不可突破的瓶颈:

     

    style="font-family: 'Microsoft YaHei';">JS和数据没加载完,JS拼装数据的逻辑没执行完,浏览器不能开始正常的渲染流程。

    这个性能差异我感觉短时间内这种JS渲染的webapp是无法跟传统页面输出模式相比较的,因为浏览器的各种渲染优化策略基本上都是围绕着传统页面时序展开的。有没有办法突破这个性能瓶颈,并且兼顾SEO,但还保留单页面应用的体验呢?

    答案是:有办法。

    有人或许会想到 Isomorphic Javascript,所谓的同构JavaScript,或者什么前后端模板复用,相信我,这个概念根本就是扯淡!

    其实办法很简单,根本用不着同构JS,页面还是服务端拼装好的,CSS在head中,主文档是完整的HTML,JS在body尾部;但需要在后端模板中实现一种功能:允许通过特殊的ajax请求以json格式响应页面中的局部区域。这项技术被称为 Quickling。

    此外,单页面应用还有一项优化手段,叫PageCache,前端控制页面切换时,把之前的页面缓存到内存中,下次再回到这个页面就直接展现,不用再次请求数据拼装模板渲染,进一步优化用户在站内浏览的体验。

    基于Quickling和PageCache我们在印度市场(网络环境超差)实现了两个单页面应用产品:YoloSong 和 Huntnews ,其优化点:

    • 首次访问服务端渲染,页面间Quickling切换,单页面体验
    • 所有链接可爬取,解决SEO问题
    • PageCache缓存已访问页面,加速切换,历史记录前进后退
    • 可 全站禁用JS,不影响浏览体验
    • 按需加载,请求合并

    本文由新葡亰496net发布于新葡亰官网,转载请注明出处:官方文书档案笔记,angular连串学习笔记

    关键词: