好物快应用、H5端开发小结

栏目: JavaScript · 发布时间: 6年前

内容简介:前提条件:需要知道app的包名引入快应用或者在网页中嵌入以下

第一步:检测是安装了app

前提条件:需要知道app的包名

// 判断用户是否安装了app
export const checkInstalledApp = (pkg_name) => {
  const pkg = require('@system.package')
  return new Promise((resolve,reject)=>{
    pkg.hasInstalled({
      package: pkg_name,
      success: function (data) {
        resolve(data.result) //返回true、false
      },
      fail: function (data, code) {
        reject(code)
      }
    })
  })
}

第二步:调起deepLink

let pkg = 'com.newsqq.fda' // 传入包名
let deep_link = '' // 跳转到app的地址
let params = {}

checkInstalledApp(pkg).then(hasInstalledApp=>{
    // 用户已经安装了app, deep_link直接跳转
    if(hasInstalledApp && deep_link){
      params = {uri:deep_link}
    }else{ // 否则跳转到H5地址
      params = {
        uri:'Webview',//对应于manifest中的配置
        params:{
          url,
          title:goods_name
        }
      }
    }
    this.$app.$def.router.push(params)
})

1.2 H5 页面呼起快应用

引入快应用 官方提供的代码 ,这里做了一下处理

export const quickapp = (function(){
  !function(e) {
        "use strict";
        window.appRouter = function(e, t, a, o) {
            return a = a || {},
            o && (a.__PROMPT__ = 1, a.__NAME__ = o),
            n(e, t, a)
        },
        window.installShortcut = function(e, t) {
            return n("command", "", {
                type: "shortcut",
                package: e,
                name: t
            })
        },
        window.channelReady = function(e) {
            var n = {
                available: new Function,
                availableTimeout: 2e3
            };
            return "function" == typeof e ? n.available = e: "object" == typeof e &&
            function(e, n) {
                n = n || {};
                for (var t in n) e[t] = n[t]
            } (n, e),
            function(e) {
                var n = "http://thefatherofsalmon.com/images",
                t = document.createElement("img");
                if (t.style.width = "1px", t.style.height = "1px", t.style.display = "none", n += "/" + 1e20 * Math.random(), t.src = n, document.body.appendChild(t), t.complete) e.available.call(null, !0);
                else {
                    t.onload = function() {
                        clearTimeout(a),
                        e.available.call(null, !0)
                    };
                    var a = setTimeout(function() {
                        e.available.call(null, !1)
                    },
                    e.availableTimeout)
                }
            } (n)
        };
        function n(e, n, t) {
            var a = "http://thefatherofsalmon.com/",
            o = "";
            if (e && (a = a + "?i=" + e), n && (a = a + "&p=" + n),
            function(e) {
                if (!e) return ! 0;
                var n = void 0;
                for (n in e) return ! 1;
                return ! 0
            } (t)) {
                var i = window.location.search;
                i.indexOf("?") > -1 && (o = i.substr(1))
            } else {
                o = Object.keys(t).map(function(e) {
                    return e + "=" + encodeURIComponent(t[e])
                }).join("&")
            }
            "" !== o && (a = a + "&a=" + encodeURIComponent(o));
            var l = document.createElement("img");
            l.src = a,
            l.style.width = "1px",
            l.style.height = "1px",
            l.style.display = "none",
            document.body.appendChild(l)
        }

    } ();

    return {
      appRouter:window.appRouter,
      installShortcut:window.installShortcut,
      channelReady:window.channelReady
    }
})()

或者在网页中嵌入以下 js ,支持 HTTPHTTPS 访问。上面的代码和这个一样的,只是做了一下模块化处理

<script type="text/javascript" src="//statres.quickapp.cn/quickapp/js/routerinline.min.js"></script>

调起应用

appRouter(packageName, path, params, confirm)更多详情

第一步:检测手机型号

只有在对应的应用商店上架才可以打开

// 检测手机型号
export const checkPhone = ()=>{
  const MobileDetect = require('mobile-detect')
  let device_type = navigator.userAgent;//获取userAgent信息
  let md = new MobileDetect(device_type);//初始化mobile-detect
  let os = md.os();//获取系统
  let model = "";

  //判断数组中是否包含某字符串
  Array.prototype.contains = function(needle) {
      for (i in this) {
          if (this[i].indexOf(needle) > 0)
              return i;
      }
      return -1;
  }

  if (os == "iOS") {//ios系统的处理
      os = md.os() + md.version("iPhone");
      model = md.mobile();
  } else if (os == "AndroidOS") {//Android系统的处理
      os = md.os() + md.version("Android");
      var sss = device_type.split(";");
      var i = sss.contains("Build/");
      if (i > -1) {
          model = sss[i].substring(0, sss[i].indexOf("Build/"));
      }
  	let phoneModel = model.toLocaleLowerCase()
      //判断是否是oppo
      if(phoneModel.indexOf('oppo')!==-1){
      	return true
      }

  }
  return false
}

第二步:调起快应用

以呼起 OPPO 手机下已经上架的快应用为例

// H5页面中呼起快应用

// page你所在的页面标志,goods_id是传递的参数
export const openQuickapp = ({page,goods_id})=>{
  const appRouter = (path,params={})=>quickapp.appRouter('com.yesdat.poem',`/${path}`,params)

  // 检测OPPO手机下呼起唐诗三百首快应用首页
  if(!checkPhone()){
    return false
  }
  if(page == 'home'){
    appRouter('Home')
  }else if(page == 'detail'){
    appRouter('Detail',{goods_id})
  }else if(page == 'search'){
    appRouter('Search')
  }

}

1.3 H5页面呼起deepLink

H5 页检测手机是否安装 app 相关流程

uri获取

这里的 uri ,指得就是通过 Url scheme 来实现的 H5 与安卓、苹果应用之间的跳转链接。

我们需要找到客户端的同事,来获取如下格式的链接。

xx://'跳转页面'/'携带参数'

简单解释下 url scheme

  • url 就是我们平常理解的链接。
  • scheme 是指 url 链接中的最初位置,就是上边链接中 ‘xx’ 的位置。
  • 详细介绍可以看这里: 使用url scheme详解

用这个链接我们可以跳转到 应用中的某个页面,并可以携带一定的参数

具体实现

第一步:通过iframe打开App

Android 平台则各个 app 厂商差异很大,比如 Chrome 从25及以后就不再支持通过 js 触发(非用户点击),所以这里使用 iframe src 地址等来触发 scheme

//在iframe 中打开APP
var ifr = document.createElement('iframe');
ifr.src = openUrl;
ifr.style.display = 'none';

第二步: 判断是否安装某应用

原理:若通过 url scheme 打开 app 成功,那么当前 h5 会进入后台,通过计时器会有明显延迟。利用时间来判断。

  • 由于安卓手机,页面进入后台,定时器 setTimeout 仍会不断运行,所以这里使用 setInterval ,较小间隔时间重复多次。来根据累计时间判断。
  • 根据返回 true false 来判断是否安装。
  • document.hidden 对大于 4.4 webview 支持很好,为页面可见性 api
// 检测app是否安装 
export const hasInstalledApp = (deepLink)=>{
  return new Promise((resolve,reject)=>{
      var timeout, t = 1000, hasApp = true;
      setTimeout(function () {
        if (hasApp) {
          resolve(true)
        } else {
          resolve(false)
        }
        document.body.removeChild(ifr);
      }, 2000)

      var t1 = Date.now();
      var ifr = document.createElement("iframe");
      ifr.setAttribute('src', deepLink);
      ifr.setAttribute('style', 'display:none');
      document.body.appendChild(ifr);

      timeout = setTimeout(function () {
         var t2 = Date.now();
         if (!t1 || t2 - t1 < t + 100) {
           hasApp = false;
         }
      }, t);
  })
}

使用方式

// deep_link与h5链接跳转区分
if(deepLink){
	Toast.loading('正在跳转中...',0)
	hasInstalledApp(deepLink).then(hasInstall=>{
		Toast.hide()
 		if(!hasInstall){//未安装 直接跳H5
 		  window.location.href = h5Url
 		}
 	})
}else{
	window.location.href = h5Url
}

二、剪贴板分享

主要是使用到 clipboard 简化

import ClipboardJS from 'clipboard'

class Test extends Component {
    showShare = ()=>{
    	//实例化 ClipboardJS对象;
        const copyBtn = new ClipboardJS('.copyBtn');
        
        copyBtn.on("success",function(e){
            // 复制成功
        	Toast.info('复制成功,可分享到微信、浏览器打开',2);
        });
        copyBtn.on("error",function(e){
            //复制失败;
            Toast.fail(`复制失败${e.action}`,1);
        });
    }
}

//复制功能:需要复制的文本内容传递data-clipboard-text,定义类copyBtn用于实例化 
<Flex.Item
	data-clipboard-text={window.location.href}
	className="copyBtn"
	onClick={()=>showShare()}>
	<IconWrapper><IoMdShare/></IconWrapper>复制
</Flex.Item>

更多使用方式详情: https://github.com/zenorocha/clipboard.js

三、加载更多

h5 页面需要分页加载优化, react 中为例

第一步:封装一个loadMore组件

import React from 'react'
import PropTypes from 'prop-types';
import { Spin } from 'antd';
import styled from 'styled-components'

const LoadMoreWrapper = styled.div`
  border-top: 1px dashed #ddd;
  .load-more{
    text-align: center;
    padding: 10px 0;
    background-color: #fff;
    color: #999;
  }
`
class LoadMore extends React.Component {
    constructor(props, context) {
        super(props, context);
    }
    _loadMoreHandle(){
        // 执行传递过来的loadMoreData
        this.props.loadMoreFn()
    }
    render() {
        const {hasMore} = this.props

        return (
            <LoadMoreWrapper>
              <div className="load-more" ref='wrapper'>
                 {
                     this.props.isLoadingMore && hasMore
                     ? <span className="loading"><Spin tip="Loading..."/> </span>
                     : (hasMore?<span onClick={this._loadMoreHandle.bind(this)}>加载更多</span>:<span>没有更多了</span>)
                 }
              </div>
            </LoadMoreWrapper>
        )
    }
    componentDidMount(){
        const wrapper = this.refs.wrapper;

        let timeoutId;
        window.addEventListener('scroll',()=>{
            if (this.props.isLoadingMore) return;
            if(timeoutId) clearTimeout(timeoutId);

            timeoutId = setTimeout(()=>{
                // 获取加载更多这个节点距离顶部的距离
                const top = wrapper.getBoundingClientRect().top;
                const windowHeight = window.screen.height;

                if (top && top < windowHeight) {
                    // 当wrapper已经在页面可视范围之内触发
                    this.props.loadMoreFn();
                }
            },50)
        },false)
    }
}

LoadMore.propTypes = {
  isLoadingMore:PropTypes.bool.isRequired,
  hasMore:PropTypes.bool.isRequired,
  loadMoreFn:PropTypes.func.isRequired
}

export default LoadMore

第二步:处理分页

需要后台支持分页

import React, {Component} from 'react'

class Home extends Component {
	state = {
		goodsList:[], // 存储列表信息
		hasMore:true, // 记录当前状态下还有没有更多的数据可供加载
		isLoadingMore:false, //记录当前状态下,是加载中,还是点击可加载更多
		page:1, //页码
	}

	constructor(props) {
		super(props)
	}
	componentDidMount() {
		// 获取首屏数据
		this.props.fetchTopGoods({page:this.state.page})
	}
	// 加载更多
	 _loadMoreData(){
		const {topGoods} = this.props
		const _this = this

		_this.setState({
			isLoadingMore:true
		})

		if(_this.state.hasMore){
			_this.setState({page:++_this.state.page})// 页码累加

			_this.props.fetchGoods({page:_this.state.page}).then(res=>{

				const data = res.goods.list
				let dataList = _this.state.goodsList

				if(!dataList.length){
					dataList = topGoods.data
				}

				if(data && data.length < PAGE_SIZE) {
					_this.setState({
						hasMore:false
					})
				}else{
					_this.setState({
						goodsList:dataList.concat(data),
						hasMore:true,
						isLoadingMore:false
					})
				}

			})

		}else{
			this.setState({
				isLoadingMore:false
			})
		}

	}

	render() {
		return <LoadMore isLoadingMore={this.state.isLoadingMore} hasMore={this.state.hasMore} loadMoreFn={this._loadMoreData.bind(this)} />
	}
}

四、搜索历史

封装cache

import storage from 'good-storage'

const SEARCH_KEY = '__search__'
const SEARCH_MAX_LEN = 15 // 最大保存15条

// 搜索条目更新到数组中
function insertArray(arr, val, compare, maxLen) {
  const index = arr.findIndex(compare)
  if (index === 0) {
    return
  }
  if (index > 0) {
    arr.splice(index, 1)
  }
  arr.unshift(val)
  if (maxLen && arr.length > maxLen) {
    arr.pop()
  }
}

// 从数组中移除
function deleteFromArray(arr, compare) {
  const index = arr.findIndex(compare)
  if (index > -1) {
    arr.splice(index, 1)
  }
}

// 暴露方法:保存搜索关键词 query传入的关键词
export function saveSearch(query) {
  let searches = storage.get(SEARCH_KEY, [])
  insertArray(searches, query, (item) => {
    return item === query
  }, SEARCH_MAX_LEN)
  storage.set(SEARCH_KEY, searches)
  return searches
}

// 暴露方法: 逐条删除搜索记录 query传入的历史记录
export function deleteSearch(query) {
  let searches = storage.get(SEARCH_KEY, [])
  deleteFromArray(searches, (item) => {
    return item === query
  })
  storage.set(SEARCH_KEY, searches)
  return searches
}

// 暴露方法: 清空所有历史
export function clearSearch() {
  storage.remove(SEARCH_KEY)
  return []
}
// 暴露方法: 加载所有历史记录
export function loadSearch() {
  return storage.get(SEARCH_KEY, [])
}
  • H5 端在线体验 http://goods.yesdat.com
  • 快应用端在 OPPO 应用商店搜“好物”(标有快应用的那个)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

鸟哥的Linux私房菜

鸟哥的Linux私房菜

鸟哥 / 人民邮电出版社 / 2010-6-28 / 88.00元

本书是最具知名度的Linux入门书《鸟哥的Linux私房菜基础学习篇》的最新版,全面而详细地介绍了Linux操作系统。全书分为5个部分:第一部分着重说明Linux的起源及功能,如何规划和安装Linux主机;第二部分介绍Linux的文件系统、文件、目录与磁盘的管理;第三部分介绍文字模式接口 shell和管理系统的好帮手shell脚本,另外还介绍了文字编辑器vi和vim的使用方法;第四部分介绍了对于系......一起来看看 《鸟哥的Linux私房菜》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

URL 编码/解码
URL 编码/解码

URL 编码/解码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具