小程序的项目架构

Nam form Pixiv

目前项目用到的小程序项目架构,比较特殊的是store和behaviors,有个清晰的目录架构可以加快开发的效率,便于维护


小程序的项目架构

behavior

存放小程序需要用到的混合文件,类似vue的mixin

// page-a.js
// var myBehavior = require('./my-behavior.js')
// Page({
//   behaviors: [myBehavior],
//   onLoad: function() {
//     this.data.sharedText === 'This is a piece of data shared between pages.'
//   }
// })
const app = getApp()
module.exports = Behavior({
  data: {
    userInfo: null,
    hasUserInfo: false,
    canIUse: wx.canIUse('button.open-type.getUserInfo')
  },
  methods: {
    // 1.通过button来授权获取 用户信息
    getUserInfo: function(e) {
      console.log('--getUserInfo--')
      app.globalData.userInfo = e.detail.userInfo
      this.setData({
        userInfo: e.detail.userInfo,
        hasUserInfo: true
      })
    },

    // 2.通过静默授权的方式来获取 用户信息(为了兼容旧版本)
    silentGetUserInfo: function() {
      return new Promise((resolve, reject) => {
        console.log('--silentGetUserInfo--', app.globalData.userInfo)
        if (app.globalData.userInfo) {
          this.setData({
            userInfo: app.globalData.userInfo,
            hasUserInfo: true
          })
          // 已登录
          resolve(app.globalData.userInfo)
        } else if (this.data.canIUse) {
          // 获取用户信息 ( 如果授权过以后直接获取用户信息 )
          wx.getSetting({
            success: res => {
              console.log('scope.userInfo=', res.authSetting['scope.userInfo'])
              if (res.authSetting['scope.userInfo']) {
                // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
                wx.getUserInfo({
                  success: res => {
                    // 可以将 res 发送给后台解码出 unionId
                    app.globalData.userInfo = res.userInfo
                    console.log('res.userInfo', res.userInfo)
                    // 已登录
                    resolve(res.userInfo)
                  },
                  fail: () => {
                    // 没登录
                    reject(null)
                  }
                })
              } else {
                // 没登录
                reject(null)
              }
            },
            fail: () => {
              // 没登录
              reject(null)
            }
          })
        } else {
          // 在没有 open-type=getUserInfo 版本的兼容处理
          wx.getUserInfo({
            success: res => {
              app.globalData.userInfo = res.userInfo
              this.setData({
                userInfo: res.userInfo,
                hasUserInfo: true
              })
              // 已登录
              resolve(res.userInfo)
            },
            fail: () => {
              // 没登录
              reject(null)
            }
          })
        }
      })
    },
    // 3.退出登录
    loginOut() {
      // 清除page的登录的数据
      this.setData({
        userInfo: null,
        hasUserInfo: false,
        isLogin: false
      })
      // 清除app的登录数据
      app.globalData.userInfo = null
    }
  }
})

config

用于定义环境与接口地址,方便切换与维护

// config.dev.js
// 开发环境
module.exports = {
  isDebug: true,
  baseUrl: 'http://192.168.1.120',
  storageUrl: 'http://192.168.1.120'
}

// index.js
// 1.开发环境
// const conf = require('./config.dev.js')
// 1.测试环境
const conf = require('./config.pro.js')
// 1.正式环境
// const conf = require('./config.real.js')

module.exports = {
  isDebug: conf.isDebug,
  baseUrl: conf.baseUrl,
  storageUrl: conf.storageUrl
}

service

用于存放接口地址

var http = require('../../utils/http.js')

const api_report = {
  foo: '/aaa, 
  boo: '/bbb'
}

const reportList = (payload) => {
  return http.post(api_report.foo, payload) // 返回的是 promise
}
const reportSubmit = (payload) => {
  return http.post(api_report.boo, payload) // 返回的是 promise
}
// 对外暴露
module.exports = {
  reportList,
  reportSubmit
}

store

类似vue的vuex作用,用于存储全局变量

// store.js
import { observable, action } from 'mobx-miniprogram'

export const store = observable({

  // 数据字段( 相当于data的fields )
  numA: 1,
  numB: 2,

  // 计算属性( 相当于data的fields )
  get sum() {
    return this.numA + this.numB
  },

  // actions
  update: action(function() {
    const sum = this.sum
    this.numA = this.numB
    this.numB = sum
  })

})

util

网络方面:需要注意的是小程序的登录功能因为需要存储token和用户信息,而用户信息是存在小程序的Storage中,在http中会处理到用户的信息

utils.js

// rpx 转换为 px
const rpxTopx  =(rpx)=>{
  let deviceWidth = wx.getSystemInfoSync().windowWidth; //获取设备屏幕宽度
  let px = (deviceWidth/750)*Number(rpx);
  return px;
}
// px 转换为 rpx ,传参类型是数字(Number)
const pxTorpx = (px) => {
    let deviceWidth = wx.getSystemInfoSync().windowWidth;   //获取设备屏幕宽度
    let rpx = (750 / Number(px)) * deviceWidth;
    return rpx;
}
const getAuthToken = () => {
  return wx.getStorageSync('session_key')
}

// 清除登录的数据(退出登录)
const loginOut = () => {
  setAuthToken(null)
}
module.exports = {
  rpxTopx,
  pxTorpx,
  getAuthToken,
  loginOut 
}

http.js

// const baseUrl = '' // 基本路径
const config = require('../config/index')
const utils = require('../utils/util.js')
// 请求方法
function request(url, method, data, header, hasLoading = true) {
  data = data || {}
  header = header || {}

  // // 判断 是否有登录 token
  const auth_token = utils.getAuthToken() // 同步
  // 对接时候写死token
  if (auth_token) {
    header['auth_token'] = auth_token
  } else {
    // 测试 auth_token
    header['auth_token'] = 'abc'
  }

  // 加载动画
  if (hasLoading) {
    wx.showLoading({
      title: '加载中'
    })
  }

  // 创建 promise 对象
  const promise = new Promise((resolve, reject) => {
    wx.request({
      url: config.baseUrl + url, // 地址
      header: header,
      data: data,
      method: method,
      success: function(res) {
        console.log( res)
        if (hasLoading) {
          wx.hideLoading() // 隐藏加载动画
        }
        // 判断是否成功(res的数据需要看接口返回的数据做判断)
        if (typeof res.data === 'object') {
          // console.log(res)
          if (res.data.code === -1 || res.data.code === '1001') {
            // 判断是否有登录 请求未登录
            // {code: -1
            //  data: "401"
            //  msg: "未登录"}
            if (res.data.data === '401') {
              // 清除登录数据,重新登录
              utils.loginOut()
              setTimeout(() => {
                // 跳转到登录页面
                wx.redirectTo({
                  url: '/pages/mine/mine' //  跳转登录页
                })
              }, 500)
              wx.showToast({
                title: '请重新登录!',
                icon: 'none',
                duration: 2000
              })
              reject(res)
            // 请求失败
            } else {
              wx.showToast({
                title: res.data.msg || '请求失败',
                icon: 'none',
                duration: 2000
              })
              reject(res)
            }
          } else if (res.data.code === 200 || res.data.code === 0) {
            resolve(res)
          } else {
            reject(res)
          }
        } else {
          reject(res)
        }
      },

      // 接口调用失败的回调函数
      fail: function(res) {
        console.log('x=', config.baseUrl + url)
        if (hasLoading) {
          wx.hideLoading()
        }
        wx.showToast({
          title: '网络错误!',
          icon: 'none',
          duration: 2000
        })
        reject(res)
      }

    })
  })
  return promise
}

// 对外暴露
module.exports = {
  get: function(url, data, header) {
    return request(url, 'GET', data, header)
  },
  post: function(url, data, header) {
    return request(url, 'POST', data, header)
  }
}

template

小程序提供模板功能,可自定义几个"暂无数据"的模板进行复用

// 使用模板
<import src="../../templates/no-data/no-data.wxml"/>
<!-- 没有数据 -->
<view
   class="report-list-page no-data"
   wx:if="{{dataList.length === 0}}">
     <template is="noData"></template>
</view>

// 定义模板
<!--templates/no-login/no-data.wxml-->
<template name="noData">
  <view style="text-align: center;">
    <image 
      src="../../images/mine/no-find-bg.png"
      style="width:506rpx;height:239rpx;margin-top:107rpx"
    >
    </image>
    <view style="color:#8E919C;font-size:28rpx">暂无数据</view>
  </view>
</template>

其它

小程序有个坑点需要注意,就是css中background-image引用图片时会无法显示,需要把图片转为base64,这个转换可以在onload生命周期上进行

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

推荐阅读更多精彩内容