让微信小程序开发如鱼得水
<p><img src="http://img.wawow.xyz/imgs/wxmplogo.jpg"></p><p> 关于微信小程序开发一直想写一篇相关的文章总结和记录下,结果拖延症犯了迟迟没有下笔;这不最近天气不错,于是找一个空闲的下午将这篇文章输出下(好像跟天气没啥关系😓),那我们就开始吧!</p>
<p>注意:<strong>本文默认开发者对微信小程序开发有一定语法基础。</strong>小程序开发文档</p>
<h2 id="微信小程序小结">微信小程序小结</h2>
<p> 在接触的微信小程序开发过程中,不难发现微信小程序为了方便开发人员入手对很多底层api进行了很好的封装,比如针对接口请求的<code>wx.request()</code>,针对路由跳转和页面导航的<code>wx.switchTab、wx.navigateTo···</code>等。虽然在一定程度上简化了开发,但是对于项目工程的系统化构建还是不够的,因此本人在对比以前基于Vue开发项目的经验和自身的开发习惯,总结出如下3点可供参考:</p>
<ul>
<li>
<p>1、全局变量和配置信息统一管理;</p>
</li>
<li>
<p>2、封装路由守卫相关api:<code>vue-router</code>的<code>router.beforeEach()</code>和<code>router.afterEach()</code>真的香;</p>
</li>
<li>
<p>3、接口请求公共信息进一步提取封装;</p>
</li>
<li>
<p>4、封装接口的请求和响应拦截api:<code>axios</code>的<code>axios.interceptors.request.use()</code>和<code>axios.interceptors.response.use()</code>用过的都说好;</p>
</li>
</ul>
<p>从上述四点出发,对微信小程序初始化工程进行规范优化,能够很大程度提高开发效率和进行项目维护管理。封装的好处不只体现在调用的方便上,也体现在管理的方便上,同时,公共操作集中处理,很大程度减少繁杂重复代码。</p>
<h2 id="一项目初始化">一、项目初始化</h2>
<p> 新建微信小程序项目,在项目下新建如下目录和文件:</p>
<ul>
<li>config文件夹:统一管理可配置的信息和变量;
<ul>
<li>erroList.js:接口报错<code>错误码</code>匹配列表文件;</li>
<li>globalData.js:<code>全局变量</code>统一管理文件(相当于vuex);</li>
<li>keys.js:可配置系统信息管理文件(全局常量命名等);</li>
</ul>
</li>
<li>pages文件夹:小程序页面文件管理文件夹(每个页面一个子文件夹目录);</li>
<li>router文件夹:路由管理文件件;
<ul>
<li>router.js:对微信小程序<code>5种路由导航</code>api的封装;</li>
<li>routerConfig.js:页面路由名称和路径匹配配置文件;</li>
<li>routerFilter.js:路由前置<code>拦截</code>封装;</li>
</ul>
</li>
<li>servers文件件:接口请求服务管理文件夹;
<ul>
<li>apis文件夹:request请求封装管理和接口api配置管理文件夹;
<ul>
<li>request.js:对<code>wx.request</code>的<code>Promise</code>封装;</li>
<li>xxx.js:对应模块的接口管理文件;</li>
</ul>
</li>
<li>requestFilter.js:接口<code>请求和响应拦截</code>封装文件;</li>
</ul>
</li>
<li>其他都是初始化默认文件;</li>
</ul>
<p><img src="http://img.wawow.xyz/imgs/wxcatalog.png"></p>
<h2 id="二路由跳转和路由守卫封装">二、路由跳转和路由守卫封装</h2>
<h3 id="1路由跳转封装">1、路由跳转封装</h3>
<p> 微信小程序官方文档为开发者提供了5种路由跳转的api,每一种都有其特殊的用法:</p>
<p><img src="http://img.wawow.xyz/imgs/wxmporiginrouter.png"></p>
<p> 根据其用法,我们对路由api进行如下封装:微信小程序路由跳转最后对应<code>push、replace、pop、relaunch、switchTab</code>;<code>routes</code>对应routeConfig.js中路由路径的配置;<code>routerFilter</code>对应routerFilter.js文件,对路由跳转之前的逻辑进行处理;</p>
<h4 id="routeconfigjs每次新增页面后需要手动添加">routeConfig.js(每次新增页面后需要手动添加):</h4>
<pre><code class="language-javascript">export const routes =
{
INDEX: "/pages/index/index",
TEST: "/pages/test/test",
}
export default {...routes};
</code></pre>
<h4 id="routerfilterjs">routerFilter.js:</h4>
<pre><code class="language-javascript">export default () => {
···
//路由跳转前逻辑处理
}
</code></pre>
<h4 id="routerjsrouterfilter负责路由跳转前公共操作处理在success和fail中对路由跳转后的公共操作进行处理">router.js(routerFilter负责路由跳转前公共操作处理,在success和fail中对路由跳转后的公共操作进行处理):</h4>
<pre><code class="language-javascript">import routes from "../router/routerConfig";
import routerFilter from "./routerFilter"
/**
* 对wx.navigateTo的封装
* @param {路由} path
* @param {参数} params
* @param {事件} events
*/
const push = (path, params, events) => {
routerFilter()
wx.navigateTo({
url: routes + `?query=${JSON.stringify(params)}`,
events: events,
success(res) {
console.log(res);
},
fail(err) {
console.log(err);
}
})
}
/**
* 对wx.redirectTo的封装
* @param {路由} path
* @param {参数} params
*/
const replace = (path, params) => {
routerFilter()
wx.redirectTo({
url: routes + `?query=${JSON.stringify(params)}`,
success(res) {
console.log(res);
},
fail(err) {
console.log(err);
}
})
}
/**
* 对wx.navigateBack的封装
* @param {返回的层级} number
*/
const pop = (number) => {
routerFilter()
wx.navigateBack({
delta: number,
success(res) {
console.log(res);
},
fail(err) {
console.log(err);
}
})
}
/**
* 对wx.reLaunch的封装
* @param {路由} path
* @param {参数} params
*/
const relaunch = (path, params) => {
routerFilter()
wx.reLaunch({
url: routes + `?query=${JSON.stringify(params)}`,
success(res) {
console.log(res);
},
fail(err) {
console.log(err);
}
})
}
/**
* 对tabbar的封装
* @param {路由} path
*/
const switchTab = (path) => {
routerFilter()
wx.switchTab({
url: routes,
success(res) {
console.log(res);
},
fail(err) {
console.log(err);
}
})
}
module.exports = {
push,
replace,
pop,
relaunch,
switchTab
}
</code></pre>
<h3 id="2全局注册和使用">2、全局注册和使用</h3>
<p>在<code>app.js</code>中对封装的路由api进行全局注册:</p>
<pre><code class="language-javascript">import routerfrom "./router/router.js"
//全局注册
wx.router = router
</code></pre>
<p>在页面逻辑中使用:</p>
<pre><code class="language-javascript">//index页面跳转test页面
gotoTest(){
wx.router.push("TEST")
}
</code></pre>
<h2 id="三接口请求promise封装">三、接口请求Promise封装</h2>
<p> 对于同一个项目而言,微信小程序api<code>wx.request()</code>中很多参数都是相同的,如果直接使用,需要将这些重复参数一遍又一遍的copy,虽然copy很简单,但是当有一个参数改变了需要找到所有接口一个一个修改,维护起来费劲,再者看着也难受呀;</p>
<p><img src="http://img.wawow.xyz/imgs/wxmporiginrequest.png"></p>
<p> 借鉴<code>axios</code>对请求的封装,将<code>wx.request()</code>封装为<code>Promise</code>形式岂不美哉:</p>
<h4 id="requestjs">request.js:</h4>
<pre><code class="language-javascript">import formatError from "../requestFilter"
const app = getApp()
/**
* 接口请求封装
* @param {请求方式} method
* @param {请求的url} url
* @param {请求传递的数据} data
*/
const request = (method, url, data) => {
//设置请求头
const header = {
···
}
//promise封装一层,使得调用的时候直接用then和catch接收
return new Promise((resolve, reject) => {
wx.request({
method: method,
url: app.globalData.host + url, //完整的host
data: data,
header: header,
success(res) {
//对成功返回的请求进行数据管理和统一逻辑操作
···
resolve(res.data)
},
fail(err) {
wx.showToast({
title: '网络异常,稍后再试!',
mask: true,
icon: 'none',
duration: 3000
})
}
})
})
}
export default request;
</code></pre>
<h4 id="具体使用">具体使用</h4>
<p>以user.js为例:</p>
<pre><code class="language-javascript">import request from "./request";
// 获取用户openid
export const usrInfos = data => request("POST", "/user/usrInfos", data);
</code></pre>
<p>index页面调用:</p>
<pre><code class="language-javascript">//index.js
//获取应用实例
const app = getApp()
import { usrInfos } from "../../servers/apis/user"
Page({
onLoad: function () {
//获取用户信息
usrInfos({
uid: "xxxx"
})
.then(res => {
console.log(res)
})
.catch(err => {
console.log(err)
})
}
})
</code></pre>
<h2 id="四接口的请求和响应拦截封装">四、接口的请求和响应拦截封装</h2>
<p> <code>axios</code>的<code>axios.interceptors.request.use()</code>和<code>axios.interceptors.response.use()</code>分别对应接口请求前的拦截处理和数据响应后的拦截处理;根据这个原理我们对微信小程序的响应也做拦截封装,对接口请求返回错误进行统一管理输出:</p>
<h4 id="requestjs-1">request.js</h4>
<pre><code class="language-javascript">import formatError from "../requestFilter"
const app = getApp()
···
const request = (method, url, data) => {
···
return new Promise((resolve, reject) => {
wx.request({
···
success(res) {
//对成功返回的请求进行数据管理和统一逻辑操作
if(res.statusCode === 200){ //请求返回成功
if(res.data && res.data.code === "SUCCESS"){ //后端对接口请求处理成功,返回数据给接口调用处
resolve(res.data)//then接收
}else{ //后端对也请求判断后认为不合逻辑报错
formatError(res) //统一的报错处理逻辑
reject(res.data) //catch接收
}
}else{
reject(res.data) //catch接收
}
},
fail(err) { //请求不通报错
wx.showToast({
title: '网络异常,稍后再试!',
mask: true,
icon: 'none',
duration: 3000
})
}
})
})
}
export default request;
</code></pre>
<h4 id="requestfilterjs">requestFilter.js</h4>
<p>requestFilter.js中可以做很多对报错的处理,这里用一个简单的toast处理示范下:</p>
<pre><code class="language-javascript">/**
* 对接口返回的后端错误进行格式转化
* @param {接口成功返回的数据} res
*/
const formatError = (err =>{
wx.showToast({
title: err.message,
mask: false,
icon: 'none',
duration: 3000
})
}
export default formatError;
</code></pre>
<p>对报错进行统一处理需要明确数据规:</p>
<ul>
<li>制定统一的报错码管理规范;</li>
<li>制定前后端统一的接口请求数据返回格式;</li>
</ul>
<h2 id="五全局数据管理">五、全局数据管理</h2>
<p> 对于数据的管理在小项目的开发中显得不那么重要,但是随着项目越来越大,数据越来越多,一个很好的数据管理方案能够有效地避免很多bug,这也是vuex能够在vue生态中占有一席之地的原因。秉承着合理管理数据的原则,对于该封装的数据坚决封装,对于该分模块管理的配置坚决分块管理:</p>
<h4 id="globaldatajs">globalData.js</h4>
<p>微信小程序中全局的数据管理放在<code>app.js</code>的<code>globalData</code>属性中,当数据太多或者app.js逻辑太复杂时,将全局数据提取出来单独管理的确是个好方案:</p>
<pre><code class="language-javascript">export default {
···
host: "http://www.wawow.xyz/api/test", //接口请求的域名和接口前缀
hasConfirm: "" //是否已经有了confirm实例
currentPage: ""
···
}
</code></pre>
<h4 id="keysjs">keys.js</h4>
<p>keys.js属于个人开发中的习惯操作,将项目中可能用到的一些常量名称在此集中管理起来,十分方便调用和修改维护:</p>
<pre><code class="language-javascript">export default {
···
TOKEN: "token",
STORAGEITEM: "test"
···
}
</code></pre>
<h4 id="全局引用和注册">全局引用和注册</h4>
<p>引入app.js:</p>
<pre><code class="language-javascript">import routerfrom "./router/router.js"
import keys from "./config/keys"
import globalData from "./config/globalData"
//全局注册
wx.router = router
wx.$KEYS = keys
//app.js
App({
//监听小程序初始化
onLaunch(options) {
//获取小程序初始进入的页面信息
let launchInfos = wx.getLaunchOptionsSync()
//将当前页面路由存入全局的数据管理中
this.globalData.currentPage = launchInfos.path
},
···
//全局数据存储
globalData: globalData
})
</code></pre>
<h4 id="使用">使用</h4>
<p>在页面代码逻辑中可以通过<code>app.globalData.host</code>,<code>wx.$KEYS.TOKEN</code>方式进行调用;</p>
<h2 id="六总结">六、总结</h2>
<p> 上述关于微信小程序开发的几个方面都是在实践中学习和总结的,技术层面的实现其实很容易,但是个人觉得开发规范项目工程构建才是一个项目的重要基础;完善的规范能够有效的提高开发效率和开发者之间非必要的<code>扯皮</code>!合理的项目工程构建能够优化开发逻辑,提高代码逻辑易读性,减少后期项目的管理时间,同时给予项目更大的扩展性。</p>
<p> 有需要源码的可以关注微信公众号 <strong>哇喔WEB</strong> 回复 "wxmp"获取;</p>
<p> 欢迎大家讨论留言、进行补充!</p><br><br>
来源:https://www.cnblogs.com/wawoweb/p/14259206.html
頁:
[1]