改头换面:迁移既有项目到微信小程序

2020-06-15 15:10:00 浏览数 (1)

小程序基础请参考之前的文章: 一个简单的微信小程序DEMO

在既有的某html5移动端项目基础上,考虑到其形态和体量很适合转化为微信小程序,遂花费了不长的时间撸起袖子试试看,并将期间遇到的踩坑心得记录在此

目录

  • 复用的mock
  • 素材图片处理
  • 样式的转化
  • 小程序中几种不同的跳转
  • 小失所望的兼容性
  • 差强人意的小程序“组件”
  • 在小程序中实现表格

1. 复用的mock

之前的项目中使用express模拟前后端通信数据(mock),对其稍加改造,就可以在保留对原项目的支持的同时,满足小程序的测试了

1.1 识别请求来源为小程序

代码语言:javascript复制
//小程序中的每次请求
wx.request({
     url: `http://127.0.0.1:3201${url}`,
     data: assign(
       data,
       {
         _from_weapp: 1, //添加特殊标记,以作区分
         code: _globalData['wx_code'],
         openid: wx.getStorageSync('openid')
       }
     ),
     
     ...
});
代码语言:javascript复制
//mock中的过滤器
app.all('/api/*', function(req, res, next) {     //判断来源是不是小程序
   const IS_WEAPP = req.method == 'GET'
       ? req.query._from_weapp == 1
       : req.body._from_weapp == 1;
       
       ...    
});

1.2 登录态和权限管理

  • 服务器端通过用户唯一标识openid识别用户
  • 小程序通过api获得code,传递给服务器换取并缓存openid
  • 每次请求都携带openid
  • 登出或超时后服务器在响应中返回状态码401触发重新登录

1.3 跨域ajax设置

代码语言:javascript复制
app.all('/api/*', function(req, res, next) {  
   res.header("Access-Control-Allow-Origin", "*");
   res.header("Access-Control-Allow-Credentials", true);
   res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");  
   res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");  
   res.header("Content-Type", "application/json;charset=utf-8");  
   next();  
});
  • 要允许小程序跨域访问,服务器端应做必要的设置
  • 最主要的响应头Access-Control-Allow-Origin,在实际测试时不能为通配符 *,应该写为小程序实际发起请求的域名,也就是https://servicewechat.com

2. 素材图片处理

和之前的经验相比,小程序中的素材图片需要考虑以下几点

  • 微信小程序限制总体积,一些资源宜改为远端读取
  • 样式表中直接引用的图片要求绝对路径,对于更改域名等调试操作不便

因此,基本的运用原则就是:最小化使用素材图片,并且除了系统 tabBar 等处用到的图标等需要把图片文件打包到发布结构中,其他素材图片尽量 base64 转码后放入 wxss

代码语言:javascript复制
/*在wxcc中的引用*/
.figure {
 width: 27rpx;
 height: 25rpx;
 display: inline-block;
 background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAAZCAYAAADAHFVeAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1 /AAAABd0RVh0Q3JlYXRpb24gVGltZQAyMDE3LjUuMjKXkY//AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAAgVJREFUSImtldGRokAQhj te9cQyED22alamAlgyWDZDAxhjeC8CGQjWBKA5YF7381AM8AIvIdpcJxDC8WpskBo/q b buZ4K2yqANgBYRADmTaqMaPG7qOx2N3PvVAKfANJHLpN1CVRT2/F auDiagLVABoTYqAp6AQIDhQ2AitAUOwAtSmTbqG4geBWwr2wB7Ef0BtlKpCwR4HwWT/XgG3sUI0QVgDozau19Y14E1BtqopizqCMgEiMS8AusxsIkIf2mjJv7NsqgzgQB8aKPSWwG91pf 8lclx/U9IH9NgZ2c9zmtTSAfCwKYaqN2WCemPfczbDs8rs9E9MUXlEQi fvQPjsAmT avD4bBewcWBZ1Anxieyzyh68YKMfuYyRJ4MWkcr/RRm3g3I2TnuDtFeAc69AAWGmjMudehm2TAzBrNWK97DTOpr48/AYs6Jn2zoTZ4UwYB7TWRs1dja/yb6fxXyPfWOFCYhZYR4dtrGhsJLEk1svdWWV3VPgBNMAf7NzsYkUjwr7y/GJlQyv0YkNOE6czkKMR91Y2tEIv9lKLdAlerczJus3u1goT7L4FQDAI5gH3QKqNqq7EtsCZXHqL9TIbDBORCLvZM6w5cm1U7sXMOVU0a0Ew8DX2iK3k12b w2lvnuW4B9JYL6v22ZthHjjBfppCrPUb7Bc/b93ojqt/8akRua fVJwAAAAASUVORK5CYII=');
 background-position: 0 50%;
 background-repeat: no-repeat;
 background-size: contain;
 margin-right: 12rpx;
}
代码语言:javascript复制
//转码的脚本
const base64Img = require('base64-img');
const imgs = Array.from(process.argv).slice(2);console.log(imgs.map(imgPath=>{
   let rst = imgPath   'n-------------------n';
   rst  = base64Img.base64Sync(imgPath);
   rst  = 'nn';
   return rst;
}).join('n'));

3. 样式的转化

由于之前的项目以较合理的方式运用了 rem 尺寸,稍加改造就可以方便的转化到小程序要求的 rpx

代码语言:javascript复制
/*既有项目中的LESS预处理*/@remUIWidth: 720; /*之前的设计图尺寸*/.px2rem(@px, @name: font-size){
   @{name}: unit(@px, px);
   @{name}: unit(@px / (@remUIWidth / 20), rem);
}....logout {
   .px2rem(130px, margin-left);
}
代码语言:javascript复制
/*编译结果*/
.logout {
 margin-left: 130px;
 margin-left: 3.61111111rem;
}

改为小程序的 rpx 格式,并依旧用 Less 预处理:

代码语言:javascript复制
@WEAPP-WIDTH: 750;
@ui-width: 720;.px2rpx(@px, @name: font-size){
 @{name}: unit(@px, px);
 @{name}: unit( floor(@px * @WEAPP-WIDTH / @ui-width), rpx);
}....logout {
   .px2rpx(130px, margin-left);
}
代码语言:javascript复制
/*编译结果*/
.logout {
 margin-left: 130px;
 margin-left: 135rpx;
}

4. 小程序中几种不同的跳转

小程序现在并不允许外链,但即使是应用内的跳转,却也分出了好几种不同的方式,即便不爽还是必须了解的:

  • wx.navigateTo() 保留当前页面,跳转到应用内的某个非 tabBar 的页面
  • wx.redirectTo() 关闭当前页面,跳转到应用内的某个非 tabBar 的页面
  • wx.switchTab() 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面
  • wx.navigateBack() 关闭当前页面,返回上一页面或多级页面
  • wx.reLaunch() 关闭所有页面,打开到应用内的某个页面

<navigator /> 组件中有一个 open-type 属性,分别对应以上类型:

  • navigate 对应 wx.navigateTo 的功能
  • redirect 对应 wx.redirectTo 的功能
  • switchTab 对应 wx.switchTab 的功能
  • reLaunch 对应 wx.reLaunch 的功能
  • navigateBack 对应 wx.navigateBack 的功能

5. 小失所望的兼容性

小程序虽然已经出台多时,但在很多方面都在不断改变和完善,es6和css的语法方面,可以通过“开启 ES6 转 ES5 ”和“开启 上传代码时样式文件自动补全”来解决,但一些所谓新API,还是要手动判断处理:

旧版本微信客户端不支持 showLoading() / hideLoading()

代码语言:javascript复制
const hide_loading = () => {
 if (wx.hideLoading) {
   wx.hideLoading()
 } else {
   wx.hideToast()
 }
};

旧版本微信客户端不支持 reLaunch,分情况用不同方法代替

代码语言:javascript复制
if (wx.reLaunch)
   wx.reLaunch({
     url: rst.route
   })
else
   wx.switchTab({
     url: rst.route,
   })

某些ios客户端中,程序甫一启动时,立即调用 reLaunch() 会报错,需要延时处理

代码语言:javascript复制
if (res.statusCode == 401) {
 setTimeout(function() {
   if (wx.reLaunch)
     wx.reLaunch({
       url: '/pages/login/login'
     });
   else
     wx.redirectTo({
       url: '/pages/login/login'
     })
 }, 500);
 return;
}

兼容处理 <navigator /> 组件的 reLaunch 跳转

该例因为是不同参数的本页跳转,在旧版客户端中尝试了几种旧有属性值都无法实现,故特殊处理:

代码语言:javascript复制
<navigator wx:if="{{ tabs.current !== tab.key }}"
   url="{{ tabs.links[idx] }}"
   data-url="{{ tabs.links[idx] }}"
   open-type="{{ tabs.openType }}"
   catchtap="onTabClick">{{ tab.label }}</navigator>
代码语言:javascript复制
const openType = wx.reLaunch ? 'reLaunch' : 'navigate';...//在 onLoad(opts) 中
this.setData({
   tabs: {
       openType,
       current: opts.key,
       tabs: result.tabs,
       links: result.tabs.map(
           tab =>`/pages/path?time=${opts.time}&key=${tab.key}`
       )
   },
   ...
});...onTabClick(evt) {
   if (openType === 'reLaunch') return;
   const {url} = evt.currentTarget.dataset;
   const [m, time, key] = url.match(/time=(.*?)&key=(.*?)$/);
   this.onLoad({time, key});
}

其他一般的兼容处理方法见官方文档: https://mp.weixin.qq.com/debug/wxadoc/dev/framework/compatibility.html

6. 差强人意的小程序“组件”

小程序当前并不支持原生的“组件”概念,只能用 js / wxml / wxss 中可用的模块化方法变通替代实现

对于较简单的可复用组件,如果没有较复杂的逻辑,只考虑模版和样式即可:

代码语言:javascript复制
<!-- tabs.wxml -->
<template name="tabs">
 <view class="top-tabs">
   <block wx:for="{{ tabs.tabs }}" wx:for-item="tab" wx:for-index="idx" wx:key="key">
     ...
   </block>
 </view>
</template><!-- rank.wxml -->
<import src="tabs.wxml" />
...
<template is="tabs" data="{{ tabs }}" />
代码语言:javascript复制
/* tabs.wxss */
.scope {
 overflow: hidden;
}
.top-tabs {
 ...
}/* rank.wxss */
@import './tabs.wxss';
...

7. 在小程序中实现表格

小程序提供的组件中并不包含tabletheadtr等,但是用css属性和<view />组件旧可以完美模拟一个文首展示的表格

代码语言:javascript复制
/*table by <view>*/
.table {
   border-collapse:collapse;
   border-spacing:0;
   width:100%;
   display: table;
}
.table .thead {
 display: table-header-group;
}
.table .tbody {
 display: table-row-group;
}
.table .tr {
 display: table-row;
}
.table .th,
.table .td {
 display: table-cell;
}

* 原创文章转载请注明出处

0 人点赞