评论

【实战】微信小程序云开发中,使用TcbRouter路由模块在一个云函中解决多个业务逻辑

最近在做自己的小程序《看啥好呢》,这个小程序是使用云开发的方式开发的,功能特别简单,就是获取豆瓣、大麦网的数据展示,虽然功能简单,但还是记录下开发过程和一些技术点,大约会有两篇博文产出,这是第二篇。

最近在做自己的小程序《看啥好呢》,这个小程序是使用云开发的方式开发的,功能特别简单,就是获取豆瓣、大麦网的数据展示,虽然功能简单,但还是记录下开发过程和一些技术点,大约会有两篇博文产出,这是第二篇。GitHub 地址

在上一篇《实战:在小程序中获取用户所在城市信息》中,介绍了如何获取用户所在城市,这一篇就介绍一下小程序云函数开发的一些东西。

项目结构

小程序《看啥好呢》全部数据都来自豆瓣网和大麦网,整个项目结构如下

电影、电视模块下的每个分类,只是改变豆瓣网同一个接口某个字段即可,本地好看模块是拿的大麦网的接口,而电影详情页是使用 Cherrio 实现豆瓣电影详情页网页解析拿到的数据。

项目目录结构

项目开发

由于电影、电视列表模块用的都是同一个接口,只是某些参数不同,而详情页是解析网页方式,不是走的接口,所以处理逻辑与列表不相同,怎么样在一个云函数中处理不同的逻辑呢。

从上面的项目目录结构可以看出,我为整个项目只划分了两个云函数,分别是 damai 和 douban,在 damai 中处理来自大麦网的数据,douban 中处理来自豆瓣的数据。

Router 模块

在前端中,Router 可以处理不同的请求分支,于是在云函数中也可以使用 Router,下面使用了 tcb-router,它是一个基于 koa 风格的小程序·云开发云函数轻量级类路由库,主要用于优化服务端函数处理逻辑。

douban/index.js

// 云函数入口文件
const cloud = require("wx-server-sdk");
const TcbRouter = require("tcb-router");

cloud.init();

// 云函数入口函数
exports.main = async (event, context) => {
  const app = new TcbRouter({ event });
  /** 查询列表 */
  app.router("list", async (ctx, next) => {
    const list = require("./list.js");
    ctx.body = list.main(event, context);
  });
  /** 查询详情 */
  app.router("detail", async (ctx, next) => {
    const detail = require("./detail.js");
    ctx.body = detail.main(event, context);
  });
  return app.serve();
};

云函数目录结构如下

/douban
----/node_modules
----index.js
----list.js
----detail.js
----package.json

HTTP 请求

HTTP 请求方面,小程序云函数中常用的是 request-promise,它是一个 Promise 分格的 HTTP 请求库,使用它还必须安装它的依赖,两个包都要安装

npm install --save request
npm install --save request-promise

下面看看电影列表是怎么处理的,douban/list.js

const rp = require("request-promise");

exports.main = async (event, context) => {
  const type = event.type;
  const tag = encodeURI(event.tag);
  const limit = event.limit || 50;
  const start = event.start || 0;

  const options = {
    uri: `https://movie.douban.com/j/search_subjects?type=${type}&tag=${tag}&page_limit=${limit}&page_start=${start}`,
    headers: {
      Host: "movie.douban.com",
      Referer: "https://movie.douban.com/",
    },
    json: true,
  };
  return rp(options)
    .then((res) => res)
    .catch((err) => {
      console.log(err);
    });
};

请求参数都放在 event 当中,在调用云函数的时候传递,下面是电影列表页面调用云函数的代码

let { id, type } = this.data;
wx.cloud
  .callFunction({
    name: "douban",
    data: {
      $url: "list",
      type,
      tag: id == "hot" ? "热门" : "最新",
    },
  })
  .then((res) => {
    const result = res.result;
    this.setData(
      {
        dataList: result.subjects,
      },
      () => {
        wx.hideLoading();
      }
    );
  })
  .catch((err) => {
    console.log(err);
    wx.showToast({
      title: "出错了",
      icon: "none",
    });
    wx.hideLoading();
  });

从调用云函数的 data 属性中的第一个参数 $url 是请求的路由,第二个参数开始即是请求需要的参数。

Cherrio 实现详情页解析

cheerio 是一个 jQuery Core 的子集,其实现了 jQuery Core 中浏览器无关的 DOM 操作 API,以下是一个简单的示例:

var cheerio = require("cheerio");

// 通过 load 方法把 HTML 代码转换成一个 jQuery 对象
var $ = cheerio.load('<h2 class="title">Hello world</h2>');

// 可以使用与 jQuery 一样的语法来操作
$("h2.title").text("Hello there!");
$("h2").addClass("welcome");

console.log($.html());
// 将输出 <h2 class="title welcome">Hello there!</h2>

简单来说,cheerio 就是服务器端的 jQuery,去掉了 jQuery 的一些效果类和请求类等等功能后,仅保留核心对 dom 操作的部分,因此能够对 dom 进行和 jQuery 一样方便的操作。它是我们筛选数据的利器——把多余的 html 标签去掉,只留下我们想要的内容的重要工具。需要注意的是,cheerio 并不支持所有 jQuery 的查询语法,比如 $('a:first') 会报错 ,只能写成 $('a').first() ,在使用的时候需要注意。

下面是电影、电视的详情页处理逻辑

const rp = require("request-promise");
const cheerio = require("cheerio");

exports.main = async (event, context) => {
  const subjectId = event.id;
  const baseUrl = "https://movie.douban.com/j";
  const options = {
    uri: `${baseUrl}/subject_abstract?subject_id=${subjectId}`,
    headers: {
      Host: "movie.douban.com",
      Referer: "https://movie.douban.com/",
    },
    json: true,
  };
  return rp(options)
    .then((res) => {
      return rp(
        `https://movie.douban.com/subject/${subjectId}/`
      )
        .then((html) => {
          const $ = cheerio.load(html);
          const plot = $("#link-report")
            .find("span")
            .text(); //.replace(/\s/g, '')
          res.subject.plot = plot;
          return res;
        })
        .catch((err) => {
          console.log(err);
        });
    })
    .catch((err) => {
      console.log(err);
    });
};

完整源码已开源 GitHub,是一个很好的学习项目。

最后一次编辑于  2020-12-23  
点赞 0
收藏
评论

1 个评论

  • 阿康.sol
    阿康.sol
    2020-12-28

    像你这样玩的话,只需要创建一个云函数不就完事儿?

    那针对云函数特性的话,冷启动时候,云函数创建实例,是不是上面的所有程序流程,每个用户请求都会走一遍?因为云函数不是常驻的,只无状态的哦,就算有个函数池能达到复用效果,好像这种方式做起来还是有点奇怪哈?

    2020-12-28
    赞同
    回复 1
    • 2021-01-04
      你可以都放在一个云函数,你也可以根据业务模块分多个云函数,这跟你处理页面路由配置一样,我们的单页程序的路由配置文件也会根据业务模块分成不同的js文件。

      每个用户请求都会走一遍?则么会呢,你调用了哪个路由才会走哪个啊,至于初始化实例,也只有一个啊,只会初始化index.js
      2021-01-04
      回复
登录 后发表内容