小程序开发前的准备工作

2,793 阅读1分钟

磨刀不误砍柴工,在开发之前做好准备工作可以大大提升开发效率、减少冗余代码,这篇文章结合自己做过的几个小程序的经验做一个总结【demo地址】。


相关参考 vant-weapp武装你的小程序——开发流程指南

着手开发小程序前都有哪些工作要准备?

  • 重写小程序Page、Component函数
  • Request方法封装
  • Router路由封装
  • Less的使用
  • Util的封装

为什么要重写Page、Component函数?

准备开发页面之前你是否经历过这样...

import store from "../utils/store";
import util from "../utils/util";
import fetch from "../utils/fetch";
import config from "../utils/config";

Page ({
  // ...  
})

每个页面都要复制粘贴 我@#$%^&*


以下办法可能会解决这些问题 并且相当舒适

  1. 创建Init.js 并引入到app.js里, 像这样⬇️
require('./utils/Init.js');

App({
  onLaunch: function () {}
})
  1. 重写Page、Component函数

函数重写思考:小程序属于单页面应用,全局的页面组件注册全依靠Page、Component函数,实际上就是调用了一个函数 传入了一个对象,那我们能不能在函数调用前,对参数做一些小动作呢?

先来试验一下

编辑Init.js

// 函数劫持 重写注册函数
let originPage = Page;
Page = (opt) => {
    // 在传入Page函数的参数中 添加一个util对象
    opt.util = {
        test () {
            return 1;
        }
    }
    return originPage(opt);
}

在页面onLoad方法中测试一下

Page({
  data: {
  },
  onLoad: function () {
    console.log(this.util.test())
  }
})

结果输出如下

输出结果

运行成功!接下来你应该懂我的意思 塞他!

编辑Init.js

import _store from "./store";
import _util from "./util";
import _fetch from "./fetch";

let originPage = Page;
Page = (opt) => {
    // 把创建好的工具类引入opt参数内
    opt = {
        ...opt,
        _store,
        _util,
        _fetch
    }
    return originPage(opt);
}

然后在页面中输出一下this关键字

嗯...终于不用引乱七八糟的东西了

注意!Component函数中如果也这么写 组件实例中并不会出现添加的对象 像这样 ⬇️

// Init.js
import _store from "./store";
import _util from "./util";
import _fetch from "./fetch";

let originComponent = Component;
Component = (opt) => {
    opt.util = {
        test() {
            return 1
        }
    }
    return originComponent(opt);
}

------------
// components/img/index.js
Component({
  attached () {
    this.test();
  },
  methods: {
    test() {
      console.log(this.util)
    }
  }
})

运行结果

但这就不代表我没有办法

编辑init.js 重写component部分

Component = (opt) => {
  // Component函数的options无法直接插入参数 只能在某个生命周期函数被调用时动态赋值
  let originAttached = opt.attached || function () {};
  opt.attached = function(e) {
    this.util = {
      a() {
        return 1;
      }
    };
    return originAttached.apply(this);
  }
  return originComponent(opt)
}

运行结果

最终把下面的各种工具类添加上之后 简单写个请求、跳转 大概就是这个样子⬇️

Page({
  data: {
  },
  onLoad: function ({ goodId }) {
    this._fetch({
      url: "getGoods",
      data: { goodId }
    }).then(res => {
      if (res.length) {
        this._router.go("detail", { firstGoodsId: res[0].id })
      }
    })
  }
})


以下是工作中各种用到的封装

Request方法封装

wx.request方法封装点:请求状态、错误统一处理,以当前上下文可控制页面所有需要请求状态的组件

Fetch.js

const host = {
  Dev: "http://test.com"
}

const api = {
  getUserInfo: "...",
  getGoods: "..."
}

export default function ({ url, data, showLoading = false }) {
  let self = this;
  changeFetchState(self, true);
  showLoading && wx.showLoading({ title: showLoading })
  return new Promise((resolve, reject) => {
    const options = {
      url: host.Dev + api[url],
      method: "POST",
      header: { "content-type": "application/json" },
      data,
      success: ({ data }) => {
        resolve(data.data, data);
      },
      fail: err => {
        reject(err);
      },
      complete() {
        changeFetchState(self, false);
        showLoading && wx.hideLoading();
      }
    };

    wx.request(options);
  })
}
// 以当前作用域调用,可控制页面需要请求状态的组件
function changeFetchState (self, state) {
  self && self.setData({ _fetchState: state });
}

Router路由封装

规范路由管理、传参,以{key:value}形式定义路由,重新封装路由跳转方法,方便调用。

Router.js

// 路由定义
const routePath = {
  "index": "/pages/index/index",
  "detail": "/pages/detail/index",
  "service": "/pages/service/index"
};
// tabbar名单 特殊处理
const tabbar = ['index', 'service']

const Router = {
  // 参数转换
  parse: function (data) {
    if (!data) return '';
    let tempArr = [];
    for (let key in data) {
      tempArr.push(`${key}=${encodeURIComponent(data[key])`);
    }
    return '?' + tempArr.join('&');
  },
  go: function (path = 'index', params = null, duration = 0) {
    setTimeout(() => {
      const isTabbar = tabbar.indexOf(path) == -1;
      // 如果是tabbar用switchTab方法切换
      wx[isTabbar ? 'navigateTo' : 'switchTab']({
        url: routePath[path] + this.parse(params),
      })
    }, duration * 1000);
  },
  goBack: function (delta = 1, duration) {
    setTimeout(() => {
      wx.navigateBack({ delta })
    }, duration * 1000)
  }
}

export default Router;

Less的使用

首先全局安装less插件 npm install less -g

步骤1
WebStorm -> Preferences
Tools -> File Watchers -> 新建监听文件选择Less -> 【重点】设置Output文件格式为wxss


Util通用类封装

自己常用的工具大概有这几种:Storage存储、页面地址取参、获取当前上下文等等,通常Util一下也想不全都是在开发中边用边写,以下几个为例子。

Util.js

// Storage相关
function getStore (name1, name2) {
  if (!name1) return false;
  let baseStore = wx.getStorageSync(name1);
  if (!baseStore) return false;
  return name2 ? (baseStore[name2] || '') : baseStore;
}
function setStore (name, value) {
  if (!name) return false;
  return wx.setStorageSync(name, value);
}
function setStoreChild (name1, name2, value) {
  if (!name1 || !name2) return false;
  let baseStore = getStore(name1) || {};
  baseStore[name2] = value;
  return setStore(name1, baseStore);
}
/**
 * 获取数据类型
 * @param value
 * @returns {*}
 */
function getValueType(value) {
  if (typeof value === 'number') return Number;
  if (typeof value === 'string') return String;
  if (typeof value === 'boolean') return Boolean;

  if (value instanceof Object && !value instanceof Array) return Object;
  if (value instanceof Array) return Array;

  return null;
}
/**
 * 获取当前页面上下文
 * @returns {*}
 */
function getPageContext() {
  var pages = getCurrentPages();
  return pages[pages.length - 1];
}
/**
 * 获取元素
 * @param classItem
 */
function $select(className, cb) {
  const query = wx.createSelectorQuery().in(this)
  query.select(className).boundingClientRect()
  query.selectViewport().scrollOffset()
  query.exec(function (res) {
    cb(className.substr('0') === '#' ? res[0] : res);
  })
}

module.exports = {
  getStore,
  setStore,
  setStoreChild,
  getValueType,
  getPageContext,
  $select
}