2 谈谈你对SPA单页面的理解

SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTMLJavaScriptCSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载

优点:

  • 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
  • 基于上面一点,SPA 相对对服务器压力小;
  • 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理

缺点:

  • 初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScriptCSS 统一加载,部分页面按需加载;
  • 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
  • SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势

单页应用与多页应用的区别

| 单页面应用(SPA) | 多页面应用(MPA)
---|---|---
组成 | 一个主页面和多个页面片段 | 多个主页面
刷新方式 | 局部刷新 | 整页刷新
url模式 | 哈希模式 | 历史模式
SEO搜索引擎优化 | 难实现,可使用SSR方式改善 | 容易实现
数据传递 | 容易 | 通过urlcookielocalStorage等传递
页面切换 | 速度快,用户体验良好 | 切换加载资源,速度慢,用户体验差
维护成本 | 相对容易 | 相对复杂

实现一个SPA

  • 监听地址栏中hash变化驱动界面变化
  • pushsate记录浏览器的历史,驱动界面发送变化

  1. hash 模式 :核心通过监听url中的hash来进行路由跳转
    // 定义 Router  
    class Router {  
        constructor () {  
            this.routes = {}; // 存放路由path及callback  
            this.currentUrl = '';  
              
            // 监听路由change调用相对应的路由回调  
            window.addEventListener('load', this.refresh, false);  
            window.addEventListener('hashchange', this.refresh, false);  
        }  
          
        route(path, callback){  
            this.routes[path] = callback;  
        }  
          
        push(path) {  
            this.routes[path] && this.routes[path]()  
        }  
    }  
      
    // 使用 router  
    window.miniRouter = new Router();  
    miniRouter.route('/', () => console.log('page1'))  
    miniRouter.route('/page2', () => console.log('page2'))  
      
    miniRouter.push('/') // page1  
    miniRouter.push('/page2') // page2  
  1. history模式history 模式核心借用 HTML5 history apiapi 提供了丰富的 router 相关属性先了解一个几个相关的api
  • history.pushState 浏览器历史纪录添加记录
  • history.replaceState修改浏览器历史纪录中当前纪录
  • history.popStatehistory 发生变化时触发
    // 定义 Router  
    class Router {  
        constructor () {  
            this.routes = {};  
            this.listerPopState()  
        }  
          
        init(path) {  
            history.replaceState({path: path}, null, path);  
            this.routes[path] && this.routes[path]();  
        }  
          
        route(path, callback){  
            this.routes[path] = callback;  
        }  
          
        push(path) {  
            history.pushState({path: path}, null, path);  
            this.routes[path] && this.routes[path]();  
        }  
          
        listerPopState () {  
            window.addEventListener('popstate' , e => {  
                const path = e.state && e.state.path;  
                this.routers[path] && this.routers[path]()  
            })  
        }  
    }  
      
    // 使用 Router  
      
    window.miniRouter = new Router();  
    miniRouter.route('/', ()=> console.log('page1'))  
    miniRouter.route('/page2', ()=> console.log('page2'))  
      
    // 跳转  
    miniRouter.push('/page2')  // page2  

题外话:如何给SPA做SEO

  1. SSR服务端渲染

将组件或页面通过服务器生成html,再返回给浏览器,如nuxt.js

  1. 静态化

目前主流的静态化主要有两种:

  • 一种是通过程序将动态页面抓取并保存为静态页面,这样的页面的实际存在于服务器的硬盘中
  • 另外一种是通过WEB服务器的 URL Rewrite的方式,它的原理是通过web服务器内部模块按一定规则将外部的URL请求转化为内部的文件地址,一句话来说就是把外部请求的静态地址转化为实际的动态页面地址,而静态页面实际是不存在的。这两种方法都达到了实现URL静态化的效果
  1. 使用Phantomjs针对爬虫处理

原理是通过Nginx配置,判断访问来源是否为爬虫,如果是则搜索引擎的爬虫请求会转发到一个node server,再通过PhantomJS来解析完整的HTML,返回给爬虫。下面是大致流程图

Last Updated:
Contributors: leeguooooo