内容简介:vue 项目写多了,觉得不能一成不变,想去外面的世界看看。所以尝试了一把react开发,嗯~ o(项目地址:开始解决上面提到的问题
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…
由于是同时开发多页面应用和单页面应用,所以我再开发之前考虑到了如下问题:
- h5+api App多页面应用 和 react 单页面应用跳转问题,以及跳转页面所需的参数。
- h5+api App 支持离线应用,在离线状态如何获取上次有网的数据,以及离线提交。react webApp 不支持离线。
- 如何在不同的终端打包不同的代码(例如:在APP端打包扫码功能模块,在h5端不打包此模块代码),以及在react jsx中根据不同的平台,渲染不同的代码
开始解决上面提到的问题
- 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本地数据库实现此功能 )。原理是在初始化的时候创建一个表,第一次请求的时候将请求接口和数据插入表中,以后的每次请求都是跟新表中当前接口的数据。
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到当前父webview self.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到当前父webview parentView.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 :null if(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'); } } } }) }, 复制代码
写的废话有点多。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Head First HTML5 Programming(中文版)
埃里克•弗里曼 (Eric Freeman)、伊丽莎白•罗布森 (Elisabeth Robson) / 中国电力出版社 / 2012-9 / 78.00元
《Head First HTML5 Programming(中文版)》内容简介:你可能想创建具有动态性、交互性、包含丰富数据而且互连的Web页面。先等一下,Web页面?为什么不用HTML5创建成熟的Web应用呢?另外,为什么不使用现代技术,像在移动设备上一样轻松地应用到桌面浏览器呢?当然,你肯定希望使用最新的HTML5技术来完成,比如地理定位、视频、2D绘制、Web存储、Web工作线程等,是不是?......一起来看看 《Head First HTML5 Programming(中文版)》 这本书的介绍吧!