- H5网页跳转小程序
现在小程序越来越普遍了,从H5网页(要在微信浏览器下打开的)跳转到相应小程序的场景也越来越多。至此微信提供了相应的微信开放标签让网页开发者可安全便捷地使用微信或系统的能力,为微信用户提供更优质的网页体验。 需要注意的是,微信开放标签有最低的微信版本和最低的系统版本要求。 微信版本要求为:7.0.12及以上系统版本要求为:iOS 10.3及以上、Android 5.0及以上 对于符合微信或系统最低版本要求但仍无法使用微信开放标签的场景,将会在下方使用步骤中的[代码]wx.config[代码]权限验证成功后触发[代码]WeixinOpenTagsError[代码]事件告知开发者。仅无法使用微信开发标签,JS-SDK其他功能不受影响。可通过如下方法监听并进行回退兼容: document.addEventListener('WeixinOpenTagsError', function (e) { console.error(e.detail.errMsg); // 无法使用开放标签的错误原因,需回退兼容。仅无法使用开放标签,JS-SDK其他功能不受影响 }); 根据目前已知的错误场景,回退兼容建议如下: iOS15底层 WebKit 接口发生变更,微信版本8.0.8以下(不包括8.0.8)无法使用开放标签,可引导用户升级最新版本微信;开放标签依赖Web Components方案,极少部分 Android 系统可能由于版本太低而不支持,可引导用户升级系统固件。H5网页跳转小程序有如下步骤: 1.在微信公众号(已认证的服务号)绑定“JS接口安全域名” 如果是公众号身份的网页,需要绑定安全域名。登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。 如果是使用小程序云开发静态网站托管的小程序网页,则不需绑定安全域名即可直接使用(即跳过该"步骤一:绑定JS接口安全域名")。 2.引入JS文件 在需要调用 JS 接口的页面引入如下 JS 文件:http://res.wx.qq.com/open/js/jweixin-1.6.0.js (支持https) 如需进一步提升服务稳定性,当上述资源不可访问时,可改访问:http://res2.wx.qq.com/open/js/jweixin-1.6.0.js (支持https) 备注:支持使用 AMD/CMD 标准模块加载方法加载。 注意:js文件必须使用1.6.0版本以上 3.通过config接口注入权限验证配置并申请所需开放标签 与使用 JS-SDK 配置方式相同,所有需要使用开放标签的页面必须先注入配置信息,并通过[代码]openTagList[代码]字段申请所需要的开放标签,否则将无法使用(同一个 url 仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用。目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)。开放标签的申请和 JS 接口的申请相互独立,因此是可以同时申请的。 wx.config({ debug: true, // 开启调试模式,调用的所有 api 的返回值会在客户端 alert 出来,若要查看传入的参数,可以在 pc 端打开,参数信息会通过 log 打出,仅在 pc 端时才会打印 appId: '', // 必填,公众号的唯一标识 timestamp: , // 必填,生成签名的时间戳 nonceStr: '', // 必填,生成签名的随机串 signature: '',// 必填,签名 jsApiList: [], // 必填,需要使用的 JS 接口列表 openTagList: [] // 可选,需要使用的开放标签列表,例如['wx-open-launch-app'] }); 注意:如果使用的是小程序云开发静态网站托管的域名的网页,可以免鉴权直接跳任意合法合规小程序,调用 wx.config 时 appId 需填入非个人主体的已认证小程序,不需计算签名,timestamp、nonceStr、signature 填入非空任意值即可。 4.通过ready接口处理成功验证 wx.ready(function () { // config信息验证后会执行 ready 方法,所有接口调用都必须在 config 接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在 ready 函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在 ready 函数中 }); 5.通过error接口处理失败验证 wx.error(function (res) { // config信息验证失败会执行 error 函数,如签名过期导致验证失败,具体错误信息可以打开 config 的debug模式查看,也可以在返回的 res 参数中查看,对于 SPA 可以在这里更新签名 }); 接口调用说明所有接口通过wx对象(也可使用jWeixin对象)来调用,参数是一个对象,除了每个接口本身需要传的参数之外,还有以下通用参数: success:接口调用成功时执行的回调函数。fail:接口调用失败时执行的回调函数。complete:接口调用完成时执行的回调函数,无论成功或失败都会执行。cancel:用户点击取消时的回调函数,仅部分有用户取消操作的api才会用到。trigger: 监听Menu中的按钮点击时触发的方法,该方法仅支持Menu中的相关接口。备注:不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回。 以上几个函数都带有一个参数,类型为对象,其中除了每个接口本身返回的数据之外,还有一个通用属性errMsg,其值格式如下: 调用成功时:"xxx:ok" ,其中xxx为调用的接口名 用户取消时:"xxx:cancel",其中xxx为调用的接口名 调用失败时:其值为具体错误信息 使用说明 所使用的标签允许提供插槽,由于插槽中模版的样式是和页面隔离的,因此需要注意在插槽中定义模版的样式。插槽模版及样式均需要通过[代码]<script type="text/wxtag-template"></script>或<template></template>[代码]进行包裹。另外,对于具名插槽还需要通过[代码]slot[代码]属性声明插槽名称,下文标签插槽中的 default 插槽为默认插槽,可不声明插槽名称。 对于标签事件,均可通过[代码]event.detail[代码]获得详细信息。如果无特殊说明,下文标签事件说明中的返回值均指代[代码]event.detail[代码]中的内容。 另外,需要注意以下几点: 页面中与布局和定位相关的样式,如[代码]position: fixed; top -100;[代码]等,尽量不要写在插槽模版的节点中,请声明在标签或其父节点上;对于有 CSP 要求的页面,需要添加白名单[代码]frame-src https://*.qq.com webcompt:[代码],才能在页面中正常使用开放标签。(CSP相关内容可查看以下几篇文章:https://zhuanlan.zhihu.com/p/142987601、https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)开放对象已认证的服务号,服务号绑定“JS接口安全域名”下的网页可使用此标签跳转任意合法合规的小程序。已认证的非个人主体的小程序,使用小程序云开发的静态网站托管绑定的域名下的网页,可以使用此标签跳转任意合法合规的小程序。代码 参考:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/staticstorage/jump-miniprogram.html https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_Open_Tag.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>H5跳转小程序</title> <!-- weui 样式 --> <link rel="stylesheet" href="https://res.wx.qq.com/open/libs/weui/2.4.1/weui.min.css"> <!-- 页面样式 start --> <style> /* --------START reset.css------- */ * { margin: 0; padding: 0; } html, body { background-color: #fff; } a { text-decoration: none; } a, button, input, span, div { -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } li { list-style-type: none; } /* --------END reset.css------- */ .hidden { display: none; } .full { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } .public-web-container, .wechat-web-container, .wechat-web-container wx-open-launch-weapp, .desktop-web-container { display: flex; flex-direction: column; align-items: center; } .public-web-container p, .wechat-web-container p, .desktop-web-container p { position: absolute; top: 40%; } .public-web-container a { position: absolute; bottom: 40%; } .wechat-web-container wx-open-launch-weapp { position: absolute; bottom: 40%; left: 0; right: 0; } .wechat-web-container .open-btn { display: block; margin: 0 auto; padding: 8px 24px; width: 200px; height: 45px; border: none; border-radius: 4px; background-color: #07c160; color: #fff; font-size: 18px; text-align: center; } </style> <!-- 页面样式 end --> </head> <body> <!-- 页面容器 start --> <div id="h5OpenMiniprogram"> <!-- <template> --> <!-- 页面内容 start --> <div class="page full"> <!-- 移动端微信外部浏览器 --> <div id="public-web-container" class="hidden"> <p>正在打开“小程序名字”</p> <a href="javascript:" id="public-web-jump-button" class="weui-btn weui-btn_primary weui-btn_loading" onclick="openWeapp()"> <span id="public-web-jump-button-loading" class="weui-primary-loading weui-primary-loading_transparent"> <i class="weui-primary-loading__dot"></i> </span> 打开小程序 </a> </div> <!-- 微信内部浏览器 --> <div id="wechat-web-container" class="hidden"> <p>点击以下按钮打开“小程序名字”</p> <!-- username:必填,所需跳转的小程序原始id,即小程序对应的以gh_开头的id; path:非必填,所需跳转的小程序内页面路径及参数(默认小程序的初始页面【即首页】) --> <wx-open-launch-weapp id="launch-btn" username="gh_XXX" path="/pages/XXX"> <!-- 第一种: 不适用于Vue.js开发的项目,template标签会冲突 --> <template> <style> .open-btn { display: block; margin: 0 auto; padding: 8px 24px; width: 200px; height: 45px; border: none; border-radius: 4px; background-color: #07c160; color: #fff; font-size: 18px; text-align: center; } </style> <button class="open-btn">打开小程序</button> </template> <!-- 第二种:几乎适用于所有前端框架开发的项目 --> <!-- <script type="text/wxtag-template"> <style> .open-btn { display: block; margin: 0 auto; padding: 8px 24px; width: 200px; height: 45px; border: none; border-radius: 4px; background-color: #07c160; color: #fff; font-size: 18px; text-align: center; } </style> <button class="open-btn">打开小程序</button> </script> --> </wx-open-launch-weapp> </div> <!-- 桌面端 --> <div id="desktop-web-container" class="hidden"> <p>请在手机打开网页链接</p> </div> </div> <!-- 页面内容 end --> <!-- </template> --> </div> <!-- 页面容器 end --> <!-- 引入jQuery --> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script> <!-- 调试用的移动端 console --> <script src="https://cdn.jsdelivr.net/npm/eruda"></script> <script> eruda.init(); </script> <!-- 公众号 JSSDK --> <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script> <!-- 云开发 Web SDK --> <script src="https://res.wx.qq.com/open/js/cloudbase/1.1.0/cloud.js"></script> <script> function docReady(fn) { document.addEventListener('WeixinOpenTagsError', function (e) { console.error(e.detail.errMsg); // 无法使用开放标签的错误原因,需回退兼容。仅无法使用开放标签,JS-SDK其他功能不受影响 }); if (document.readyState === "complete" || document.readyState === "interactive") { fn(); } else { document.addEventListener("DOMContentLoaded", fn); } } docReady(async function () { var ua = navigator.userAgent.toLowerCase(); var isWXWork = ua.match(/wxwork/i) == "wxwork"; var isWeixin = !isWXWork && ua.match(/micromessenger/i) == "micromessenger"; console.log("isWeixin", isWeixin, isWXWork); var isMobile = false; var isDesktop = false; if (navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|IEMobile)/i)) { isMobile = true; } else { isDesktop = true; } if (isWeixin) { var containerEl = document.getElementById("wechat-web-container"); containerEl.classList.remove("hidden"); containerEl.classList.add("full", "wechat-web-container"); // 公众号网页需要绑定安全域名 // 获取签名,timestamp、nonceStr、signature $.ajax({ url: "请求地址", dataType: "json", success: function (res) { console.log("WeChatConfig", res); if (res.id === 1) { var data = res.items; // 根据实际情况返还的数据进行赋值 wx.config({ // debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印 appId: data.appId, // 必填,公众号的唯一标识 timestamp: data.timestamp, // 必填,生成签名的时间戳 nonceStr: data.nonceStr, // 必填,生成签名的随机串 signature: data.signature, // 必填,签名 jsApiList: ["chooseImage"], // 必填,需要使用的JS接口列表(此处随意一个接口即可) openTagList: ["wx-open-launch-weapp"], // 可选,需要使用的开放标签列表,例如['wx-open-launch-app'] }); /** * config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后。 * config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。 * 对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中 * */ wx.ready(function (res2) { console.log("ready", res2); var launchBtn = document.getElementById("launch-btn"); launchBtn.addEventListener("ready", function (e) { console.log("开放标签 ready"); }); launchBtn.addEventListener("launch", function (e) { console.log("开放标签 success"); }); launchBtn.addEventListener("error", function (e) { console.log("开放标签 fail", e.detail); }); }); // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名 wx.error(function (err) { console.log("error", err); }); } } }) // 小程序云开发静态网站托管的网页 // var launchBtn = document.getElementById("launch-btn"); // launchBtn.addEventListener("ready", function (e) { // console.log("开放标签 ready"); // }); // launchBtn.addEventListener("launch", function (e) { // console.log("开放标签 success"); // }); // launchBtn.addEventListener("error", function (e) { // console.log("开放标签 fail", e.detail); // }); // wx.config({ // // debug: true, // 调试时可开启 // appId: "", // 非个人主体的已认证的小程序APPID // timestamp: 0, // 必填,填任意数字即可 // nonceStr: "nonceStr", // 必填,填任意非空字符串即可 // signature: "signature", // 必填,填任意非空字符串即可 // jsApiList: ["chooseImage"], // 必填,随意一个接口即可 // openTagList: ["wx-open-launch-weapp"], // 填入打开小程序的开放标签名 // }); } else if (isDesktop) { // 在 pc 上则给提示引导到手机端打开 var containerEl = document.getElementById("desktop-web-container"); containerEl.classList.remove("hidden"); containerEl.classList.add("full", "desktop-web-container"); } else { var containerEl = document.getElementById("public-web-container"); containerEl.classList.remove("hidden"); containerEl.classList.add("full", "public-web-container"); // 云函数 // 因未开通云开发环境,此处不做处理 // var c = new cloud.Cloud({ // identityless: true, // 必填,表示是未登录模式 // resourceAppid: "小程序 AppID", // 资源方 AppID // resourceEnv: '云开发环境 ID', // 资源方环境 ID // }); // await c.init(); // window.c = c; // var buttonEl = document.getElementById("public-web-jump-button"); // var buttonLoadingEl = document.getElementById("public-web-jump-button-loading"); // try { // await openWeapp(() => { // buttonEl.classList.remove("weui-btn_loadin"); // buttonLoadingEl.classList.add("hidden"); // }) // } catch (error) { // buttonEl.classList.remove("weui-btn_loadin"); // buttonLoadingEl.classList.add("hidden"); // throw error; // } } }); async function openWeapp(onBeforeJump) { console.log("未开通云开发环境", onBeforeJump); // 因未开通云开发环境,此处不做处理 // var c = window.c; // const res = await c.callFunction({ // name: "public", // data: { // action: "getUrlScheme", // }, // }); // console.warn(res); // if (onBeforeJump) { // onBeforeJump(); // } // location.href = res.result.openlink; } </script> </body> </html> 错误提示 (1)没有在“JS接口安全域名”设置 [图片] 成功提示 (1)微信开发者工具 [图片] (2)真机:会有要打开小程序的名字 [图片]
03-06 - 微信开发者工具创建的Typescript小程序需要改造目录结构后才能使用第三方组件
微信开发者工具创建的typescript小程序目录结构并不能良好的使用npm的“自定义组件”,想要漂亮地使用“自定义组件”,得自行改造工程结构 注:这里是指wxml中使用的“自定义组件”,不是ts或js中可调用的纯js 问题现象分析如下: 微信开发者工具,创建出的Typescript小程序工程结构如下: [代码]|--miniprogram | |--pages | |--app.js | |--app.wxss | |--app.json |--typings | |--wx | |--index.d.ts | |--lib.wa.es6.d.ts |--package.json |--project.config.js |--ts.json [代码] 这样的结构,小程序根目录在miniprogram,导致package.json不在小程序根目录内,也即node_modules目录中的第三方组件不在小程序根目录,违背了npm支持中node_modules位置的要求:npm 支持-使用npm包 此时,如果app.json或index.json中,无论如何引用不到node_modules中的第三方组件(注意是第三方开发的微信“自定义组件”,如vant-weapp,而纯js是可用的) 如: [图片] 图中的"…/node_modules"无论如何修改,控制台也会报类似的错误: [图片] 问题原因: 经过测试,存在两个约束导致了以上问题: app.json或index.json(以及任何页面的配置文件)的"usingComponents"不能引用node_modules目录下的组件; "usingComponents"只能引用小程序根目录(此例是miniprogram)及子目录下的组件。 通过以下步骤,就可以验证: 将node_modules下的vant-weapp目录复制到miniprogram目录下,"usingComponents"修改为其相对位置,组件即可用: [代码]|--miniprogram | |--pages | |--app.js | |--app.wxss | |--app.json | |--want-weapp |--typings | |--wx | |--index.d.ts | |--lib.wa.es6.d.ts |--package.json |--project.config.js |--ts.json [代码] [图片] 成功使用自定义组件的效果: [图片] 2. 如果vant-weapp放在miniprogram以外的目录,仍然引用不到。 但是,有代码洁癖的同学肯定不能满足于,把所有的依赖都手工复制到miniprogram目录这么弱智的操作方法吧? 解决方案: 怎样才能做到npm安装的组件,可以直接在小程序中引用呢? 其实答案就在npm 支持这篇文章里,只不过其写的比较含糊,没有解释清楚细节,总结起来就是对模板工程做两步改造,分别解决上面提到的两个问题: 将小程序根目录即miniprogram移动到与package.json同级别; 使用“npm构建”功能(将node_modules目录的组件构建到miniprogram_npm目录下)构建后,再直接引用组件即可使用。 操作步骤如下: 第一步:移动miniprogram目录下的所有文件到根目录(与package.json同级): [代码]|--pages |--app.js |--app.wxss |--app.json |--want-weapp |--typings | |--wx | |--index.d.ts | |--lib.wa.es6.d.ts |--package.json |--project.config.js |--ts.json [代码] 第二步:修改project.config.json,去掉下面这行: [代码]"miniprogramRoot": "miniprogram/", [代码] 或者修改为 [代码]"miniprogramRoot": "./", [代码] 第三步:修改tsconfig.json中的include/exclude以确保自己的ts代码被编译: [代码] "include": [ "./miniprogram/**/*.ts" ], "exclude": [ "node_modules", "miniprogram_dist", "miniprogram_npm", "**/*.spec.ts", "typings/**/*.d.ts" ] [代码] 改为 [代码]"include": [ "./**/*.ts" ], "exclude": [ "node_modules", "miniprogram_dist", "miniprogram_npm", "**/*.spec.ts", "typings/**/*.d.ts" ] [代码] 说明:include目录外移一层,同时exclude掉typings和miniprogram_npm目录 第四步:使用微信开发者工具的“工具-构建npm”功能构建组件到miniprogram_npm目录 第五步:在app.json(或者页面的json配置)里面引入组件 [代码] "usingComponents": { "van-button": "vant-weapp/button/index" } [代码] 注意:这里使用相对于miniprogramnpm的目录就可以了,并不需要相对于app.json文件 后记: 微信开发者工具创建出的typescript小程序,本意应该是将小程序根目录与typescript、node环境隔离开来,提供一个清晰的目录结构,但是对npm集成、“自定义组件”支持的不佳,说明了微信团队在“自定义组件”,npm支持,typescript支持还处在起步阶段,团队间的磨合还不到位,文档也不够透彻,当然这需要时间去改进。 我希望微信小程序团队通过以下几点,能很快地原生支持typescript+npm+第三方自定义组件: 1、调整微信开发者工具的typescript小程序模板工程结构,或者反过来让支持现在的工程结构下使用第三方的npm自定义组件; 2、完善npm支持相关的文档,如果自己完善不来,其实可以用wiki的方式,鼓励广大开发者一起来完善
2019-08-11