vue 项目写多了,觉得不能一成不变,想去外面的世界看看。所以尝试了一把react开发,嗯~ o( ̄▽ ̄)o 就在想做一个webApp吧,脚手架也自己搭一个吧。然后脚手架搭建完,项目可以正式开始了,自己又出幺蛾子,为什么不能打包成App呢,之前接触过cordova平台打包App,这次决定用HBuilder h5+api 开发一个同时打包多页面App应用 和 SPA单页面应用。(小程序,哎 野心太大,但是实力不允许),在抹平平台差异后,可以愉快的写代码了,但是在我看了uni-app文档后,觉得自己写的好原始。哎。虽然写的喽了点,但是对HBuilder h5+api 有了一定的了解,在看uni-app 文档时可以在脑海里模拟它接口,功能的实现了。还有他封装的功能为了实现多平台对h5+api的简化。

项目地址:github.com/wangyaxinon…

由于是同时开发多页面应用和单页面应用,所以我再开发之前考虑到了如下问题:

  1. h5+api App多页面应用 和 react 单页面应用跳转问题,以及跳转页面所需的参数。
  2. h5+api App 支持离线应用,在离线状态如何获取上次有网的数据,以及离线提交。react webApp 不支持离线。
  3. 如何在不同的终端打包不同的代码(例如:在APP端打包扫码功能模块,在h5端不打包此模块代码),以及在react jsx中根据不同的平台,渲染不同的代码

开始解决上面提到的问题

  1. APP端页面跳转是通h5+api过创建一个Webview (plus.webview.create)窗口,并且设置创建的窗口显示 (plus.webview.show),在显示后的回调中设置上一个webview隐藏。但是在react 单页面应用中是使用react-router 。所以我封装了一个适配模式,在ios、android 平台打包app页面切换代码,在h5平台打包 react-router跳转页面封装的代码。 (process.env.platform 后面再说)
   if(process.env.platform==='ios' || process.env.platform==='Android'){var router = require(`./app.js`)}else{var router = require(`./web.js`)}router.default && (router = router.default)export default router;
复制代码

app端跳转代码

import allRouter from "@/utils/route.js"
import utils from "@/utils/init.js"
var _openw;export function push({path,titleViewOptions,AnimationType}){if(_openw || !path){return;}  // 防止快速点击if(path==="/login"){if(isLogin()){return;} }if(path==='/' ||path==='/index' ){path = `index.html`}else if(path[0]==='/'){var pathArr = path.split('/');var newpath = pathArr[pathArr.length-1]path = `/pages/${newpath}.html`}utils.changePage(path).then(()=>{_openw=null;})}
export function go(num){utils.go()
}
function isLogin() {var userDetail = utils.getItem("userDetail");if(userDetail &&userDetail.token){return true;}else{return false;}
}复制代码

web端跳转代码

import { createHashHistory } from 'history'
var history = createHashHistory();
var push = function (data){console.log(arguments);return history.push(data.path)
}
var go = (num)=>{return history.go(num)
}
export {push,go
}
复制代码

2.h5+api App 支持离线应用,在离线状态如何获取上次有网的数据,以及离线提交。react webApp 不支持离线。解决方案: APP端离线的一些静态资源如 html css js img font 都是打包在应用内的可以直接离线访问,但是比如一个商品列表的数据是从后台请求过来的。在离线的情况下是肯定拿不到数据的。但是我们可以借助h5+api (sqlite本地数据库实现此功能 )。原理是在初始化的时候创建一个表,第一次请求的时候将请求接口和数据插入表中,以后的每次请求都是跟新表中当前接口的数据。

var qs = require('qs')
import config  from "./config.js"
import SQLite from "@/platform/storage/app.js"var types = {};
types['0'] = "未知";
types['1'] = "未连接网络";
types['2'] = "有线网络";
types['3'] = "WiFi网络";
types['4'] = "2G蜂窝网络";
types['5'] = "3G蜂窝网络";
types['6'] = "4G蜂窝网络";function get(options){if(!options.url){return }if(!options.type){options.type = 'get';}if(Object.prototype.toString.call(options.data)!=="[object String]"){options.data = qs.stringify(options.data)}return new Promise((resolve,reject)=>{var xhr = new plus.net.XMLHttpRequest();xhr.onreadystatechange = function () {if(xhr.readyState==4){if ( xhr.status == 200 ) {resolve(xhr.responseText );} else {reject(xhr.readyState );}}}xhr.open( options.type,  `${options.url}?${options.data}` );xhr.send();})}
function post(options){if(!options.url){return }if(Object.prototype.toString.call(options.data)!=="[object String]"){options.data = JSON.stringify(options.data)}return new Promise((resolve,reject)=>{var xhr = new plus.net.XMLHttpRequest();xhr.onreadystatechange = function () {if(xhr.readyState==4){if ( xhr.status == 200 ) {resolve(xhr.responseText );} else {reject(xhr.readyState );}}}xhr.open( options.type, `${options.url}?${options.data}` );xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');xhr.send();})
}export default function (options){options.url = config.baseUrl+options.url;var CurrentType = types[plus.networkinfo.getCurrentType()];options.cache = options.cache || true;//无网络时或者cache,读取数据库中上一次请求成功的数据if(CurrentType==='未知' || CurrentType==='未连接网络' && options.cache){return SQLite.selectSQL(`select * from database WHERE key = '${options.url}'`).then((data)=>{var nowData;if(data && data.length){nowData = data[0].data;}try{nowData = JSON.parse(nowData)}catch{}return nowData || {};})}else{if(options.type==='get' || !options.type){return Promise.race([get(options),new Promise((resolve, reject) => {setTimeout(() => reject('request timeout'), config.timeout ? config.timeout : 30 * 1000);})]).then((res)=>{try {res = JSON.parse(res);}catch(err) {}setsqLite(`UPDATE database SET data = '${JSON.stringify(res)}', date = '${new Date()/1} WHERE key = '${options.url}'`)return res;})}else{return Promise.race([post(options),new Promise((resolve, reject)=>{setTimeout(() => reject('request timeout'), config.timeout ? config.timeout : 30 * 1000);})]).then((res)=>{try {res = JSON.parse(res);}catch(err) {}setsqLite({res,options})return res;})}}function setsqLite({options,res}) {SQLite.selectSQL(`select key from database WHERE key = '${options.url}'`).then((data)=>{if(data && data.length){//跟新表中数据SQLite.executeSql(`UPDATE database SET data = '${JSON.stringify(res)}', time = '${new Date()/1}' WHERE key = '${options.url}'`)}else{//第一次请求数据SQLite.executeSql(`insert into database values('${options.url}','${JSON.stringify(res)}','${new Date()/1}')`)}})}
}复制代码

3.区分不同的平台,我是借助webpack 实现的,在package.json scripts 传入一个参数 --ios --wx --android --web,同时在根目录下的 config/webpack.config.base.js 文件中获取这些参数,在webpack.DefinePlugink中设置全局变量

"scripts": {"build-ios": "cross-env NODE_ENV=production webpack --ios --config configMulti/webpack.config.js","build-Android": "cross-env NODE_ENV=production webpack --Android  --config configMulti/webpack.config.js","build-web": "cross-env NODE_ENV=production webpack --web  --config configSPA/webpack.config.js","build-wx": "cross-env NODE_ENV=production webpack --wx  --config configSPA/webpack.config.js","dev-web": "cross-env NODE_ENV=development webpack-dev-server  --web --inline --host 0.0.0.0 --config configSPA/webpack.config.dev.js","dev-ios": "cross-env NODE_ENV=development webpack --w  --ios --inline --host 0.0.0.0 --config configMulti/webpack.config.dev.js","dev-Android": "cross-env NODE_ENV=development webpack-dev-server  --Android --inline --host 0.0.0.0 --config configMulti/webpack.config.dev.js","dev-wx": "cross-env NODE_ENV=development webpack-dev-server  --wx --inline --host 0.0.0.0 --config configSPA/webpack.config.dev.js"},
复制代码
//读取命令行传入的参数
var parms = process.argv;
var DefinePlugin = null
if(parms.includes('--ios')){DefinePlugin = {'process.env': {platform: '"ios"'}}
}
if(parms.includes('--Android')){DefinePlugin = {'process.env': {platform: '"Android"'}}
}
if(parms.includes('--wx')){DefinePlugin = {'process.env': {platform: '"wx"'}}
}
if(parms.includes('--web')){DefinePlugin = {'process.env': {platform: '"web"'}}
}
// DefinePlugin.NODE_ENV = '"development"'
config.plugins.push(new webpack.DefinePlugin(DefinePlugin),
)
module.exports = config
复制代码

项目的核心来了:

单页面还好,多页面视图的切换,底部的导航,顶部的titleNView 子视图的创建 这些调用的都是原生功能。所以我做了一个配置,不必每次都该源代码,按规则修改配置视图也跟着去变化。这些配置在初始化的时候去创建。

比如初始化页面

initSubPages() {if(routeConfig){for(var key in routeConfig){var children = routeConfig[key].children;var parentConfig = routeConfig[key];if(children && children.length){//默认打开的第一个首页if(key==='index'){var self = plus.webview.currentWebview();var titleNView = self.getTitleNView();console.log('titleNView')console.log(JSON.stringify(titleNView))children.forEach((item,idx)=>{var page = item.MultiPath;var meta = item.meta || {};if(!plus.webview.getWebviewById(page)){// 初始化第一个子页面if(idx ==0 ){utils.setStatusBar(item);var sub = plus.webview.create( page, page, item.WebviewStyles,meta);// append到当前父webviewself.append(sub);//添加第一个子页面进入栈utils.setItem('pagesList',[page])}}})}else{//其他在需要显示的时候创建// var parentPage = routeConfig[key].MultiPath;// var parent = plus.webview.create( parentPage, parentPage);// children.forEach((item)=>{//     var page = item.MultiPath;//     var meta = item.meta//     if(!plus.webview.getWebviewById(page)){//         var sub = plus.webview.create( page, page, utils.subPageStyle,meta);//         // append到父webview//         parent.append(sub);//         // 初始化隐藏//         sub.hide();//     }// })}}else{//其他在需要显示的时候创建// var parentPage = routeConfig[key].MultiPath;// var parent = plus.webview.create( parentPage, parentPage);// parent.hide();}}}},
复制代码

初始化所有路由页面配置的底部按钮

//递归路由配置,创建原生底部导航
initAllTabBar() {if(routeConfig){drawAllNative(routeConfig);}function drawAllNative(routeConfig) {if(Object.prototype.toString.call(routeConfig)==="[object Object]"){for(var key in routeConfig){var View = routeConfig[key].View;if(View && View.length){View.forEach((item,idx)=>{var nowView = new plus.nativeObj.View(item.id, item.styles, item.tags);var parentWebview = plus.webview.getWebviewById(routeConfig[key].MultiPath==='/index.html'?utils.indexId:routeConfig[key].MultiPath);if(parentWebview){parentWebview.append(nowView)}else{//未创建页面在切换时加载View}})}var children = routeConfig[key].children;if(children && children.length){drawAllNative(children);}} }else if(Object.prototype.toString.call(routeConfig)==="[object Array]"){routeConfig.forEach((item,idx)=>{var View = item.View;if(View && View.length){View.forEach((item,idx)=>{var nowView = new plus.nativeObj.View(item.id, item.styles, item.tags);var parentWebview = plus.webview.getWebviewById(item.MultiPath);if(parentWebview){parentWebview.append(nowView)}else{//未创建页面在切换时加载View}})}var children = item.children;if(children && children.length){drawAllNative(children);}})}    }},
复制代码

h5+api切换页面

//切换页面changePage(targetPage) {return new Promise((resolve,reject)=>{var pagesList = utils.getItem('pagesList')var activePage =  pagesList[pagesList.length-1];if(targetPage===activePage){return;}if($.isEmptyObject(utils.MuLti)){utils.MuLti = getMuLtiConfig(routeConfig)}else{}var targetPageWebview = plus.webview.getWebviewById(targetPage)if(targetPageWebview){plus.webview.show(targetPage , (utils.MuLti[targetPage].AnimationTypeShow || 'auto'), 300,()=>{hidePage()});console.log('已存在');}else{// plus.webview.open(targetPage, targetPage, {}, 'slide-in-right', 200);var nowConfig = utils.MuLti[targetPage];var meta = nowConfig.meta || {};console.log('parentPath :   '+nowConfig.parentPath)if(nowConfig.parentPath){var parentView = plus.webview.getWebviewById(nowConfig.parentPath=="/index.html"?utils.indexId:nowConfig.parentPath);var sub = plus.webview.create( nowConfig.MultiPath, nowConfig.MultiPath, nowConfig.WebviewStyles,meta);// append到当前父webviewparentView.append(sub);addNowPageView();plus.webview.show(sub, (nowConfig.AnimationTypeShow || 'auto'), 300,()=>{hidePage()});}else{var ws = plus.webview.create( targetPage, targetPage, nowConfig.WebviewStyles ,meta);addNowPageView();plus.webview.show(ws, (nowConfig.AnimationTypeShow || 'auto'), 300,()=>{hidePage()});}console.log('初次创建');}utils.setStatusBar(utils.MuLti[targetPage]);function addNowPageView(){var nowConfig = utils.MuLti[targetPage];if(nowConfig.View && nowConfig.View.length){nowConfig.View.forEach((item)=>{var nowView = new plus.nativeObj.View(item.id, item.styles, item.tags);var parentWebview = plus.webview.getWebviewById(nowConfig.MultiPath);if(parentWebview){parentWebview.append(nowView)}})}}//隐藏当前 除了第一个父窗口function hidePage() {resolve('success')var pagesList = utils.getItem('pagesList')if(utils.MuLti[targetPage] && utils.MuLti[targetPage].meta && utils.MuLti[targetPage].meta.ignore){// activePage = pagesList[pagesList.length-1] //activePage = 上一次打开的页面}else{}pagesList.push(targetPage)utils.setItem('pagesList',pagesList)activePage = pagesList[pagesList.length-2] //activePage = 上一次打开的页面if(activePage !== plus.webview.getLaunchWebview().id) {var AnimationTypeClose = utils.MuLti[activePage] ? utils.MuLti[activePage].AnimationTypeClose :nullif(utils.MuLti[activePage] && utils.MuLti[activePage].meta && utils.MuLti[activePage].meta.leaveClose) {plus.webview.close(activePage,AnimationTypeClose || 'auto');}else{plus.webview.hide(activePage,AnimationTypeClose || 'auto');}}}})},
复制代码

写的废话有点多。

转载于:https://juejin.im/post/5d00ab626fb9a07ef90c9209

一次失败的尝试,h5+Api 结合 react,webpack,同时生成android 、ios、h5端代码相关推荐

  1. 免费开源的B2B、B2C商城系统(支持PC+小程序+Android+IOS+H5)

    真正的大师,永远都怀着一颗学徒的心! 一.项目简介 可免费二开的商城系统,支持PC端和移动端(小程序+Android+IOS+H5). 二.实现功能 首页.用户中心.分类页 产品详情.购物车.下单页 ...

  2. android studio运行手机时出错怎么解决_小程序 android ios h5解决方案

    你现在开发android,ios,小程序用什么工具,怎么开发的?还在单个端的开发吗?今天我们主要讨论的是一次开发多端使用的技术,也是这两年比较流行的开发方向.现在的终端太多了,app两个端androi ...

  3. uniapp 手写canvas海报(兼容android/ios/h5/微信小程序)

    先上成功图 1.在父组件里面定义弹出层,并且调用子组件制作海报的方法 2.点击显示二维码调用子组件海报方法 showPoster(customerPostId) {             // co ...

  4. 【前端】【H5 API】地理定位(获取经纬度)

    H5 API 地理定位 地理定位在日常生活中应用比较广泛,例如,互联网打车.在线地图等.在HTML 5的规范中,增加了获取用户地理位置信息的接口Geolocation,开发者可以通过经纬度来获取用户的 ...

  5. 友盟统计,h5 API

    友盟统计,h5 API /*** 友盟统计事件 API https://developer.umeng.com/docs/67963/detail/74517* _trackEvent 事件统计* _ ...

  6. HID Relay, 有线键盘转蓝牙项目学习:记一次失败的尝试

    HID Relay, 有线键盘转蓝牙项目学习:记一次失败的尝试 开始学习嵌入式后,最难受的一个点在于电脑端口不够.我的电脑有两个USB口一个TypeC口,鼠标和键盘都要插USB口,stm32和51也都 ...

  7. Android与H5交互探索之旅

    周一清早,某技术(对接我司业务SDK的技术)在有我司boss的微信群火急火燎地艾特我说为什么H5的回调 SDK 这边收不到?看到消息的我内心瞬间那是焦虑的一P,飞奔公司打开电脑双击IDE,心想别人用的 ...

  8. android alpha不起作用,API 28(P)的Android设计支持库不起作用

    我已经成功配置了android-P SDK环境.当我尝试使用android设计支持库时,我遇到项目构建错误.项目配置为: IDE:3.2 Canary 17目标API:28编译API:28 apply ...

  9. WebView详解与简单实现Android与H5互调

    为什么要学习Android与H5互调? 微信,QQ空间等大量软件都内嵌了H5,不得不说是一种趋势.Android与H5互调可以让我们的实现混合开发,至于混合开发就是在一个App中内嵌一个轻量级的浏览器 ...

最新文章

  1. VMware安装Linux ubuntu虚拟机
  2. HTML的br/标签和hr/标签
  3. 45 个常用Linux 命令,让你轻松玩转Linux!
  4. C# 字符串按设置的格试在前面或后面增加空格或其它字符
  5. IT人的学习方法论-4 一些重要的能力
  6. linux指令:输出重定向与追加- 输出重定向 - 表示追加
  7. Java新手之Java 从代码到运行的过程
  8. 新增成功到编制为空bug_36 个JS 面试题为你助力金九银10
  9. 点阵字体显示系列补记:将字库文件转换成数组形式
  10. 关于stm32f407wifi模块的设置_西门子S7300PLC模拟量模块使用方法及编程方法
  11. Java开发工程师,每个阶段需要掌握什么重点?
  12. kali Linux的安装
  13. python爬虫xpath提取数据_Python网络爬虫四大选择器(正则表达式、BS4、Xpath、CSS)总结...
  14. 运算放大器-偏置电流是怎样影响运放电路的
  15. 《2021企业数智化转型升级创新服务企业》榜重磅发布
  16. shapely库的基础学习
  17. Java修行——DAY12
  18. 【01Studio MaixPy AI K210】1.LED
  19. 程序员创业:小程序开发费用报价表,包含项目工期和费用明细
  20. Elasticsearch: date时间数据格式踩过的坑

热门文章

  1. Codeforces Beta Round #7 C. Line (扩展欧几里德)
  2. 信息安全系统设计基础第二周学习总结
  3. Linux下root密码丢失和运行级别错误的解决办法
  4. WCF HelpPage 和自动根据头返回JSON XML
  5. Perlin Noise algorithms(备忘)
  6. 剑指Offer面试题:4.从尾到头打印链表
  7. HNCU 1328: 算法2-18~2-19:双向循环链表
  8. 如何优雅的实现界面跳转 之 统跳协议 - DarwinNativeRouter
  9. 怎样用jquery添加HTML代码
  10. SharedPreference Demo