A failed attempt, h5 + Api binding react, webpack, while generating android, ios, h5 side code

vue project write more, that can not be static, want the outside world to see. So try a handful react development, ah O ~ ( ¯ ▽ ¯ ) O in a webApp want it, take a scaffold also own it. Then Scaffolding finished, the project can officially begin, but it is a unitary moths, why not pack it into App, before contact cordova platform packaged App, this time decided a while packaged applications and multi-page App SPA with HBuilder h5 + api development single-page application. (Applets, hey too ambitious, but the strength is not allowed), after a smooth platform differences, can have fun writing code, but after I saw the uni-app documentation, write a good feel of the original. Hey. Although written myself a point, but HBuilder h5 + api have a certain understanding, can be simulated in mind when looking at it uni-app document interface functionality achieved. And his package of features to achieve multi-platform h5 + api simplified right.

Project Address: github.com/wangyaxinon...

Because it is at the same time developing applications and multi-page single-page application, so I consider before redevelopment to the following questions:

  1. API + H5 App multi-page application and react single-page application jumps problem, and jump parameters desired page.
  2. h5 + api App support offline applications, how to get there last time offline data network, as well as off-line submission. react webApp not supported offline.
  3. How to package different codes in different terminals (for example: in the scan code for packing APP function module, this module code is not packaged terminal h5), and according to different platforms, different rendering the code react jsx

Begin to solve the problems mentioned above

  1. APP is an end page jump through h5 + api by creating a Webview ( plus.webview.create ) window, and sets the created window ( plus.webview.show ), a webview hidden on the display is provided after the callback. However react single-page application is to use the react-router. So I encapsulates an adaptation mode, ios, android platform packaged app page switching code, packaged in platform h5 Code react-router jump page package. (Behind process.env.platform say)
   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 end jump codes

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;
    }
}


复制代码

Code jump end 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 support offline applications, how to get there last time offline data network, as well as off-line submission. react webApp not supported offline. Solution: APP end off some static resources such as html css js img font are packaged within the application can directly access offline, but such a commodity list data is requested from the background over. In the case of off-line is certainly not get the data. But we can make use of H5 + API ( SQLite local database to implement this feature). The principle is to create a table at initialization time, the first time the request of the request interface and data into a table, each subsequent data requests are currently in the table with the new interface.

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. The distinction between different platforms, I realized by means of webpack, passing a parameter in package.json scripts --ios --wx --android --web, while in the root directory of the config / webpack.config.base. js file obtain these parameters, set global variables in 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
复制代码

The core of the project to:

Fortunately single page, create call native functions of these switches are multi-page view, and navigation at the bottom, titleNView sub-top view. So I made a profile, do not always have the source code, modify the configuration view to change also followed by the rules. These configurations during initialization to create.

For example, initial page

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();
                }
            }
        }
        
    },
复制代码

Initializes all the buttons at the bottom of the page to configure routing

//递归路由配置,创建原生底部导航
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 switch page

//切换页面
    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');
                    }
                }
            }
        })
    },
复制代码

Write nonsense a bit more.

Reproduced in: https: //juejin.im/post/5d00ab626fb9a07ef90c9209

Guess you like

Origin blog.csdn.net/weixin_34009794/article/details/93166909