小程序webview跳转页面后没有返回按钮完美解决方案

随着小程序越来越火爆,使一个产品如果只有公众号H5页面APP显得不怎么完美,总感觉不搭上小程序这趟流量车,就会少了点什么,心里别扭地很。在此驱动下,我所在公司也决定赶紧上车。

但是,如果要按照小程序的套路重新写一份的话,又感觉付出的时间成本太大了,非常的不划算。于是乎,脑子灵光一闪,想起了小程序貌似有个小弟叫webviewwebview是啥,按字面意思就是网页视图(来自百度翻译)。官方是这么定义的:

web-view 组件是一个可以用来承载网页的容器,会自动铺满整个小程序页面。个人类型与海外类型的小程序暂不支持使用。

简单的讲就是我们可以在小程序内放置一个<web-view>组件来链接我们的HTML页面了。也就是说,已经写好的微信公众号H5页面可以直接挪到小程序了。一行代码就完成公众号到小程序的转移,一行代码就让我们多了一个流量入口,一行代码就解决了重新开发一套小程序代码的难题。想到这真的是激动死了。

当我兴致冲冲的把链接放进webview中,就像这样:(真实项目链接地址,用自己的域名代替了^_^)

<!--微信小程序index.wxml-->
<web-view src="https://www.hxkj.vip/"></web-view>

但是点击跳转页面的时候。唉哟,我去,我当时就真的楞了一下,页面左上角没有!!返回按钮!!导致回不去了,只能不停的往下点,没有回头路,这不是搞笑的吗。。。

当时想了一种方案,就是在每个H5页面都加上一个带返回按钮的导航栏,但是想了一下,这种方案不可取,因为小程序的头部已经有个导航栏了,再加一个的话就显得非常的不协调,可以用一个字来形容——【丑到爆】。果断弃用!

看了下其他的小程序,发现如果是两个小程序页面跳转的话,第二个页面的左上角是会有返回按钮的。那我们可以想到,如果在第二个页面也放个webview组件,用来显示跳转之后的链接,不就完美解决了吗?那么问题来了,怎么把主页点击的链接传到第二个页面呢?

我们可以在H5页面使用jssdkH5页面跳转到小程序页面的方法wx.miniProgram.navigateTo,然后再携带一个weburl参数:

//H5页面js
navigate(modelName) { //控制页面跳转---小程序、公众号、非微信跳转方式 【modelName---vue路由名字】
      this.isMiniProgram((res)=>{//判断是否是小程序页面的回调函数
        if (res) {//小程序页面
             wx.miniProgram.navigateTo({url: '../webview/webview?weburl=https://www.hxkj.vip/#/' + modelName});
        } else {
             this.$router.push({name: modelName});//非小程序页面使用vue路由跳转
        }
      })
},
isMiniProgram(callback) { //是否为小程序环境
      //是否在微信环境
      if (!isWeixin()) {
          callback(false);
      } else {
           //微信API获取当前运行环境
           wx.miniProgram.getEnv((res) => {
               if (res.miniprogram) {//小程序环境
                   callback(true);
               } else {
                   callback(false);
               }
          })
      }
}

接着在小程序第二个页面webview.js中接收weburl参数,注意配置分享的页面链接:

// pages/webview/webview.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    url:''
  },
  
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    this.setData({
      url: options.weburl //获取H5页面传递过来的weburl
    });
  },

  /**
   * 用户点击右上角分享***【特别注意这里,配置分享链接一定要给首页链接,要不然别人点进来又是没有返回按钮的哦】***
   */
  onShareAppMessage: function () {
    return {
      path: '/pages/index/index'
    }
  }
})

并赋值给webview.wxml中的src属性

<!--pages/webview/webview.wxml-->
<web-view src="{{ url }}"></web-view>
好了,这样的话,就完美解决了小程序webview跳转页面后没有返回按钮的问题。

作者:_TSY_
链接:https://www.jianshu.com/p/a7bb1a826548
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

微信小程序反编译 wxss 丢失问题

鉴于同事执行了删除代码的疯狂操作,并多次覆盖后,硬盘恢复文件的几率几乎为零,只能另辟蹊跷,所幸之前看到过微信小程序反编译的一篇文章。通过简单的搜索,就找到了相关的文章。根据文章所写,其他的都还算顺利,但是样式文件就迟迟弄不出来。

微信小程序反编译可以参考 https://github.com/qwerty472123/wxappUnpacker

0x1

执行wxss反编译程序后获得如下错误:

Guess wxss(first turn)...

/Users/aimuz/workspace/web/wxappUnpacker/node_modules/vm2/lib/main.js:214
            throw this._internal.Decontextify.value(e);
            ^
ReferenceError: $gwx is not defined
    at vm.js:3:3
    at Script.runInContext (vm.js:134:20)
    at VM.run (/Users/aimuz/workspace/web/wxappUnpacker/node_modules/vm2/lib/main.js:208:72)
    at runVM (/Users/aimuz/workspace/web/wxappUnpacker/wuWxss.js:69:6)
    at runOnce (/Users/aimuz/workspace/web/wxappUnpacker/wuWxss.js:86:27)
    at Array.preRun (/Users/aimuz/workspace/web/wxappUnpacker/wuWxss.js:166:5)
    at CntEvent.decount (/Users/aimuz/workspace/web/wxappUnpacker/wuLib.js:17:43)
    at ioLimit.runWithCb (/Users/aimuz/workspace/web/wxappUnpacker/wuLib.js:81:11)
    at agent (/Users/aimuz/workspace/web/wxappUnpacker/wuLib.js:54:14)
    at FSReqCallback.readFileAfterClose [as oncomplete] (internal/fs/read_file_context.js:53:3)

在翻阅相关的资料后,发现不少人遇到了相同的问题

这简直和我的一毛一样啊。可惜没有解决方法。

偌大的互联网竟然还没有人找到解决问题。😞

0x2 分析文件

到了这里可以确认,微信小程序官方应该对小程序进行了升级,导致原反编译工具失效。可是这并不能为难我胖虎,通过 http://jsnice.org/ 工具对 page-frame.html 进行简单的反混淆后。获得了较为清晰的文件

通过仔细阅读代码后发现了 page-frame.html 文件,包含了样式信息。

__wxAppCode__['components/menu/menu.wxss'] = setCssToHead([".", [1], "menuBox{ width: 100%; height: ", [0, 100], "; position: fixed; bottom: ", [0, 0], "; left: ", [0, 0], "; display: -webkit-flex; display: flex; background: #fff; padding-top: ", [0, 10], "; border-top: ", [0, 2], " solid #ccc; }\n.", [1], "menuBox .", [1], "menu{ width: ", [0, 250], "; text-align: center; font-size: ", [0, 22], "; color: #8a8a8a }\n.", [1], "menuBox wx-image{ width: ", [0, 50], "; height: ", [0, 50], "; }\n.", [1], "menuBox .", [1], "menu wx-view{ }\n",], undefined, { path: "./components/menu/menu.wxss" });

setCssToHead 函数 定义

var setCssToHead = function (file, _xcInvalid, info) {

通过查看 setCssToHead 函数,发现其中有一个 makeup 方法,这个方法是对原来的wxss中rpx进行单位转换,换成通用型的px。

makeup 方法相关的代码

function makeup(file, opt) {
    var _n = typeof(file) === "number";
    if (_n && Ca.hasOwnProperty(file)) return "";
    if (_n) Ca[file] = 1;
    var ex = _n ? _C[file] : file;
    var res = "";
    for (var i = ex.length - 1; i >= 0; i--) {
        var content = ex[i];
        if (typeof(content) === "object") {
            var op = content[0];
            if (op == 0) res = transformRPX(content[1], opt.deviceWidth) + "px" + res;
            else if (op == 1) res = opt.suffix + res;
            else if (op == 2) res = makeup(content[1], opt) + res;
        } else res = content + res
    }
    return res;
}

在这之后又找到了一个 rewritor
rewritor 相关代码

var rewritor = function(suffix, opt, style) {
    opt = opt || {};
    suffix = suffix || "";
    opt.suffix = suffix;
    if (opt.allowIllegalSelector != undefined && _xcInvalid != undefined) {
        if (opt.allowIllegalSelector) console.warn("For developer:" + _xcInvalid);
        else {
            console.error(_xcInvalid + "This wxss file is ignored.");
            return;
        }
    }
    Ca = {};
    css = makeup(file, opt);
    if (!style) {
        var head = document.head || document.getElementsByTagName('head')[0];
        window.__rpxRecalculatingFuncs__ = window.__rpxRecalculatingFuncs__ || [];
        style = document.createElement('style');
        style.type = 'text/css';
        style.setAttribute("wxss:path", info.path);
        head.appendChild(style);
        window.__rpxRecalculatingFuncs__.push(function(size) {
            opt.deviceWidth = size.width;
            rewritor(suffix, opt, style);
        });
    }
    if (style.styleSheet) {
        style.styleSheet.cssText = css;
    } else {
        if (style.childNodes.length == 0) style.appendChild(document.createTextNode(css));
        else style.childNodes[0].nodeValue = css;
    }
}

并发现setCssToHead最后就是返回了rewritor 方法,由此以及根据上述代码可以猜出,该方法应该主要是还原wxss文件。

0x3 分析代码

rewritor 拥有三个参数,阅读代码可以得出 suffix 参数是给样式添加前缀,例如原来样式名是 .abcsuffix 参数是 a,那么会拼接成 .aabc

opt 看代码结构应该是一个对象

style 应该是一个document.node的对象。

通过浏览器直接打开page-frame.html文件,在控制台中输入 __wxAppCode__['components/menu/menu.wxss'], 看看其返回值

WX20190123-104403@2x.png

看出,确实是返回一个方法,先不传任何参数,再次输入测试 __wxAppCode__['components/menu/menu.wxss']()

WX20190123-104540@2x.png

直接返回了 undefined

由此说明参数应该必填的,现在输入正确的参数类型尝试 __wxAppCode__['components/menu/menu.wxss']("",{},document.body)

WX20190123-105904@2x.png

7E7E64C7831D01CAB0D40E2C45FCB463.jpg

1_8170889.gif

把代码复制到小程序中,🙃,像素比例竟然放大了。再次翻看一下代码,rewritor又调用了makeup方法,并且用到了设备宽度

13_2c5d12435b42e1457636d3c03de5422b.jpg

改变浏览器宽度后,确实发生了改变

.menuBox{ width: 100%; height: 50px; position: fixed; bottom: 0px; left: 0px; display: -webkit-flex; display: flex; background: #fff; padding-top: 5px; border-top: 1px solid #ccc; }
.menuBox .menu{ width: 125px; text-align: center; font-size: 11px; color: #8a8a8a }
.menuBox wx-image{ width: 25px; height: 25px; }
.menuBox .menu wx-view{ }

把设备宽度调成了iPhone 6的宽度,然后复制小程序里面

3_0c2fc143a9a2197c7333f37a830500ea.jpg

再把代码过了一遍之后,了解到 opt 会使用到一个 deviceWidth 属性,传入iPhone 6的宽度像素,也得到了

__wxAppCode__["pages/index/cardDetail/carsDetail.wxss"]("",{deviceWidth:375},document.body)

wxss 代码

.menuBox{ width: 100%; height: 50px; position: fixed; bottom: 0px; left: 0px; display: -webkit-flex; display: flex; background: #fff; padding-top: 5px; border-top: 1px solid #ccc; }
.menuBox .menu{ width: 125px; text-align: center; font-size: 11px; color: #8a8a8a }
.menuBox wx-image{ width: 25px; height: 25px; }
.menuBox .menu wx-view{ }

但是这个只能在iPhone 6上适配啊,需要改成rpx,方能适配其他的设备终端。rpx和px 的转换公式,

rpx = px * 2
px = rpx / 2

微信相关文档:
https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxss.html#wxss

经过转换后,问题就完美解决了。

微信小程序开源项目库集合

UI组件

开发框架

实用库

开发工具

服务端

其他

Demo

==============推广=================

Wafer – 快速构建具备弹性伸缩能力的微信小程序

Wafer 是腾讯云面向广大开发者提供的小程序开发全栈资源套件,套件提供小程序会话管理服务和 WebSocket 信道服务,部署方式具备良好的弹性伸缩能力,可以快速应对业务的爆发增长,同时具备较低的开发门槛。

阅读 Wiki 文档了解 Wafer 提供的服务、部署方法、架构设计以及实现细节。

开发者资源索引

关于 Wafer

Wafer 是一个愿景,希望可以给开发者提供到像晶片一样精致且可靠的开源项目,也希望和广大开发者一起进行打磨,打造健康的小程序全栈开发生态。

Wafer 的全称是 Weapp Application Fullstack Essential Resources,即微信小程序全栈基础资源。

关于 Wafer 的探索历程,可以阅读这篇文章

https://github.com/tencentyun/wafer