使用eggjs+WX小程序开发一个时间管理小程序(二)——项目搭建

书接上文,前端采用小程序原生开发,后端采用eggjs。关于后端项目搭建,可以参考我的另一篇文章《使用egg.js + Vue实现一个简书(三)---服务端搭建》(点我传送),有非常详细的讲解,而且代码也在gitee上共享了,后文会对一些改造点单独提出。

本章要点

  • 从头搭建一个WX小程序
  • 导航栏和tabbar的配置和优化
  • 实现接口封装
  • 实现自动登录

搭建小程序

1. 注册小程序

WX公众平台上注册小程序

小程序注册页面

在该页面,依照提示注册即可,需要注意的是:

  • 第一步账号信息,一个邮箱只能申请一个小程序
  • 小程序主体是个人还是企业,决定了WX能提供的服务内容。比如接入支付需要WX认证(要花钱,一年300),而个人号是无法进行WX认证的。
  • 服务类目决定了小程序允许有的功能,比如我的小程序有日程提醒的功能,则必须申请工具-备忘录的服务类目,首页有一个日历,则必须申请工具-日历的服务类目。这个上线的时候,提交审核后如果缺少某些类目,审核员会打回申请并告知缺漏了什么,补上就可以了。每月可以申请5次更改,问题不大。
    我的服务类目
2. 创建一个小程序工程

小程序开发必须要用到WX开发者工具,在官方文档上下载即可。

开发者工具可以预览效果,也可以写代码,不过我习惯在编辑器上写代码,所以只用来看页面效果和其他操作。

打开开发者工具后,进入创建项目弹窗

创建项目弹窗

APPID填写你申请小程序的appid即可,开发模式选小程序,后端模式选不使用云服务,模板选择选不使用模板
上面是我的选择,各位可以根据自身需求选择不同的选项。

3. 调整目录结构

如果要使用外部的npm包,需要把项目代码和node_modules分隔出来,之后小程序打包,会用项目代码文件夹里的代码出包。


小程序项目目录
  • 调整目录
    如图,原本项目代码在birthtips文件夹下,我创建了一个miniprogram文件夹,用于存放小程序的代码,将模板生成的代码,除了project.config.jsonproject.private.config.json之外,其他的都放到miniprogram文件夹里。
  • 创建package.json
    npm init -y即可,然后按需安装需要的npm包
    比如我前面提到的lunar-javascript包,就在这时候安装。
    安装包之后,会生成node_modules文件夹。
  • 修改配置
    由于我们调整了小程序的目录解构,所以需要修改下project.config.json的配置,让小程序运行的时候,知道要去什么位置找相应的配置文件。
    project.config.json

    如图添加或修改红圈里的配置即可。
  • 编译node_modules
    要知道的是,小程序有效的内容,在miniprogram文件夹中,所以外部node_modules里安装的内容,需要再经过一次构建才能生效。
    在开发工具中,点工具-构建npm,构建后,会在miniprogram中生成miniprogram_npm文件夹。

node_modules 目录不会参与编译、上传和打包中,所以小程序想要使用npm包必须走一遍构建 npm的过程,在每一份miniprogramRoot内开发者声明的package.json的最外层的node_modules的同级目录下会生成一个 miniprogram_npm目录,里面会存放构建打包后的npm包,也就是小程序真正使用的npm包。

  • miniprogram下的目录结构
    目录结构

    如图,为我的项目miniprogram文件夹下的目录结构。

app.json是全局配置
每新加一个页面都需要在这里添加配置,且配置tabbar、导航栏的配置,也是在这里。具体内容可以看WX小程序开发官方文档,写的很清楚。
app.js是入口文件,页面运行从这里开始
pages是页面文件夹,下面也是,一个页面一个文件夹,文件夹下需要有index.js、index.json、index.wxml、index.wxss,这个 属于固定配置,wxmlwxss写法和html、css基本一样,会前端的小伙伴可以丝滑切入。
components文件夹里是组件,组件文件夹的构成和页面文件夹一致。
image文件夹里是图片资源,在小程序里,页面引用、css引用图片文件,都必须使用https的线上图片,而tabbar的图标,则需要放在这里。

至此,项目搭建工作告一段落。

tabbar配置

我使用的是原生的tabbar,有【轻记】和【我的】两个tab,直接在app.json中配置即可。

// app.json
"tabBar": {
    "custom": false, // 如果要自定义tabbar,将该属性设为true
    "color": "#000",
    "selectedColor": "#E0555D",
    "backgroundColor": "#f1f1f1",
    "list": [
        {
            "pagePath": "pages/index/index",
            "text": "轻记",
            "selectedIconPath": "image/change-selected-icon.png",
            "iconPath": "image/change-icon.png"
        },
        {
            "pagePath": "pages/user/index",
            "text": "我的",
            "selectedIconPath": "image/user-selected-icon.png",
            "iconPath": "image/user-icon.png"
        }
    ]
},

colorselectedColor指的是tab里的文字颜色,backgroundColor是tabbar的背景色,pagePath是当前tab跳转的页面路由,selectedIconPathiconPath指的是选中和未选中时的图标图片地址。

效果如图

如果要自定义tabbar,则将custom属性设为true,同时其他配置也得补全,然后自己实现tabbar组件。具体方案可参考文档 传送门

导航栏配置

有三种导航栏实现方案

  • 默认配置
  • 自定义
  • page-meta + navigation-bar

这三种方案,我基于页面需要都有使用,接下来将一一介绍。

首先在app.json进行基本设置。

// app.json
"window": {
    "backgroundTextStyle": "light", // 下拉 loading 的样式,仅支持 dark / light
    "navigationBarBackgroundColor": "#f1f1f1", // 导航栏背景颜色
    "navigationBarTitleText": "Weixin", // 默认的标题,在具体页面的index.json中覆盖其值
    "navigationBarTextStyle": "black", // 导航栏字体颜色
    "backgroundColor": "#f1f1f1" // 窗口的背景色
},

1. 默认配置

// pages/cardetail/index.json
"navigationBarTitleText": "打卡详情"

navigationBarTitleText属性被页面配置覆盖,所以该页面标题显示【打卡详情】
这种方案适用于标题是固定的场景。

2. 自定义
小程序的首页,需要有展开收起日历的按钮,且日历收起时,标题为空,日历展开时,标题为当前日历的年月。

首页

step1:设置使用自定义导航栏

// pages/index/index.json
"navigationStyle": "custom"

navigationStyle设置为custom后,原有的导航栏位置将会消失,.wxml中的页面将顶到最顶部,但左侧的返回按钮右侧的胶囊会一直悬浮在原位。
可以在.wxml中增加一个dom元素,替代原有导航栏的位置,上面可以自定义的放置需要的内容。

这时,遇到了一个问题,导航栏的高度应该是多少?
我们肯定不能写死这个高度,因为不同机型的高度肯定是不一样的。

step2:获取本机信息,计算导航栏需要的高度

// 小程序的导航栏高度 = 状态栏高度 + 右侧胶囊高度,胶囊高度大部分机型是44px,我们直接取44px即可。
// 状态栏高度需要通过小程序提供的方法获取,我们一般在app.js中onLaunch的生命周期中获取,然后存到globalData中,这样页面中就可以直接用了,而不用频繁调用系统接口。
// app.js
const globalData = {
  navBarHeight: 44
};
// app.js
App({
  globalData,
  onLaunch() {
    let res = wx.getSystemInfoSync(); // 同步获取系统信息
    this.globalData.windowWidth = res.windowWidth;
    this.globalData.windowHeight = res.windowHeight;
    this.globalData.statusBarHeight = res.statusBarHeight;
    this.globalData.navBarHeight = res.statusBarHeight + 44 //顶部状态栏+顶部导航,大部分机型默认44px
  }
})

step3: 写页面

// 首页.wxml
<view style="height: {{ navBarHeight }}px;" class="custom-header">
  <view
    class="calendar-btn"
    style="margin-top: {{ statusBarHeight }}px;"
    bindtap="changeCalendarType"
  >{{showCalendar ? '收起' : '展开'}}日历
  </view>
  <view class="custom-title" style="margin-top: {{ statusBarHeight }}px;">{{ nbTitle }}</view>
</view>

// 首页.wxss
.custom-header {
    position: fixed;
    top: 0;
    background: #f1f1f1;
    width: 100%;
    text-align: center;
    z-index: 20;
}
.custom-header .custom-title {
    height: 44px;
    line-height: 44px;
}
.custom-header .calendar-btn {
    height: 44px;
    line-height: 44px;
    position: fixed;
    padding-left: 22rpx;
    color: #2D6286;
    font-size: 28rpx;
}

这种方案适用于需要在导航栏增加一些元素,或者需要去掉导航栏,顶头展示页面内容的场景。
3. page-meta + navigation-bar
小程序的新增页面,需要根据进入时选的日程类型显示新增页的标题,比如从每日打卡进来,标题为【创建每日打卡】,这时默认配置就达不到目的了。

// 新增.wxml  page-mata必须是页面内的第一个节点
<page-meta>
  <navigation-bar
    title="{{nbTitle}}"
    background-color="{{nbBackgroundColor}}"
    front-color="#000000"
    color-animation-duration="2000"
    color-animation-timing-func="easeIn"
  />
</page-meta>
<view>页面其他内容。。。。。</view>

加这个就可以了,只需要按需修改nbTitle这个变量的值就可以了,非常简单。
这种方案适用于,需要自定义设置标题的值,但对导航栏的其他部分无需改动的场景。

小程序接口封装

1. 全局配置

正常小程序开发,会有多个环境,开发、测试、生产环境,针对不同环境,有一些配置项的值,也会有所不同。我的习惯是创建一个config.js,如下:

// config/index.js
const envKey = 'test'; // 需要什么环境配什么环境
const envMap = {
    'dev': {
        // 后端接口的地址(由于开发环境也是本地运行的eggjs服务,所以指向127.0.0.1)
        requestUrl: 'http://127.0.0.1:7001', 
        messageTemplateId: '开发环境模板消息模板id',
    },
    'test': {
        requestUrl: '测试环境接口地址',
        messageTemplateId: '测试环境模板消息模板id',
    },
    'prod': {
        requestUrl: '生产环境接口地址',
        messageTemplateId: '生产环境模板消息模板id',
    }
};
 
export default {
    envKey,
    envSet: envMap[envKey]
};
 
 
// app.js
import globalConfig from './config/index';
const globalData = {
  globalConfig,
};
App({
  globalData,
})

首先在config/index.js做了配置,分别导出当前环境和当前环境对应的配置。

然后在app.js中引入了配置项,然后将其存放到全局变量globalConfig中。

在页面中,可以用以下方式使用

const app = getApp();
const globalApp = app.globalData;
console.log(globalApp.globalConfig.envSet);
2. 封装请求接口方法

创建一个request.js文件

// utils/request.js
import globalConfig from '../config/index';
 
export const myRequest = (config) => {
    const header = {
        authorization: wx.getStorageSync('token') || ''
    };
    return new Promise((res, rej) => {
        let url = config.url.replace(/^\//, '');
        wx.request({
            url: `${globalConfig.envSet.requestUrl}/${url}`,
            data: config.data,
            header: Object.assign({}, header, config.header || {}),
            method: config.method || 'POST',
            responseType: config.responseType,
            success(reqRes) {
                if (reqRes && reqRes.data) {
                    if (config.responseType == 'arraybuffer') {
                        res(reqRes.data);
                    } else {
                        if (reqRes.data.status == 200) {
                            res(reqRes.data);
                        } else {
                            wx.showToast({
                                title: reqRes.data.message,
                                icon: 'none'
                            });
                            rej(reqRes.data);
                        }
                    }
                }
            },
            fail(reqErr) {
                rej(reqErr);
            }
        })
    });
}
 
 
// app.js
import { myRequest } from './utils/request';
const globalData = {
  myRequest,
};
App({
  globalData,
})

我们封装了myRequest这个方法,将其放到全局变量globalData中,后续页面发起接口调用,可以用以下方式

// 获取应用实例
const app = getApp();
const globalApp = app.globalData;
 
globalApp.myRequest({
   url: '/plan/list',
   data: {
      needCardDetail: true
   }
}).then(res => {
   if (res && res.status == 200) {
      // 后续操作
   }
}).catch(err => {
    console.log(err);
});

小程序请求接口,必须通过微信提供的wx.request方法【传送门】

我们封装的myRequest方法,接收页面调用时的入参config对象

key 含义
url 接口地址
data 请求参数
header 请求头配置
method 请求方式
responseType 响应格式

config对象参数列表

看上述代码可知:
1.实际请求的url由globalConfig.envSet.requestUrl里配置的值 + 入参config中的url拼接得到。
header中默认有一个authorization,这是登录后服务端生成的token,存在前端缓存storage中。config中的header中也可以传入一些头信息,最终会合并在一起。
2.接口返回状态码决定了请求是否成功,这里的设定是,200为请求成功,401为登录信息失效,其他为失败。
3.myRequest方法最终返回一个Promise对象,接口请求成功,会通过resolve回调将接口返回值传递给调用方。请求失败,会通过reject回调将错误信息返回,并弹窗提示报错信息,在reqRes.data.message中。
4.在调用方视角,如果接口请求成功,会执行.then方法,如果请求失败,会弹窗出现错误信息,并执行.catch方法

总结

本篇文章介绍了小程序工程的基本搭建,但同时留了一个尾巴,就是小程序的自动登录。这个需要前后端一起配合实现,我将另开一篇文章详细展示。
关于本文各位有什么意见和建议,也欢迎评论区提出,么么哒(づ ̄ 3 ̄)づ

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 225,529评论 6 524
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,683评论 3 406
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,905评论 0 370
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,318评论 1 303
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,325评论 6 401
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,754评论 1 316
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,081评论 3 431
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 41,088评论 0 280
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,653评论 1 327
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,651评论 3 347
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,766评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,359评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,083评论 3 341
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,491评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,654评论 1 278
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,367评论 3 383
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,836评论 2 367

推荐阅读更多精彩内容