基于ServerLess的极简网页计数器:源码分析与实践

image.png

这几天基于支持HTML5无感认证的ServerLess平台开发了一款博客、门户网站等web平台经常使用的PV统计工具:page-counter 。主要用到的技术是js+webpack。javascript

回首看来,解决了如下几个比较有意思的问题:css

  • 如何设计代码,用统一的方式支持多个ServerLess平台?
  • 如何架构项目,使得其支持CDN和npm两种方式引入?
  • 如何精简源码,源码大小控制在4kb?
  • 如何借助webpack分离生产和测试环境?

源码地址:github.com/dongyuanxin…html

npm地址:www.npmjs.com/package/pag…前端

若是有兴趣的同窗,欢迎在阅读完本文后一块儿接入其余平台的开发; 以为不错的同窗,欢迎给个Star哦java

🔍查看所有文章目录 / 阅读原文🔍jquery

项目目录

如上图所示,bin/backend 目录是暂时没用的。几个比较重要的目录功能说明:webpack

  • build/ : webpack的配置文件,分别是公共配置、开发模式配置、生产模式配置
  • dist/
    • index.template.html: 开发模式下配合webpack的html模板文件
    • page-counter.min.js: 打包后的page-counter内容,供CDN引入
    • page-counter.bomb-1.6.7.min.js:我手动修改而且打包的Bomb平台源码
  • examples/ : gh-pages页面,请看此页面
  • deploy.sh : gh-pages部署脚本,支持ssh和https协议
  • index.js : npm的入口文件
  • index.build.js : CDN打包入口文件
  • src/ :
    • serverless/ : 暴露不一样平台的统一接口
    • config.js : 自动读取全局配置
    • utils.js : 经常使用函数方法

抽象接口:支持多Serverless平台

src/serverless/interface.js 中定义了不一样平台的类的公共父类。虽然js不支持抽象接口,可是也能够经过抛出错误来实现:css3

export default class ServerLessInterface {
  constructor () {}

  ACL () {
    throw new Error('Interface method "ACL" must be rewritten')
  }

  setData () {
    throw new Error('Interface method "setData" must be rewritten')
  }

  count () {
    throw new Error('Interface method "count" must be rewritten')
  }
}
复制代码

而 leancloud.js 、bomb.js 等不一样平台的类都要实现这个接口中的这3个方法。而后经过 src/serverless/index.js 统一暴露出去:git

import LeanCloud from './leancloud'
import Bomb from './bomb'

class ServerLessFactory {
  constructor (name) {
    name = name.toLocaleLowerCase()

    switch (name) {
      case 'leancloud':
        return new LeanCloud()
      case 'bomb':
        return new Bomb()
      default:
        throw new Error('Serverless must be one of [ leancloud, bomb ]')
    }
  }
}

export default ServerLessFactory
复制代码

这两种设计,既解耦了不一样平台的代码,并且还约束了实现规则。若是想接入更多平台,只须要建立新文件,而且暴露一个继承 ServerLessInterface 接口的指定方法的子类便可。es6

快速方便:支持CDN和npm的使用

一个成熟的前端小工具须要考虑到多种引入方式,目前主流的就是cdn和npm。例如jquery,cdn引入,jq会被自动挂载在window对象上;npm引入,则做用域只在当前文件模块有用。

在考察了多种同类工具后,针对cdn和npm作了不一样的处理。

npm

对外暴露 PageCounter 对象,其上有3个方法:

  • setData() :将当前页面信息发送到云数据库
  • countTotal() : 统计数据库总记录数(网站总PV),而且将返回结果自动放入id为page-counter-total-times的标签里
  • countSingle() : 统计数据库符合要求的记录数(当前页面PV),而且将返回结果自动放入id为page-counter-single-times的标签里
import PageCounter from './src'

export default PageCounter
复制代码

CDN

不会在全局挂载上述对象方法,会自动执行上面的3种方法。考虑到并发以及pv数容许1之内的偏差,没有保证串行。

import PageCounter from './src'

PageCounter.setData()
PageCounter.countTotal()
PageCounter.countSingle()
复制代码

精简源码:巧用package.json和第三方SDK

通过精简,打包后cdn引入的源码只有4kb。npm引入的话,webpack会自动进行tree shaking。由于要对接不一样的serverless平台,所以须要使用他们的sdk。

而这些sdk分红2种:

  • 相似leancloud:既能够npm引入,也能够cdn引入后自动挂载到window对象
  • 相似bomb:无cdn引入,只要npm引入

针对第二种状况,我采起的方案是手动打包编译。好比对于bomb的sdk,专门建立新的工程,而后配合webpack和如下代码,进行打包。

import Bomb from 'hydrogen-js-sdk'
window.Bomb = Bomb
复制代码

打包后的源码放入版本库,这样借助 unpkg.com 等常见的CDN平台就能够引入了。这么作的很重要的一点是: 代码中都是经过window上的对象读取对应serverless平台的api,这样就不会被webpack识别,进而发生重复无用打包

关于读取配置的文件,都放在了 src/config.js 下。考虑到script标签引入形成的变量挂载时间点不肯定,读取采用了动态读取。为了操做起来更方便,而不是像调用函数那样,借助了 es6类语法中的 setter和getter。

// 举个例子:
class Config {
  constructor() {}

  get serverless() {
    if (!window.PAGE_COUNTER_CONFIG) {
      throw new Error('Please init variable window.PAGE_COUNTER_CONFIG')
    }

    return window.PAGE_COUNTER_CONFIG.serverless || 'leancloud'
  }
}

const config = new Config()
console.log(config.serverless) // 返回当前最新的window.PAGE_COUNTER_CONFIG.serverless
复制代码

最后讲讲package.json的小技巧。虽然代码中没有使用import语法读取sdk的对象,可是我仍是把leancloud、bomb平台的sdk放入了 dependencies 。这样作有什么好处呢?

用户只须要安装page-counter便可,其余sdk自动安装(不须要手动再敲命令)。而后用户就可使用下面语法美滋滋引入:

import('hydrogen-js-sdk') 
  .then(res => {
    // 将 Bomb 对象挂载在 window 上
    window.Bomb = res.default
    // 设置应用信息
    window.PAGE_COUNTER_CONFIG = {
      // ...
    }
    return import('page-counter')
  })
  .then(res => {
    const PageCounter = res.default
    PageCounter.setData() // 发送当前页面数据
    PageCounter.countTotal() // 将总浏览量放入 ID 为 page-counter-total-times 的DOM元素中
    PageCounter.countSingle() // 将当前页面浏览量放入 ID 为 page-counter-single-times 的DOM元素中
  })
复制代码

Webpack:分离生产和开发环境

不得不说,webpack真的好用呀。脏活累活以及常见工具,它都给你承包了。

webpack.base.conf.js 是两种模式的公共配置,指明入口文件以及代码环境(web)。而且可以识别模式,而后自行拼接配置。

webpack.prod.conf.js :生产模式,主要为了打包源码,方便CDN引入。

webpack.dev.conf.js : 开启热更新以及本地服务器方便调试,渲染的前端调试页面的模板文件就是 dist/index.template.html

更多文章

⭐在GitHub上收藏/订阅⭐

《前端知识体系》

《设计模式手册》

《Webpack4渐进式教程》

⭐在GitHub上收藏/订阅⭐

相关文章
相关标签/搜索