移动端App uni-app + mui 开发记录
前言
1、uni-app
uni-app是DCloud推出的终极跨平台解决方案,是一个使用Vue.js开发所有前端应用的框架,官网:https://uniapp.dcloud.io/
2、mui
号称最接近原生APP体验的高性能前端框架,官网:https://dev.dcloud.net.cn/mui/
个人觉得,mui除了页面设计很接近原生App之外,还有一个特点就是能方便的使用App扩展规范Html5 Plus(http://www.html5plus.org/doc/h5p.html),我们能在它的源码中看到比较多的地方都有使用到
{ "pages": [ //pages数组中第一项表示应用启动页 { "path": "pages/index/index", "style": { "navigationBarTitleText": "首页", "titleNView": { "buttons": [{ "type": "none", "float": "left" }, { "type": "none", "float": "right", "fontSrc":"/static/fonts/mui.ttf" }] } } } ], "globalStyle": { "navigationBarTextStyle": "black", "navigationBarTitleText": "", "navigationBarBackgroundColor": "#F8F8F8", "backgroundColor": "#F8F8F8", "backgroundColorTop": "#F4F5F6", "backgroundColorBottom": "#F4F5F6" }, "tabBar": { "color": "#7A7E83", "selectedColor": "#007AFF", //#007AFF 蓝色 #f07837 橙色 "borderStyle": "black", "backgroundColor": "#F8F8F8", "list": [{ "pagePath": "pages/index/index", "iconPath": "static/image/index/index_.png", "selectedIconPath": "static/image/index/index.png", "text": "首页" }], "position": "bottom" } }View Code
监听标题栏按钮
设置进度条颜色
设置进度条颜色、监听webview的url变化判断是否需要标题栏按钮等操作全都在App.vue中进行,具体页面可以直接调用样式对象、监听方法
App.vue
<script> export default { onLaunch: function() { //应用加载后初始后端服务地址 uni.phoneServiceAddress = "http://qch2.vipgz2.idcfengye.com"; //为了方便App演示,这里开了一个内网穿透 //监听软键盘高度变化,隐藏或显示tabbar uni.onKeyboardHeightChange(res => { if (res.height > 0) { uni.hideTabBar(); } else { uni.showTabBar(); } }) //全局进度条样式 uni.webviewStyles = { progress: { color: '#007AFF' } }; //全局监听标题栏按钮 uni.listenTitleButton = function(thid) { let webView = thid.$mp.page.$getAppWebview(); //webView加载完成时触发,开始监听子对象的onloaded事件 webView.onloaded = function() { let wv = webView.children()[0]; //webView的子对象加载完成时触发 wv.onloaded = function() { let url = wv.getURL(); //判断是否显示返回按钮 if ( url.indexOf("hybrid/html/error.html") >= 0 || url.indexOf("/index/index") >= 0 || url.indexOf("/login/index") >= 0 ) { // console.log("标题栏隐藏返回按钮"); webView.setTitleNViewButtonStyle(0, { type: 'none' }); thid.backFun = function(object){} } else { // console.log("标题栏显示返回按钮"); webView.setTitleNViewButtonStyle(0, { type: 'back' }); thid.backFun = function(object){ if(object.index == 0){ //回退 uni.navigateBack(); } } } //因为我们手动设置了一些属性,导致标题栏的title不能自动获取、设置,这里需要我们手动设置一下 uni.setNavigationBarTitle({ title: wv.getTitle() }); } } //webView手动加载、便于触发方法 webView.loadURL(thid.url); } }, onShow: function() { }, onHide: function() { } } </script> <style> /*每个页面公共css */ </style>View Code
index.vue
<!-- vue单文件组件 --> <template> <!-- 注意必须有一个view,且只能有一个根view。所有内容写在这个view下面 --> <view class="main"> <!-- 直接嵌入页面 --> <web-view id="webView" :src="url" :webview-styles="webviewStyles"></web-view> </view> </template> <!-- js代码,es6语法 --> <script> //外部文件导入 import * as util from '../../common/js/util.js'; export default { data() { return { //当前webview请求的url url: uni.phoneServiceAddress + "/index/index", //进度条颜色样式 webviewStyles: uni.webviewStyles, //回退按钮事件,比如第一页是不需要回退按钮,点进去之后的页面才需要 backFun:function(object){} } }, //点击标题栏按钮,这里主要是用于回退按钮 onNavigationBarButtonTap:function(object){ this.backFun(object); }, //页面装载完成,开始监听webview路径变化 onReady: function(options) { console.log("onReady"); // #ifdef APP-PLUS uni.listenTitleButton(this); // #endif }, onLoad: function(options) { console.log("onLoad"); }, onShow: function(options) { console.log("onShow"); }, // 点击导航栏,webview重新请求this.url onTabItemTap: function(object) { // #ifdef APP-PLUS let wv = this.$mp.page.$getAppWebview().children()[0]; wv.loadURL(this.url); // #endif } } </script> <!-- css样式代码 --> <style> /* css外部文件导入 */ @import "../../common/css/uni.css"; </style>View Code
然后其他的页面跟首页差不多,只是this.url的路径不同,同时,如果标题栏还需要其他按钮(比如右边再来个分享、或者添加按钮),就再加一个按钮,然后操作不同的下标
配置错误页面
/* 封装自定义弹窗 上右下左,居中 */ .huanzi-dialog { position: fixed; background-color: white; z-index: -1; overflow: hidden; } .huanzi-dialog-top { width: 100%; top: -100%; border-radius: 0 0 13px 13px; } .huanzi-dialog-right { width: 85%; top: 0; right: -85%; bottom: 0; border-radius: 13px 0 0 13px; } .huanzi-dialog-bottom { width: 100%; bottom: -100%; border-radius: 13px 13px 0 0; } .huanzi-dialog-left { width: 85%; top: 0; left: -85%; bottom: 0; border-radius: 0 13px 13px 0; } .huanzi-dialog-center { border-radius: 13px; opacity: 0; /* 方案一 */ /*margin: auto; left: 0; right: 0; bottom: 0; top: 0;*/ /* 方案二 */ top: 50%; left: 50%; transform: translate3d(-50%, -50%, 0) scale(1.185); }View Code
js
封装在common.js中
/* 封装天讯弹窗 */ var HuanziDialog = { mask: null,//mui遮阴层对象 showSpeed: 300,//弹出速度 hideSpeed: 100,//隐藏速度 removeFlag: true,//close内部是否执行操作 /** * 隐藏弹窗,内部方法 * @param select jq元素选择器,#xxx、.xxx等,如果为空,则隐藏所有 * @param callback 回调方法 * @param speed 速度 */ hideFun: function (select, callback, speed) { let $huanziDialog = select ? $(select) : $(".huanzi-dialog"); speed = speed ? speed : HuanziDialog.hideSpeed; //上右下左,居中 $huanziDialog.each(function () { let dialog = $(this); let clazz = dialog.attr("class"); if (clazz.indexOf("huanzi-dialog-top") > -1) { dialog.animate({top: '-100%'}, speed); } else if (clazz.indexOf("huanzi-dialog-right") > -1) { dialog.animate({right: '-85%'}, speed); } else if (clazz.indexOf("huanzi-dialog-bottom") > -1) { dialog.animate({bottom: '-100%'}, speed); } else if (clazz.indexOf("huanzi-dialog-left") > -1) { dialog.animate({left: '-85%'}, speed); } else if (clazz.indexOf("huanzi-dialog-center") > -1) { dialog.animate({opacity: 0}, speed); } setTimeout(function () { dialog.css("z-index", "-1"); }, speed) }); callback && callback(); }, /** * 显示弹窗,内部方法 * @param select jq元素选择器,#xxx、.xxx等,如果为空,则显示所有 * @param callback 回调方法 * @param speed 速度 */ showFun: function (select, callback, speed) { let $huanziDialog = select ? $(select) : $(".huanzi-dialog"); speed = speed ? speed : HuanziDialog.hideSpeed; //上右下左,居中 $huanziDialog.each(function () { let dialog = $(this); dialog.css("z-index", "999"); let clazz = dialog.attr("class"); if (clazz.indexOf("huanzi-dialog-top") > -1) { dialog.animate({top: '0%'}, speed); } else if (clazz.indexOf("huanzi-dialog-right") > -1) { dialog.animate({right: '0%'}, speed); } else if (clazz.indexOf("huanzi-dialog-bottom") > -1) { dialog.animate({bottom: '0%'}, speed); } else if (clazz.indexOf("huanzi-dialog-left") > -1) { dialog.animate({left: '0%'}, speed); } else if (clazz.indexOf("huanzi-dialog-center") > -1) { dialog.animate({opacity: 1}, speed); } }); HuanziDialog.removeFlag = true; callback && callback(); }, /** * 初始化mui遮阴层对象 */ init: function () { HuanziDialog.mask = mui.createMask(); /** * 重写close方法 */ HuanziDialog.mask.close = function () { if (!HuanziDialog.removeFlag) { return; } //方法直接在这里执行 HuanziDialog.hideFun(); //调用删除 HuanziDialog.mask._remove(); }; }, /** * 显示弹窗,供外部调用(参数同内部方法一致) */ show: function (select, callback, speed) { HuanziDialog.showFun(select, callback, speed); HuanziDialog.mask.show();//显示遮罩 }, /** * 隐藏弹窗,供外部调用(参数同内部方法一致) */ hide: function (select, callback, speed) { HuanziDialog.hideFun(select, callback, speed); HuanziDialog.mask.close();//关闭遮罩 }, /** * 警告框 * @param title 标题 * @param message 内容 * @param callback 点击确认的回调 */ alert: function (title, message, callback) { let $html = $("<div class=\"mui-popup mui-popup-in\" style=\"display: block;\">" + "<div class=\"mui-popup-inner\">" + " <div class=\"mui-popup-title\">" + title + "</div>" + " <div class=\"mui-popup-text\">" + message + "</div>" + "</div>" + "<div class=\"mui-popup-buttons\">" + "<span class=\"mui-popup-button mui-popup-button-bold confirm-but\">确定</span>" + "</div>" + "</div>"); $html.find(".confirm-but").click(function () { HuanziDialog.removeFlag = true; HuanziDialog.mask.close(); $html.remove(); callback && callback(); }); HuanziDialog.mask.show();//显示遮罩 HuanziDialog.removeFlag = false; $("body").append($html); }, /** * 确认消息框 * @param title 标题 * @param message 内容 * @param callback 点击确认的回调 */ confirm: function (title, message, callback) { let $html = $("<div class=\"mui-popup mui-popup-in\" style=\"display: block;\">" + "<div class=\"mui-popup-inner\">" + " <div class=\"mui-popup-title\">" + title + "</div>" + " <div class=\"mui-popup-text\">" + message + "</div>" + "</div>" + "<div class=\"mui-popup-buttons\">" + "<span class=\"mui-popup-button mui-popup-button-bold cancel-but\" style='color: #585858;'>取消</span>" + "<span class=\"mui-popup-button mui-popup-button-bold confirm-but\">确定</span>" + "</div>" + "</div>"); $html.find(".cancel-but").click(function () { HuanziDialog.removeFlag = true; HuanziDialog.mask.close(); $html.remove(); }); $html.find(".confirm-but").click(function () { $html.find(".cancel-but").click(); callback && callback(); }); HuanziDialog.mask.show();//显示遮罩 HuanziDialog.removeFlag = false; $("body").append($html); }, /** * 自动消失提示弹窗 * @param message 内容 * @param speed 存在时间 */ toast: function (message, speed) { speed = speed ? speed : 2000; let $html = $("<div class=\"huanzi-dialog huanzi-dialog-center\" style=\"width: 45%;height: 20%;opacity: 1;z-index: 999;background-color: #5a5a5ad1;\">" + " <p style=\" position: relative; top: 50%; left: 50%; transform: translate3d(-50%, -50%, 0) scale(1); color: #e0e0e0; font-size: 20px; \">" + message + "</p>" + "</div>"); $("body").append($html); setTimeout(function () { $html.remove(); }, speed); } }; //先初始化自定义弹窗 HuanziDialog.init();View Code
html
测试页面
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>基于MUI封装常用弹窗</title> <!-- jquery --> <script th:src="@{/webjars/jquery/3.1.1/jquery.min.js}"></script> <!-- 引入mui框架 --> <link rel='stylesheet' th:href="@{/common/mui/css/mui.css}"/> <script th:src="@{/common/mui/js/mui.js}"></script> <!-- 最后引入公用代码 --> <link rel='stylesheet' th:href="@{/common/common.css}"/> <script th:src="@{/common/common.js}"></script> <style> body{ text-align: center; } .mui-btn{ width: 50%; margin: 10px auto; } </style> </head> <body> <h4>基于MUI封装常用弹窗</h4> <button class="mui-btn" onclick="HuanziDialog.show('#top')">上</button> <button class="mui-btn" onclick="HuanziDialog.show('#bottom')">下</button> <button class="mui-btn" onclick="HuanziDialog.show('#left')">左</button> <button class="mui-btn" onclick="HuanziDialog.show('#right')">右</button> <button class="mui-btn" onclick="HuanziDialog.show('#center')">居中</button> <button class="mui-btn" onclick="HuanziDialog.alert('系统提示','我是警告框!',function() {console.log('你已确认警告!')})">警告框</button> <button class="mui-btn" onclick="HuanziDialog.confirm('系统提示','确认要XXX吗?',function() {HuanziDialog.toast('很好,你点击了确认!');console.log('很好,你点击了确认!')})">确认框</button> <button class="mui-btn" onclick="HuanziDialog.toast('提交成功')">自动消失提示框</button> <!-- 上 --> <div id="top" class="huanzi-dialog huanzi-dialog-top" style="height: 500px"> <h5>我从上边弹出</h5> </div> <!-- 下 --> <div id="bottom" class="huanzi-dialog huanzi-dialog-bottom" style="height: 500px"> <h5>我从下边弹出</h5> </div> <!-- 左 --> <div id="left" class="huanzi-dialog huanzi-dialog-left"> <h5>我从左边弹出</h5> </div> <!-- 右 --> <div id="right" class="huanzi-dialog huanzi-dialog-right"> <h5>我从右边弹出</h5> </div> <!-- 居中 --> <div id="center" class="huanzi-dialog huanzi-dialog-center" style="width: 65%;height: 30%"> <h5>我从中间弹出</h5> </div> </body> </html>View Code
其实后面的警告框、确认框的样式就是mui的5+端样式,那我们为什么还要封装呢?在开发中我们发现,在PS端浏览器将调试模式改成手机端,mui的封装的弹窗是上面的效果,但到真机上运行它又变成原生的弹窗样式,原来mui底层有进行了判断,安卓、苹果、5+等样式都不一样,这里我们为了弹窗风格的统一,同时也是为了方便后期的统一调整,因此再进行了一层封装
App调试、打包
运行 -> 运行到手机或模拟器
需要安装个模拟器(我的是雷电)、或者直接用USB数据先连接进行调试(PS:我的模拟器连接经常会断开,不知道是什么回事,有时候调试调试着就断开了,检查了也没有其他应用占用adb)
App打包是在:发行 - > 原生App-云打包
开发阶段,使用Dcloud公司的公用证书云打包就可以了,正式上线就需要自己的证书去打包
打包成功后控制台就会返回下载链接
后记
移动端App uni-app + mui 开发暂时先记录到这,后续再补充;由于是公司的App,就不方便演示,等有空了再做个demo把完整的一套东西再做完整演示;
- 跨平台移动端APP开发---简单高效的MUI框架
- 使用apicloud开发移动端APP,IOS list页面滚动卡顿解决记录
- mui 开发app 微信分享
- 快速开发app,这个快速开发框架,整合PC、移动端开发,让开发更畅快!
- h5开发app,移动端 click 事件响应缓慢的解决方案
- mui开发app之多图压缩与上传(仿qq空间说说发表)
- 使用hbuilder+mui框架开发移动app之模拟器的使用
- 最新《结合MUI框架完成HTML5移动端混合应用开发》
- mui开发APP教程之mui.ajax请求后出现“加载中”
- 移动端app效果开发中在手机端查看的方法
- MUI框架开发HTML5手机APP(一)--搭建第一个手机APP(转)
- 移动端跨平台开发框架 Cordova 学习笔记(一) 环境搭建及创建第一个 Cordova Android APP
- 【精】H5移动端webapp开发(快装app)项目案例
- 【Android开发】问答机器人,聊天类App的开发制作过程记录
- vueJS+ES6开发移动端APP实战项目笔记
- 问答机器人,聊天类App的开发制作过程记录
- HTML5开发Web移动端APP技巧笔记——入门篇
- 基于html5 plus + Mui 移动App开发(三)-食全库
- webapp开发要点记录-iphone 各机型相关知识
- 跨平台移动APP开发进阶(三)hbuilder+mui mobile app 开发心酸路