CDN模式开发运用SPA技术解决方案

808 阅读7分钟

前端开发技术目前更新迭代很快,SPA技术目前趋势很明显,前端开发中,React&Vue两大框架在NPM里面的下载量分别达到300W&79W,这并不包括目前很多企业在非NodeJS开发环境下的CDN环境,未来的趋势很明显,但是React和Vue在官网上给出的示例和讲解都是基于NodeJS环境下进行的运用,本人最近好奇,查看了部份源码,尝试运用了在CDN环境下如何集成Vue + Vue-Resource + Vuex + Vue-Router + IView,这套运行方案,主要解决的是后端人员排斥NodeJS的情况(大部份是后端人员觉得学习成本高、效率慢)下,或者针对老项目的历史遗留问题需要SPA技术的情况下才可以使用。

在实现这套方案,其实主要有二个难点要攻关,一、单页模板Template Component如何与模块化的export JS进行整合?二、静态文件的引用,如何使用包管理?

在实现这套方案前,需要准备以下几个脚本(简单介绍用途):

  • vue --不用解释
  • vuex --貌似不用解释,状态共享
  • vue-resource --ajax库,虽然停止维护了,但是还是喜欢这里面的语法,官网不维护,可 以下载源码,自己维护,顺便还可以学习一下里面的优秀语法,不喜欢的可以换axios库,个人觉得axios名字太low,语法也不喜欢,不喜欢用。
  • vue-router --组件视图的路由
  • iview --UI框架
  • requireJS --CDN模式下,优秀的模块化脚本,本解决方案,全部靠它。
  • lodash.js --一种Array,Collection,Function,Lang,Math等等函数扩展库,可利用率极高,建议下载FUll版,不要压缩版。
  • text.js --可以正确解释任何文本内容的脚本(运行时1)
  • css.js --样式表的解释器 (运行时2)
  • html.js --超文本内容解释器 (运行时3)

上述的脚本文件内容几乎覆盖了前端大部份的问题依赖的脚本,运行时1、2、3是作为运行时的编辑器功能,不同的是NodeJS中是在Build的时候运用这种方案,而CDN模式中是在浏览器加载Template时运用这种方案,可以理解成一个是开发阶段(运行时)编辑器,一个是生产阶段(浏览器渲染)编辑器,这是技术方案不同,带来的技术解决线路,这里只做说明,不做细节的讨论。下面在给大家看看本人的代码结构:

上图的代码,只做本人实验时的开发结构,可根据个人的开发需求和习惯调整,接下来,我们在来看看如何将这库进行构建成一个完成的App应用,首先,我们要从入口文件开始:

上图中算是详细的解释了这些配置的用途,map与shim配置的节点解释有点绕,在这里我再仔细解释一下这两上节点的意义,map 中配置的key表达Require语法使用时,当前运行脚本PATH是否匹配,如果以“*”来表示key,则匹配所有路径,key后面表达的value,意思是说,如果当前运行脚本匹配正确PATH && Require语法使用到了相应的前缀,则持行指定的脚本,看下面例子:

map : {
      "/api" : {
               "text" : "/scripts/loader/text-1"
       },
      "/api-1" : {
             "text" : "/scripts/loader/text-2"
      }
}
// PATH === ./src/scripts/api/data.js
require("text!http://localhost:8001/assetcs/json/data.json");     --->持行./src/scripts/loader/text-1.js来处理json
//PATH  === ./src/scripts/api-1/data.js
require("text!http://localhost:8001/assetcs/json/data.json");    --->持行./src/scripts/loader/text-2.js来处理json

shim配置是告诉Require依赖关系,简单的理解就是管理脚本之间的运行顺序,shim与map配合使用就是表达在匹配合法的路径下,优先使用某个脚本。

baseUrl、paths、packages三个配置我就不详细的解释了 ,看字面的意思也知道是做什么用途的,最后的一句Require(['view/app']);则是整个app运行的启动入口,在这个app.js里面,则是vue全家桶运用的开始,先看看我的app的代码,然后我在一步一步的解析如何运用:

上图描述了app入口文件运行过程,在这里面主要的是分析RouterGlobal封装内容和Store的内容,在这一步,我们先来分析RouterGlobal脚本,假如我认为你已经具有vue-router的基础知识,在我们使用官方文档时,几乎是template的SPA单页组件,而我们现在使用的是CDN方案,官方的SPA单页不能适用,现在我们就要解决问题一:如何处理Template Component和模块化的问题。

首先,我们来看看vue-router的源码,找到Template Component部份的运行过程,如下图:

我们知道,vue-router加载SPA单页组件的入口配置是components, component来表达组件对象的,所以我通过这两个字段调试,发现vue-router中内部组成Template Componet返回内部实际对象是VueComponent对象,我现在一步一步解释这个代码。

首先我们在new VueRouter({ routes : routers, mode : 'history' })的时候,vue-router内部已经开始解析相关配置信息了,而components, component两个字段最终首先进入到2039行代码:extractGuards函数,这个函数的主要作用是将用户的routers中components和component转换成VueComponent数组,2045行调用1794行的flatMapComponents函数,这个函数主是作用是循环运行2046行回调函数,2046行extractGuard是最终的VueComponet对象的封装器,def = _Vue.extend(def);2062这句代码已经很明显的暴露出了Vue的SPA组件是什么对象了,def在这里传递运用很频繁,那def是什么呢,看下图:

上面两个图已经很清楚的指出了vue-router的SPA Component是如何封装的了,相信大家也看得非常明白了,不知道大家有没有注意到本人的routes配置的components并不是一个Object,而是一个function类型,这并不符合官方的typeof def !== "function"的断言,在这里我为什么要这样做呢?首先这里我的做法主要是考虑性能问题,假如说当我们请求一个A页面的时候,因为vue-router是集中加载所有组件,会将A页面不相关的B、C、D...等等全部加载进来,这并不是我们想要的,如果是一个非常大的项目,请求一个页面,要花10~20s,或者更长,我想这种app应该活不过一个月,在官方针对这种情况有require.ensure函数来处理这种极端情况,但是CDN模式是没有这种函数的,所以我们需要自己来完成。

到这里,我们已经分析清楚了SPA Component的核心实现内容了,那么我们的难点问题一就有办法解决了,下面看看本人是如何构建的:

Router脚本重新封装路由元主要的目标就是解决难点一中的Template Component模块化具体实现,看到这里,我们只剩下将RouterConfig和Router(路由元信息)进行整合到VueRouter中来,看下图:

当我们请求一个app地址时,根据路由的配置关系,会去加载到指定的html和js脚本,返回一个VueComponent对象给Vue,这个时候,就可以看到我们真正的App界面了,难点二的问题在整个项目构建当中,已经贯穿了讲解了,在这里,vuex的封装本人就不写出来了,相信大家也有了自己思路,值得一提的是,vuex中官方的语法:

import {  mapGetters, mapActions } from 'vuex'
export default {
     computed : {
        getUser () {
            //相关代码
        },
        ...mapGetters({
            'getUserName' : 'User/getUserName'
        })
    }
}
//代替方法:
var vuex = require('vuex');
var component = Vue.extend({
    computed :  Object.assign({
        getUser : function() {  
            //相关代码 
        }
    }, vuex.mapGetters({
        'getUserName' : 'User/getUserName'
    }))
})

以上是全部Vue全家桶在CDN模式下开发,最主要的解决方案代码,当然 ,上面的代码也是本人做的一个实验,整体封装还是比较随意的,如果是真实项目,可以参考本人的实现思路,对整个框架可以更加细致、系统性的封装,这里只做学习使用。

给大家看看最终运行成果:

github.com/Song-Qian/C…