使用eggjs+微信小程序开发一个时间管理小程序(三)——前端项目搭建

目录

系列文章目录

本章要点

一、搭建小程序

1. 注册小程序

2. 创建一个小程序工程

3. 调整目录结构

二、tabbar配置

三、导航栏配置

1. 默认配置

2. 自定义

3. page-meta + navigation-bar

四、小程序接口封装 

1. 全局配置      

2. 封装请求接口方法

总结


系列文章目录

使用eggjs+微信小程序开发一个时间管理小程序(一)——项目介绍

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

使用eggjs+微信小程序开发一个时间管理小程序(三)——前端项目搭建

使用eggjs+微信小程序开发一个时间管理小程序(四)——自动登录实现

本章要点

  1. 从头搭建一个微信小程序
  2. 导航栏和tabbar的配置和优化
  3. 实现接口封装
  4. 实现自动登录

一、搭建小程序

1. 注册小程序

微信公众平台上注册小程序

小程序注册页面


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

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


2. 创建一个小程序工程

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

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

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

创建项目弹窗


      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、导航栏的配置,也是在这里。具体内容可以看微信小程序开发官方文档,写的很清楚。

  • 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组件。具体方案可参考文档 [传送门]

三、导航栏配置

      有三种导航栏实现方案

  1. 默认配置
  2. 自定义
  3. 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对象

config对象参数列表
key意义
url接口地址
data请求参数
header请求头配置

method

请求方式

responseType

响应格式

      看上述代码可知:

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

总结

      本篇文章介绍了小程序工程的基本搭建,但同时留了一个尾巴,就是小程序的自动登录。这个需要前后端一起配合实现,我将另开一篇文章详细展示。

      关于本文各位有什么意见和建议,也欢迎评论区提出,么么哒(づ ̄ 3 ̄)づ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值