单页WEB应用(九),终篇-总结和发布

前言

本篇是该书系列最后一篇,本书所有涉及的功能和其中所涉及的知识点,基本已经学习完成。

大体上有以下几个部分

  1. 简单的页面布局

    第一个部分首先是进行简单的页面,和其布局的设计,并且采用的是模块化思想,组件组装器概念,因此每个模块都有自己的页面代码,在加载模块的时候将其渲染出来。

  2. 功能添加和功能容器,或者说模块容器及其子模块

    这部分主要涉及,模块容器 shell 和页面子模块的完成,这部分主要值得学习的是该单页应用的实现思想,即组件组装器概念:也就是将 shell 定义成全局的容器作用,在该容器中负责着子组件的加载和初始化行为,而子组件的功能实现被限定在自身模块当中,从而不会与其他模块产生混淆,降低了模块与模块之间的依赖性;

  3. 数据模型模块

    这部分主要负责数据模型的创建和管理,比如(用户管理对象 peoplechat 聊天消息对象等等),而数据模块负责对该应用涉及到的数据的增删改查等功能的实现。

  4. 服务器

    如果说前面几个部分都属于客户端一块的东西,那这以及这以后的部分则是涉及到服务器端的业务和功能的内容了,该应用的服务器部分自然由 Node.js 来承担,而其中涉及的路由、通信、数据库则是由 expresssocket.io,和 mongodb 等来完成,并且在后台服务器的处理过程中,还涉及了一些认证,数据验证,CRUD 等概念。

经过该书的系统性学习加实践,对于单页应用的大体开发流程,和其中涉及的部分关键技术有了更深的认识,其中也对部分关键技术做了更深入的研究,比如(Node.jsexpresssocket.iomongodb),并且在该书系列完成之后便会依次完整系统性的去学习这些知识点。

知识点回顾

模块化思想

模块化实现,不仅让功能进行分离,减少模块间的依赖,还可使代码更容易维护,甚至能使模块得到复用,好处自然是值得称赞。

比如当前主流的模块化编程有(CommonJSAMDCMDES6 标准),这里简单看下它们的简单使用方式,稍详细点的介绍,之前有篇文章是关于这几个的,地址:WEB前端模块化基础知识 go ✈

  • CommonJS

    相对而言,CommonJS 模块化更偏向于服务器端,也正由于它的发展就是由服务器端模块化的需要,随着技术不断更新和发展,前端模块化的概念推出,CommonJSNode.js的推动下大力支持了前端模块化。

    // welcome.js
    
    var hello = 'hello world!';
    
    function sayHello () {
        alert( hello );
    }
    
    function bye() {
        alert( 'bye bye !' );
    }
    
    module.exports = {
        hello   : sayHello,
        bye     : bye
    }
    

    使用:

    // test.js
    var welcome = require( './welcome' );
    
    welcome.hello();    // 'hello world!'
    welcome.bye();      // 'bye bye !'
    
  • AMD

    AMD:Asynchronous Module Definition,异步模块定义,属于异步加载,这个其实是一套基于浏览器模块化开发的规范,要使用该规范进行模块化开发,需要用到其中的函数库:requireJS

    相比较 CMD 而言,这个有个不好的地方就是,模块需要提前一次性进行加载。

    来看下器定义和使用:

    定义:

    // module.js
    
    define(
        ['dependModule1', 'dependModule2', ...],  // 这个数组里面包含了该模块所依赖的所有模块
        function () {
    
            // 这里面就是新模块的内容
    
            return {
                // 返回模块对象
            };
        }
    );
    

    会发现上面模块的定义时,define 中并没有声明模块的名称,这是因为对于 AMD 而言,一个文件既是一个模块,因此该模块的文件名也就是该模块的名称:module

    使用:

    // test.js
    
    // 加载模块
    require(['module'], function ( myModule ) {
    
        // myModule 就是模块加载完成之后,该模块的对象名,在这个回调里面
        // 可以通过 myModule 来引用该模块
    });
  • CMD

    CMD:Common Module Definition,表示通用模块定义,这个相对 AMD 有个好处就是,能按需加载模块,也就是说在哪里需要才去加载模块,而不是一次性把模块全部加载好,这点会一定程度上减少程序启动时间,但在运行过程中则需要实时去加载模块。

    定义:

    // module.js
    
    define(function (require, exports, module) {
    
        var dependModule = require( 'dependModuleName.js' );
    
        // balabala  module content
    });
    

    使用模块,需要用到 seajs

    seajs.use(['module.js'], function (myModule) {
    
        // 模块加载完成
    });
  • ES6

    ES6 标准下的模块化编程,主要分两种方式

    1. 命名式导出

      这种方式,支持一个文件同时可以导出多个函数或变量

      比如:

      // module.js
      
      export function fn1() {}
      export var1 = 10;

      然后通过 import 使用:

      // test.js
      
      import { fn1, var1 } from 'module'
      
      // 或者别名
      import { fn1, var1 } as myModule from 'module'
      
      // 或者
      import { fn1 as show, var1 as name } from 'module'
    2. 定义式导出

      定义式导出,一个文件只能出现一个定义式的导出变量或函数

      比如:

      // module.js
      
      // 这个时候就可以省略调函数名
      export function () {}

      使用的时候,可以通过文件名,或者 default 关键字来使用 module.js 模块中定义式声明的函数,因为一个文件只需要一个,因此不用担心会引用不对的情况发生

      // test.js
      
      import myModule from 'module';
      
      // 或者使用 default
      import default from 'module';
      
      // 也可别名
      
      import { default as myModule } from 'module';
    3. 混合使用

      也就是说两种定义方式可以混合使用。

      // module.js
      
      export function () {
          // 定义式导出方式的内容 在这里完成
      }
      
      // 下面是命名式方式
      export function fn1() {}
      export function fn2() {}
      export var name = 'lizc';

      使用:

      // test.js
      
      // 这里的 module 可以用 default 代替,得到的结果就是 
      // module.js 中采取定义式方式定义的那个函数
      import { module, fn1, fn2, name } from 'module'
      
服务器端编程

该书中的单页应用中的服务器端是基于 Node.js 来实现的,其中使用到的内容主要包含三大块(ExpressSocket.IOMongoDB)下面依次来简单的回顾下

  • http 服务器

    先来看下使用 http 模块创建简单的服务器

    // server.js
    
    require( 'http' ).createServer(function ( req, res ) {
        // do something
    }).listen( 3000 );
    
  • Express

    使用 express 创建服务器程序

    // server.js
    
    var express = require( 'express' )
        app = express();
    
    app.listen( 3000, function () {
        console.log( 'express server listening on port 3000.' );
    } );

    httpexpress 混合使用的情况

    // server.js
    
    var http    = require( 'http' ),
        express = require( 'express' ),
        app     = express(),
        server;
    
    server = http.createServer( app );
    server.listen( 3000, function () {
        console.log( 'http server with express listening on port 3000.' );
    } );

    express 环境变量(’development’, ‘testing’, ‘staging’, ‘production’)

    获取: app.get( 'env' );

    设置: app.set( 'env', 'testing' );

    中间件使用:比如日志中间件(logger)

    在 3.0+ 版本中 logger 可直接通过 express.logger 使用,4.0+ 版本中大部分中间件都被独立出来了,都需要单独安装使用

    安装 logger: npm install morgan --save, 4.0+ 中间件 logger 的包名是:morgan

    使用: `app.use( logger( ‘dev’ ) );

    可以根据开发环境不同设置不同的中间件

    比如:

    var env = app.get( 'env' );
    
    if ( env === 'development' ) {
        app.use( ... );
    } else if ( env === 'testing' ) {
        app.use( ... );
    }
    
    ......
    

    自定义中间件

    app.use( function ( req, res, next ) {
    
        // 中间件处理程序
    
        next();
    } );

    路由设置

    get 请求路由处理:

    app.get( '/', function ( req, res ) { ...... } );

    post 请求路由处理:

    app.post( '/', function ( req, res ) { ...... } );

    通用路由处理(必须要有 next 且执行 next(); 将路由控制流往下传递)

    app.all( '/', function ( req, res, next ) { ...... } );

  • Socket.IO

    socket.io 主要用来客户端和服务器进行通信用,通过消息加事件触发机制来完成。

    要使用 socket.io ,首先要建立个服务器,如上面 httpexpress 方式,然后获取 socket 实例监听该服务器,得到通信管道。

    // server.js
    
    var http    = require( 'http' ),
        express = require( 'express' ),
        app     = express(),
        socket  = require( 'socket.io' ),
        server, io;
    
    
    // 新建服务
    server = http.createServer( app );
    
    // socket 监听服务
    io = socket.listen( server );
    
    // 服务监听端口 3000
    server.listen( 3000, function () {
        console.log( 'http server with express listening on port 3000.' );
    } );
    
    

    服务程序创建完成,并且使用 socket 进行了监听,那么接下来就可以通过 socket 管道向客户端发送消息:

    io.sockets.emit( 'update', data );

    客户端通过连接,然后监听消息

    前提:客户端需要通过 <script> 引入 socket.io 库文件

    <script src="/socket.io/socket.io.js"></script>

    接下来就可以 :

    var connect = io.connect( 'http://192.168.179.103:3000' );
    
    // 开始监听消息
    connect.on( 'update', function ( data ) {
    
        // 这里就是客户端对服务器发送来的 update 消息的处理程序
        // 参数 data 就是服务器携带的数据
    
    } );
    

    也可以链式写法:

    io.connect( 'http://192.168.179.103:3000' ).on( 'update', function ( data ) {
    
        // 这里就是客户端对服务器发送来的 update 消息的处理程序
        // 参数 data 就是服务器携带的数据
    
    } );

    上面就是个简单的 socket.io 客户端和服务器连接,以及消息的发送和监听处理。

  • MongoDB

    数据库,基于文件存储,文件内容,采用的是 JSON 格式,也就是说该数据库的数据存储包括各种操作都是在 JSON 之上进行的。对于更多的介绍和优缺点,百度/google/官网介绍的很详细,没事可以去观摩观摩。

    这里就直接回顾下它的简单使用

    首先:安装

    sudo apt-get install -y mongodb-org

    安装之后,需要启动下服务,不然直接使用 mongo 进入数据库时候会报错。

    sudo service start mongod

    启动之后就可以进入 mongo 数据库的命令行进行数据库操作了,这里就不回顾了,重点是 Node.js 环境下 mongodb 的使用。

    在新版 2.0+ 中,连接方式已经发生了改变,使用如下:

    // 引入数据库模块,并获取数据库实例
    
    var MongoClient = require( 'mongodb' ).MongoClient,
        collection;
    
    // 连接到默认的 test 数据库
    MongoClient.connect( 'mongodb://localhost:27017/test', function ( error, db ) {
    
        // 这个函数有两个参数,第一个表示连接过程中发生的错误对象,如果
        // 该对象存在,表示连接不成功,可以通过该对象获取到具体错误信息
    
        // 只有在 error 为空的情况下,表示连接成功,那么第二个参数 db 就是连接后的数据库实例
    
        // 通过这个实例我们就可以连接到 test 数据库中具体集合对象,比如:user
        collection = db.collection( 'user' );
    
        // 随后就可以对集合进行一系列的 CRUD 操作
    } );

    CRUD 相关函数(需要注意的是这些函数都是基于数据中集合上进行操作的)

    插入:

    collection.insertOne({}, function ( error, result ) {} );

    collection.insertMany([{},{}], function ( error, result ) {} );

    error: 插入失败的错误信息;
    result:插入之后的结果,一般包含插入的条目,简要内容等信息;

    删除:

    collection.deleteOne( {}, function ( error, result ) {} );

    collection.deleteMany( [{},{}], function ( error, result ) {} );

    更新:

    collection.updateOne( find_map, set_map, function ( error, result ) {} );

    collection.updateMany( find_map, { $set: set_map }, function ( error, result ) {} );

    find_map:查找的对象,需要被更新的;
    set_map: 更新的对象,用来替换旧数据的;

    查找:查找功能很强大,并且包含很多功能,具体可参考 find链式函数

    collection.find( {}, function ( error, result ) {} );

  • 通过路由和数据库结合,返回查询结果

    // 路由处理
    app.get( '/user/list', function ( req, res ) {
    
        // 查询所有用户
        collection.find( {} ).toArray( function ( error, result ) {
    
            // 错误退出
            if ( error ) { return false; }
    
            // 成功返回查询到的的结果
            // 回调放在 toArray 中,表示将结果数组化
            res.send( result );
        } );
    } );

发布准备

最后来看下该书对于发布的准备工作都讲了些啥

前面部分讲述了爬虫,错误处理机制,CDN,重点看下缓存的内容。

缓存

缓存方式有下面四种:

  1. Web 存储:把字符串保存至客户端,应用可以访问这些数据,并用它来保存处理好的 HTML,这些HTML是根据服务器上的数据生成的;

    包含两种方式:本地存储(localStorage)和会话存储(sessionStorage)

    两种方式除了存储时期不同之外,使用方式几乎相同

    window.storage = localStorage || sessionStorage;

    对象的使用方式也有两种,通过 *item 系的函数,或者直接使用 obj[ key ] = value 形式,推荐使用函数方式;

    添加:

    storage.setItem(key, value);storage[ key ] = value;

    读取:

    storage.getItem( key );storage[ key ];

    修改:

    storage.setItem(key, newValue);

    删除:

    storage.removeItem( key );

  2. HTTP 缓存:客户端缓存,在客户端保存着服务器的响应;

    这种方式是通过浏览器进行缓存,当服务器给浏览器发送数据时,这个时候浏览器就会把数据缓存起来,下次如果发生同样的数据请求时直接从浏览器缓存中读取。

    对于 HTTP 缓存有两种模式存储:是否检查数据新鲜度(或者说是否过期)

    如果不检查,相当于直接取浏览器缓存;
    如果检查,则需要从服务器重新获取数据;

    因此 HTTP 缓存方式比较适合那种在一定时间内不会有啥改动的数据,比如图片,脚本文件,样式文件等等。

    max-age

    服务器在响应数据的时候,可以通过设置 max-age 来告诉客户端该数据需要被缓存多久,设置方式:
    单位(秒),如果设为 0 ,表示每次请求都需要检查数据是否是最新的。

    res.header( 'Cache-Control', 'max-age=28800' );

    阻止中间服务器使用它的缓存,可以通过下面三个属性来达到目的:

    • no-cache: 不进行缓存,使用起来会引起不必要的慢,建议使用 no-store
    • no-store:通知客户端和中间服务器,不缓存本次请求结果;
    • last-modified:在不设置 Cache-Control 情况下,客户端会根据这个属性来决定缓存期限,通常是这个值的 1/3;
  3. 服务器缓存:Memcached 和 Redis 服务器缓存,将客户端请求的响应数据保存至服务器,以至于其他请求同样的数据的时候可以使用,而不用重新搜索数据库;

  4. 数据库缓存:数据库缓存查询结果,供下次使用。

把书中的对比图绘制了一份,看下图更直观

缓存机制

结篇语

《单页应用开发》这本书完整的阅读下来,包括一步步跟着把书中代码敲下来,至今刚好一个月了,这本书中从前到后的介绍了单页应用的开发过程,从中学习到了很多内容,不管是编程思想上,还是技术知识上都是不小的积累。

还是随便侃几句吧,文笔太菜了,只怪书读的太少,囧!

学习的步伐越来越紧促,越是感觉知识的缺乏,技术的薄弱,不论以往经历了什么,如今能继续从事自己热爱的技术工作,是件值得开心的事情,前路漫漫,奋斗在路上 !!!

最后感谢作者,也感谢翻译此书的牛牛们!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

若叶岂知秋vip

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值