服务端搭建
一、环境准备
1.操作系统与网络
本文以Arch Linux为例,在VirtualBox虚拟机下运行,通过SSH与宿主机连接。
由于虚拟机通过NAT联网,要想访问在虚拟机中部署的Web服务需要进行端口转发:
- 打开VirtualBox管理器,进入Arch虚拟机的设置页,在网络选项卡中点击高级

 - 点击端口转发,我们新建一个端口转发规则。协议为
TCP;主机IP填写VirtualBox虚拟网卡的地址,在Windows宿主机通过ipconfig命令可查看,默认为192.168.56.1;子系统IP填写虚拟机IPv4地址,在Arch虚拟机中通过ip addr命令可查看(图中红框所示处);子系统端口为我们即将部署的Node服务的监听端口;主机端口为转发到主机后的端口,我们应该避开常用或已被占用的端口(如21、22、25、80、443),为了方便我们可以转发到原端口,本文以3000端口为例

 - 这样设置完毕后虚拟机的3000端口就可以通过
192.168.56.1:3000访问了2.Node.js
由于该虚拟机此前已安装Node.js、MongoDB等,首先将其卸载再做演示:1
pacman -Rsc nodejs mongodb #删除Node.js、MongoDB和所有依赖这两个软件包的程序
 
更多关于pacman(Arch官方软件包管理器)的用法请见这里
通过pacman安装Node.js:1
 pacman -S nodejs npm  #安装node及包管理器npm
或者从Node.js官网下载二进制文件:1
2
3 wget https://nodejs.org/dist/v8.12.0/node-v8.12.0-linux-x64.tar.xz
 xz -d node-v8.12.0-linux-x64.tar.xz
 tar -xvf node-v8.12.0-linux-x64.tar
3.MongoDB
- 方法一:
 
1  | pacman -S mongodb #从官方软件仓库安装mongodb  | 
Arch Linux的默认dbpath是/var/lib/mongodb/,启动服务后可以通过mongo命令进入Mongo Shell
- 方法二:
 
1  | cd /home  | 
为了快速使用mongodb命令,可以配置环境变量。编辑~/.profile或/etc/profile文件,将mongodb/bin路径加入即可:1
 sudo vim /etc/profile
在文件末尾添加一行:1
export PATH=$PATH:/home/mongodb-linux-x86_64-4.0.2/bin
保存修改后,在终端运行以下命令使环境变量生效:1
 source /etc/profile
启动mongod:1
2 mkdir /data/db  #创建数据存放目录
 mongod --dbpath /data/db
此时mongod已经启动,我们可以在另一终端中通过mongo命令进入交互程序
二、部署服务
1.建立应用并安装依赖
首先建立一个工作目录:1
2 mkdir wx
 cd wx
通过npm init初始化一个应用,此时会要求你输入应用的名称、版本、入口文件等信息,可以按Enter键选择默认值,最终npm会为你生成一个package.json文件1
 npm init
下面通过Node包管理器安装Express、Mongoose:1
 npm install express mongoose --save
安装成功后会出现+ express、+ mongoose等字样,这两个包及其所需依赖都被安装在/node_modules中并被写入package.json:

除了上述方法,我们也可以通过全局安装来达到一劳永逸的目的:
1  | npm install express mongoose -g  | 
2.建立HWMongo模块
编辑hwmongo.js:1
vim hwMongo.js
将数据库连接与定义Schema部分写进hwMongo.js,以便后续调用:1
2
3
4
5
6
7
8
9
10
11//hwmongo.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');  //与数据库建立连接
var helloWorldSchema = mongoose.Schema({
    country: String,
    helloWorld: String,
});  //定义数据架构helloWorldSchema
var HWMongo = mongoose.model('HWs', helloWorldSchema);
module.exports = HWMongo;  //导出HWMongo
3.初始化数据
编辑initData.js:1
vim initData.js
将初始数据插入文档:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26var HWMongo = require('./hwMongo');
new HWMongo({
    country: 'China',
    helloWorld: '你好,世界!'
}).save();
new HWMongo({
    country: 'US',
    helloWorld: 'Hello World!'
}).save();
new HWMongo({
    country: 'Danmark',
    helloWorld: 'Hallo, Verden!'
}).save();
new HWMongo({
    country: 'Germany',
    helloWorld: 'Hallo, Welt!'
}).save();
new HWMongo({
    country: 'France',
    helloWorld: 'Bonjour, tout le monde'
}).save();
运行程序:1
node initData.js
如果出现“当前URL解析器被弃用”的警告,我们可以修改hwMongo.js的数据库连接部分(第3行),使用新的解析器:1
mongoose.connect('mongodb://localhost/test', {useNewUrlParser: true});  //Line 3
数据初始化完成之后我们可以通过mongo进入Mongo Shell,在交互程序中通过use wx切换至数据库“wx”,通过show collections可以列出数据库中的全部集合,通过db.hws.find()可以进行查询:1
2
3
4
5
6
7
8
9
10> use wx
switched to db wx
> show collections
hws
> db.hws.find()
{ "_id" : ObjectId("5bb597c822106c03e8e75b72"), "country" : "US", "helloWorld" : "Hello World!", "__v" : 0 }
{ "_id" : ObjectId("5bb597c822106c03e8e75b73"), "country" : "Danmark", "helloWorld" : "Hallo, Verden!", "__v" : 0 }
{ "_id" : ObjectId("5bb597c822106c03e8e75b74"), "country" : "Germany", "helloWorld" : "Hallo, Welt!", "__v" : 0 }
{ "_id" : ObjectId("5bb597c822106c03e8e75b75"), "country" : "France", "helloWorld" : "Bonjour, tout le monde", "__v" : 0 }
{ "_id" : ObjectId("5bb597c822106c03e8e75b71"), "country" : "China", "helloWorld" : "你好,世界!", "__v" : 0 }
4.建立HTTP服务器
编辑入口文件index.js:1
vim index.js
在index.js中编写简易HTTP服务器与查询服务:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22const express = require('express');
const HWMongo = require('./hwMongo.js');  //调用模块
const app = express();
const port = 3000;  //http服务器端口号
app.get('/country', function(req,res) {
    HWMongo.findOne({'country': req.query.country}, function(err, doc) {
        if (err) {
            console.log("数据库出错");
        } else if (doc==null) {
            res.send("输入信息有误,请重试");
            console.log(req.query.country + ":" + doc);
        } else {
            res.send(doc.helloWorld);  //应答报文
            console.log(req.query.country + ":" +doc.helloWorld);
        }
    });
});
app.listen(port, function() {
    console.log('Server is running at http://localhost:3000/.');
});
HTTP服务器跑起来后宿主机所在局域网内的设备可以通过http://192.168.56.1:3000来访问:1
node index.js

至此,服务端已搭建完成
前端小程序实现
一、开发工具
在这里下载并安装微信web开发者工具
二、项目与项目配置文件
新建一个空白项目,编辑项目配置文件project.config.json,为了调试方便,我们将urlCheck一项改为false:1
2
3
4
5
6
7
8{
    ...
    "setting": {
        "urlCheck": false,
        ...
    },
    ...
}
更多关于项目配置文件的描述请见这里
三、文件结构
从微信小程序官方文档中我们可以知道小程序的文件组成结构,具体内容如下
小程序主体部分(项目根目录下):
| 文件 | 必需 | 作用 | 
|---|---|---|
| app.js | 是 | 小程序逻辑 | 
| app.json | 是 | 小程序公共配置 | 
| app.wxss | 否 | 小程序公共样式表 | 
小程序页面:
| 文件类型 | 必需 | 作用 | 
|---|---|---|
| js | 是 | 页面逻辑 | 
| wxml | 是 | 页面结构 | 
| json | 否 | 页面配置 | 
| wxss | 否 | 页面样式表 | 
因此我们在项目目录中新建app.js和app.json,并编辑app.json:
1  | {  | 
此时开发工具会自动建立pages/index目录及其页面组成文件,至此只包含一个页面的最小项目的框架搭建完成,其文件结构如下:1
2
3
4
5
6
7
8.
├── app.js
├── app.json
├── pages
│   └── index
│       ├── index.js
│       └── index.wxml
└── project.config.json
四、页面逻辑及生命周期
每个页面的JS文件中通过Page(Object)函数来注册页面,它接受一个对象类型参数,指定页面的初始数据、生命周期回调、事件处理函数等。
我在此列举几个主要、常用的对象参数(详见这里):
| 属性 | 类型 | 说明 | 
|---|---|---|
| data | 对象 | 页面第一次渲染使用的初始数据,JSON类型 | 
| onLoad | 函数 | 页面加载时触发,一个页面只会调用一次 | 
| onShow | 函数 | 页面显示/切入前台时触发 | 
| onReady | 函数 | 页面初次渲染完成时触发,一个页面只会调用一次 | 
| onHide | 函数 | 页面隐藏/切入后台时触发 | 
| 其他 | 任意 | 开发者添加任意函数或数据 | 
其中所有函数类型的参数均为生命周期回调函数
我们编辑pages/index的页面逻辑文件index.js:
1  | Page({  | 
由于微信小程序支持ECMAScript 6(ES6)标准,因此JS代码可以省略语句末的分号;,并且微信官方所有示例程序均采用省略,因此我们也应该遵循微信小程序的编码规范,语句末省略分号;
五、编译与运行
点击开发者工具工具栏中的编译按钮,即可在左侧的模拟器中看到运行效果:

至此,前端部分建构完成
将服务部署至VPS/云服务器
我们可以把我们建构好的服务部署至服务器以实现公网访问,我将该Node+Express+MongoDB组成的服务端部署在Vultr的VPS上(新加坡机房,用来搭梯子的),由于操作系统是CentOS,在部署过程中部分操作略有不同,在此不再赘述。
部署后将微信小程序中pages/index/index.js的查询函数中的URL修改为VPS/云服务器的公网IP:端口号,实现了全网访问服务:

至此,最小端到端系统实验完成
总结
本实验使我巩固了Node.js、MongoDB、Linux运维、网络原理等多方面知识,并对微信小程序的开发有了一定了解,增强了我对新事物的探索能力。我将在继续深入钻研既学技术的同时努力接触新技术,让自己的技术栈更加充实