http://www.cnblogs.com/yexiaochai/p/4921635.html
Preface
With the rise of the mobile wave, various APPs emerge in an endless stream. The rapid business expansion increases the team’s requirements for development efficiency. At this time, the cost of developing an APP using IOS&Andriod seems a bit too high , And H5’s low-cost, high-efficiency, and cross-platform features were immediately used to form a new development model: Hybrid APP.
As a mixed development mode, Hybrid The bottom layer of the APP relies on the container (UIWebview) provided by Native. The upper layer uses Html&Css&JS for business development. The bottom layer is transparent and the upper layer is diversified. This scenario is very conducive to front-end intervention and is very suitable for rapid business iteration, so Hybrid is popular.
I originally thought that this development model All know, then Hybrid has no value for discussion, but what surprised me is that there are still many people who are unfamiliar with Hybrid model. This situation is very common in second-tier cities, so I am trying to focus on it from another aspect. I would like to introduce Hybrid to you, and I hope it will be helpful for you to choose the correct technology.
Hybrid’s family history
Initially Ctrip’s applications are all Native , H5 site only accounts for a small part of its traffic. At that time, Native had 200 people booming, while H5 opened only about 5 people playing soy sauce. Behind the wireless team came a very strong server-side leader. In order to understand the front-end development, he actually used jQuery Mobile to develop the first version of the program. Although the plan was quickly overturned, the H5 team began to work hard and has caught up with Native’s business progress in a short period of time:
Suddenly a colleague from Andriod came over and told us that there is a method maximum tree limit in Andriod, and maybe some pages require us to embed H5 pages, so Native and the H5 framework team took the lead in doing the first Hybrid project. There was a situation where a set of codes was compatible with three terminals at a time. This development efficiency leveraged, the team tasted the sweetness, so the follow-up channels basically started Hybrid development. By the time I left, the whole mechanism was very mature, and there were hundreds of people on the front end.
Scene reproduction
The wolf factory has three big traffic apps, mobile phones When Baidu, Baidu Maps, and Nuomi APP recently connected to Nuomi, they found that they are also doing hybrid platform-related promotion, packaging static resources into Native. Native provides the ability to call native applications with js, from productization and engineering. It is very good, but there are two flaws:
① When all resources are packaged into Naive, the size of the APP will increase. can’t be avoided even with an incremental mechanismThe expansion of APP, because there are fewer channels now connected to a channel 500K, there is no feeling, once the platform becomes a platform, the size of the main APP will increase sharply
② The Nuomi front-end framework team encapsulates the capabilities of the Native side, but does not provide a supporting front-end framework. This solution is Incomplete. Many businesses already have H5 sites, and a separate program must be developed in order to access; and even for new business access, they will face the restriction that embedded resources must be static resources, and the projects that are made do not have SEO, if you pay attention to SEO It still needs to be re-developed, which is problematic from an engineering point of view.
But from the product accessibility and product In terms of chemistry, the general direction of Nuomi’s hybridization is very optimistic, and it has indeed achieved some results. In a short period of time, many channels have been connected. As the promotion proceeds, a large-scale Hybrid platform may be formed next year. But because I have also experienced the promotion framework, when I heard them fooling me that the performance will increase by 70%, which is basically the same as the Native experience, I don’t know why I laughed…
Summary
If you read the above stories and you still don’t know why you want to use Hybrid technology, I Here is another summary:
Hybrid development efficiency is high and cross-platform , From the perspective of business development, the underlying Hybrid has no version issues, and bugs can be fixed in time
Hybrid has its shortcomings, and the Hybrid experience is definitely not as good as Native, so it has its own use case, but for it needs quick trial and error, fast For teams that quickly occupy the market, Hybrid must be the best choice. After the team survives, they still need to build native apps with a better experience.
Okay, there’s so much to do with it The purpose of today is actually to introduce you to some design knowledge of Hybrid. If you read this article carefully, it may be helpful to you in the following aspects:
① What are the respective tasks of Native and front-end in Hybrid
②How to design the interactive interface of Hybrid
③ How to design Hybrid Header
④ How to design the directory structure of Hybrid and how to realize the incremental mechanism
⑤ Resource caching strategy, white screen problem… …
The article is some of my personal development experience, I hope it will be useful to you, and I hope you allA lot of support for discussion, point out the textinsufficientand some of your suggestions span>recommendation.
The Andriod related code in the article was provided by my colleague Mingyue. I am very grateful to Mingyue for supporting me. Scan the QR code here to download the APP for testing:
Andriod APP QR code:
Code address: p>
https://github.com/ yexiaochai/hybrid
Native and front-end division of labor
Before doing the hybrid architecture design, it is necessary to distinguish the boundary between Native and front-end. First of all, Native provides A host environment, to make reasonable use of the capabilities provided by Native, to achieve a general Hybrid platform architecture, from the front-end perspective, I think the following core design issues need to be considered.
Interaction Design
Hybrid architecture design is the first to consider The problem is how to design the interaction with the front-end. If this design is not good, it will have a profound impact on subsequent development and maintenance of the front-end framework, and this impact is often irreversible. Therefore, the front-end must cooperate with Native to provide universal Interface, such as:
① NativeUI component, header Components, message components
② Address book, System and equipment information reading interface
③ H5 Jump to each other with Native, such as how H5 jumps to a Native page, and how H5 opens a new Webview to make animation jump to another H5 page
Resource access mechanism
Native first needs to consider how to access H5 resources, so that it can access Native internal resources by file and online by URL Resources; it is necessary to provide an incremental replacement mechanism for front-end resources to get rid of the iterative release of APP and prevent users from upgrading APP. This will involve the storage strategy of static resources in the APP, the design of the update strategy, and the server-side support if it is complicated.
account information design
The account system is important and cannot be avoided , Native needs to design a good and secure identity verification mechanism to ensure that this is sufficiently transparent for business developers to open up account information.
Hybrid development and debugging
The function design is not the end, Native We need to discuss with the front-end a set of models that can be developed and debugged, otherwise many business development work will be difficult to continue. Many articles have already been accepted, so I won’t go into details in this article.
As for some communication designs that Native will pay attention to , Concurrency design, exception handling, log monitoring, and security module are not paid attention to because it is not the field I involve (in fact, I want to pay attention to it). What the front end has to do is to encapsulate Native The overall structure of the various capabilities provided is as follows:
Real business During development, Native will also encapsulate important modules such as payment in addition to the login module, which depends on the business.
Hybrid interactive design
Hybrid interaction is nothing more than Native calling the JS method of the front-end page, or the front-end page calling the interface provided by Native through JS , The bridge between the two is Webview:
The app itself can customize the url schema, and register the customized url in the dispatch center, for example
- ctrip://wireless Open the Ctrip App
- weixin:// open WeChat
< p style="line-height:1.5; margin:10px auto; font-family:verdana,Arial,Helvetica,sans-serif; font-size:14px"> Our communication between JS and Native is generally to create this kind of URL to be captured by Native Processing, other front-end calls to Native have also appeared in the follow-up, but the underlying encapsulation can be done to make it transparent, so the focus is on how to design the interaction between the front-end and Native.
JS to Native
Native will provide some APIs in each version, and a corresponding framework team will encapsulate them on the front end to release business interfaces. For example, Nuomi’s external interface looks like this:
1 BNJS.http.get();//Get the request data from the business server [1.0] The 1.3 version interface has extensions2 BNJS.http.post ();//Submit data to the business server [1.0]3 BNJS.http.sign() ;//Calculate signature [1.0]4 BNJS.http.getNA();//to NA The server takes the request data [1.0] The 1.3 version interface has extensions5 BNJS.http.postNA(); //Submit data to NA server [1.0]6 BNJS.http.getCatgData();// Obtain filtered data from Native【 1.1]
1 BNJSReady(function(){ 2 BNJS.http.post({ 3 url:'http://cp01-testing-tuan02 .cp01.baidu.com:8087/naserver/user/feedback', 4 params: { 5 msg:'Test post', 6 contact: '18721687903' 7 },< /span> 8 onSuccess: function(res){ 9 alert('Send the post request successfully! ');10 },11 onFail: function(res){12 alert('Failed to send post request!'); 13 }14 });15 });
The front-end framework defines a global variable BNJS as the object that Native interacts with the front-end. As long as the JS library provided by Nuomi is introduced, and the Webview encapsulated by Nuomi In the container, the front end has gained the ability to call Native. I guess that Nuomi’s design is because it is easy to access and use by third-party teams. Mobile Baidu has a light application framework that also takes this route:
clouda.mbaas.account //The clouda global variables are released
This has a premise that Native itself is already very stable and rarely new The function is increased, otherwise you will face an embarrassment in the case of direct connection, because the web site is always up-to-date, and the native capabilities that are not provided will be called in some low-version containers and an error will be reported.
API-style interaction
We have no way of knowing how to do the bottom layer of Shoubai and Nuomi, but we found that the way to call the Native API interface is very similar to the interface provided by us using AJAX to call the server side:
The interface of a similar meager open platform is defined as follows:
fan service (novice access guide) span> | ||
---|---|---|
read interface | Receive news | Receive user private messages, follow, unfollow, @ and other message interfaces | write interface | Send message | Reply to the user’s private message interface |
Generate a QR code with parameters | Generate a QR code interface with parameters |
All we have to do is to create an ajax request in one way:
https://api.weibo.com/2/statuses/public_timeline.json
So when I actually design the Hybrid interaction model, I design the interface as a unit. For example, the overall interaction for obtaining the address book is :
Format Convention
The first step of the interaction is to design the data format, which is divided into request data format and response data format , The request model of reference ajax is probably:
$.ajax(options) ⇒ XMLHttpRequesttype (default: "GET") HTTP request method ("GET", "POST", or other). url (default: current url) The requested url address. data (default: none) The data contained in the request. For a GET request, this is the URL address containing the query string. If it contains an object, $.param will convert it into a string.
So The request model that I agreed with Native here is:
requestHybrid({ //Create a A new webview dialog window tagname:'hybridapi', //request parameters, which will be used by Native param: {}, // The method of calling back the front end after Native processing is successful callback: function (data) {}});
这个方法执行会形成一个URL,比如:
hybridschema://hybridapi?callback=hybrid_1446276509894¶m=%7B%22data1%22%3A1%2C%22data2%22%3A2%7D
这里提一点,APP安装后会在手机上注册一个schema,比如淘宝是taobao://,Native会有一个进程监控Webview发出的所有schema://请求,然后分发到“控制器”hybridapi处理程序,Native控制器处理时会需要param提供的参数(encode过),处理结束后将携带数据获取Webview window对象中的callback(hybrid_1446276509894)调用之
数据返回的格式约定是:
{ data: {}, errno: 0, msg: "success"}
真实的数据在data对象中,如果errno不为0的话,便需要提示msg,这里举个例子如果错误码1代表该接口需要升级app才能使用的话:
{ data: {}, errno: 1, msg: "APP版本过低,请升级APP版本"}
代码实现
这里给一个简单的代码实现,真实代码在APP中会有所变化:
1 window.Hybrid = wind ow.Hybrid || {}; 2 var bridgePostMsg = function (url) { 3 if ($.os.ios) { 4 window.location = url; 5 } else { 6 var ifr = $(''); 7 $('body').append(ifr); 8 setTimeout(function () { 9 ifr.remove();10 }, 1000)11 }12 span> };13 var _getHybridUrl = function (params) {14 var k, paramStr = '', url = 'scheme://';15 url += params.tagname + '?t=' + new Date().getTime(); //时间戳,防止url不起效16if(params.callback) {17 url += '&callback=' + params.callback;18 delete params.callback;19 }20 if (params.param) {21 paramStr = typeof params.param == 'object' ? JSON.stringify(params.param) : params.param;22 url += '¶m=' + encodeURIComponent(paramStr);23 }24 return url;25 };26 var requestHybrid = function (params) {27 //生成唯一执行函数,执行后销毁28 var tt = (new Date().getTime());29 var t = 'hybrid_' + tt;30 var tmpFn;31 32 //处理有回调的情况33 if (params.callback) {34 tmpFn = params.callback;35 params.callback = t;36 window.Hybrid[t] = function (data) { span>37 tmpFn(data);38 delete window.Hybrid[t];39 }40 }41 bridgePostMsg(_getHybridUrl(params));42 };43 //获取版本信息,约定APP的navigator.userAgent版本包含版本信息:scheme/xx.xx.xx44 var getHybridInfo = function () {45 var platform_version = {};46 var na = navigator.userAgent;47 var info = na.match(/scheme\/\d\.\d\.\d/);48 49 if (info && info[0]) {50 info = info[0].split('/');51 if (info && info.length == 2) {52 platform_version.platform = info[0];53 platform_version.version = info[1];54 }55 }56 return platform_version;57 };
因为Native对于H5来是底层,框架&底层一般来说是不会关注业务实现的,所以真实业务中Native调用H5场景较少,这里不予关注了。
常用交互API
良好的交互设计是成功的第一步,在真实业务开发中有一些API一定会用到。
跳转
跳转是Hybrid必用API之一,对前端来说有以下跳转:
① 页面内跳转,与Hybrid无关
② H5跳转Native界面
③ H5新开Webview跳转H5页面,一般为做页面动画切换
如果要使用动画,按业务来说有向前与向后两种,forward&back,所以约定如下,首先是H5跳Native某一个页面
1 //H5跳Native页面 2 //=>baidubus://forward?t=1446297487682¶m=%7B%22topage%22%3A%22home%22%2C%22type%22%3A%22h2n%22%2C%22data2%22%3A 2%7D 3 requestHybrid({ 4 tagname: 'forward', 5 param: { 6 //要去到的页面 7 topage: 'home', 8 //跳转方式,H5跳N ative 9 type: 'native',10 //其它参数11 data2: 212 }13 });
比如携程H5页面要去到酒店Native某一个页面可以这样:
1 //=>schema://forward?t=1446 297653344¶m=%7B%22topage%22%3A%22hotel%2Fdetail%20%20%22%2C%22type%22%3A%22h2n%22%2C%22id%22%3A20151031%7D 2 requestHybrid({ 3 tagname: 'forward', 4 param: { 5 //要去到的页面 6 topage: 'hotel/detail', 7 //跳转方式,H5跳Native 8 type: 'native', 9 //其它参数10 id: 2015103111 }12 });
比如H5新开Webview的方式跳转H5页面便可以这样:
1 requestHybrid({ 2 tagname: 'forward', 3 param: { 4 //要去到的页面,首先找到hotel频道,然后定位到detail模块 5 topage: 'hotel/detail ', 6 //跳转方式,H5新开Webview跳转,最后装载H5页面 7 type: 'webview', 8 //其它参数 9 id: 2015103110 }11 });
back与forward一致,我们甚至会有animattype参数决定切换页面时的动画效果,真实使用时可能会封装全局方法略去tagname的细节,这时就和糯米对外释放的接口差不多了。
Header 组件的设计
最初我其实是抵制使用Native提供的UI组件的,尤其是Header,因为平台化后,Native每次改动都很慎重并且响应很慢,但是出于两点核心因素考虑,我基本放弃了抵抗:
① 其它主流容器都是这么做的,比如微信、手机百度、携程
② 没有header一旦网络出错出现白屏,APP将陷入假死状态,这是不可接受的,而一般的解决方案都太业务了
PS:Native吊起Native时,如果300ms没有响应需要出loading组件,避免白屏
因为H5站点本来就有Header组件,站在前端框架层来说,需要确保业务的代码是一致的,所有的差异需要在框架层做到透明化,简单来说Header的设计需要遵循:
① H5 header组件与Native提供的header组件使用调用层接口一致
② 前端框架层根据环境判断选择应该使用H5的header组件抑或Native的header组件
一般来说header组件需要完成以下功能:
① header左侧与右侧可配置,显示为文字或者图标(这里要求header实现主流图标,并且也可由业务控制图标),并需要控制其点击回调
② header的title可设置为单标题或者主标题、子标题类型,并且可配置lefticon与righticon(icon居中)
③ 满足一些特殊配置,比如标签类header
所以,站在前端业务方来说,header的使用方式为(其中tagname是不允许重复的):
1 //Native以及前端框架会对特殊tagname的标识做默认回调,如果未注册callback,或者点击回调callback无返回则执行默认方法 2 // back前端默认执行History.back,如果不可后退则回到指定URL,Native如果检测到不可后退则返回Naive大首页 3 // home前端默认返回指定URL,Native默认返回大首页 4 this.header.set({ 5 left: [ 6 { 7 //如果出现value字段,则默认不使用icon 8 tagname: 'back', 9 value: '回退',10 //如果设置了lefticon或者righticon,则显示icon11 //native会提供常用图标icon映射,如果找不到,便会去当前业务频道专用目录获取图标12 lefticon: 'back',13 callback: function () { }14 }15 ],16 right: [17 {18 //默认icon为tagname,这里为icon19 tagname: 'search',20 callback: function () { }21 },22 //自定义图标23 {24 tagname: 'me',25 //会去hotel频道存储静态header图标资源目录搜寻该图标,没有便使用默认图标26 icon: 'hotel/me.png',27 callback: function () { }28 }29 ],30 title: 'title',31 //显示主标题,子标题的场景32 title: ['title', 'subtitle'],33 34 //定制化title35 title: {36 value: 'title',37 //标题右边图标38 righticon: 'down', //也可以设置lefticon39 //标题类型,默认为空,设置的话需要特殊处理40 //type: 'tabs',41 //点击标题时的回调,默认为空42 callback: function () { }43 }44 });
因为Header左边一般来说只有一个按钮,所以其对象可以使用这种形式:
1 this.header.set({ 2 back: function () { }, 3 title: '' 4 }); 5 //语法糖=> 6 this.header.set({ 7 left: [{8 tagname: 'back', 9 callback: function(){}10 }],11 title: '',12 });
为完成Native端的实现,这里会新增两个接口,向Native注册事件,以及注销事件:
1 var registerHybridCallback = function (ns, name, callback) {2 if(!window.Hybrid[ns]) window.Hybrid[ns] = {};3 window.Hybrid[ns][name] = callback;4 };5 6 var unRegisterHybridCallback = function (ns) { span>7 if(!window.Hybrid[ns]) return;8 delete window.Hybrid[ns];9 };
Native Header组件的实现:
请求类
虽然get类请求可以用jsonp的方式绕过跨域问题,但是post请求却是真正的拦路虎,为了安全性服务器设置cors会仅仅针对几个域名,Hybrid内嵌静态资源是通过file的方式读取,这种场景使用cors就不好使了,所以每个请求需要经过Nativ e做一层代理发出去。
这个使用场景与Header组件一致,前端框架层必须做到对业务透明化,业务事实上不必关心这个请求是由浏览器发出还是由Native发出:
1 HybridGet = function (url, param, callback) {2 };3 HybridPost = function (url, param, callback) {4 };
真实的业务场景,会将之封装到数据请求模块,在底层做适配,在H5站点下使用ajax请求,在Native内嵌时使用代理发出,与Native的约定为:
1 requestHybrid({ 2 tagname: 'ajax', 3 param: { 4 url: 'hotel/detail', 5 param: {}, 6 //默认为get 7 type: 'post' 8 }, 9 //响应后的回调10 callback: function (data) { }11 });
常用NativeUI组件
最后,Native会提供几个常用的Native级别的UI,比如loading加载层,比如toast消息框:
1 var HybridUI = {}; 2 HybridUI.showLoading(); 3 //=> 4 requestHybrid({ 5 tagname: 'showLoading' 6 }); 7 8 HybridUI.showToast({ 9 title: '111',10 //几秒后自动关闭提示框,-1需要点击才会关闭11 hidesec: 3,12 //弹出层关闭时的回调< span style="color:rgb(0,128,128); line-height:1.5!important">13 callback: function () { }14 });15 //=>16 requestHybrid({17 tagname: 'showToast',18 param: {19 title: '111',20 hidesec: 3,21 callback: function () { }22 }23 });
Native UI与前端UI不容易打通,所以在真实业务开发过程中,一般只会使用几个关键的Native UI。
账号系统的设计
根据上面的设计,我们约定在Hybrid中请求有两种发出方式:
① 如果是webview访问线上站点的话,直接使用传统ajax发出
② 如果是file的形式读取Native本地资源的话,请求由Native代理发出
因为静态html资源没有鉴权的问题,真正的权限验证需要请求服务器api响应通过错误码才能获得,这是动态语言与静态语言做入口页面的一个很大的区别。
以网页的方式访问,账号登录与否由是否带有秘钥cookie决定(这时并不能保证秘钥的有效性),因为Native不关注业务实现,而每次载入都有可能是登录成功跳回来的结果,所以每次载入后都需要关注秘钥cookie变化,以做到登录态数据一致性。
以file的方式访问内嵌资源的话,因为API请求控制方为Native,所以鉴权的工作完全由Native完成,接口访问如果没有登录便弹出Native级别登录框引导登录即可,每次访问webview将账号信息种入到webview中,这里有个矛盾点是Native种入webview的时机,因为有可能是网页注销的情况,所以这里的逻辑是:
① webview载入结束
② Native检测webview是否包含账号cookie信息
③ 如果不包含则种入cookie,如果包含则检测与Native账号信息是否相同,不同则替换自身
④ 如果检测到跳到了注销账户的页面,则需要清理自身账号信息
如果登录不统一会就会出现上述复杂的逻辑,所以真实情况下我们会对登录接口收口。
简单化账号接口
平台层面觉得上述操作过于复杂,便强制要求在Hybrid容器中只能使用Native接口进行登录和登出,前端框架在底层做适配,保证上层业务的透明,这样情况会简单很多:
① 使用Native代理做请求接口,如果没有登录直接Native层唤起登录框
② 直连方式使用ajax请求接口,如果没有登录则在底层唤起登录框(需要前端框架支持)
简单的登录登出接口实现:
1 /* 2 无论成功与否皆会关闭登录框 3 参数包括: 4 success 登录成功的回调 5 error 登录失败的回调 6 url 如果没有设置success,或者success执行后没有返回true,则默认跳往此url 7 */ 8 HybridUI.Login = function (opts) { 9 };10 //=>11 requestHybrid({12 tagname: 'login',13 param: {14 success: function () { },15 error: function () { },16 url: '...'17 }18 });19 //与登录接口一致,参数一致20 HybridUI.logout = function () {21 };
账号信息获取
在实际的业务开发中,判断用户是否登录、获取用户基本信息的需求比比皆是,所以这里必须保证Hybrid开发模式与H5开发模式保持统一,否则需要在业务代码中做很多无谓的判断,我们在前端框架会封装一个User模块,主要接口包括:
1 var User = {};2 User.isLogin = function () { };3 User.getInfo = function () { };
这个代码的底层实现分为前端实现,Native实现,首先是前端的做法是:
当前端页面载入后,会做一次异步请求,请求用户相关数据,如果是登录状态便能获取数据存于localstorage中,这里一定不能存取敏感信息
前端使用localstorage的话需要考虑极端情况下使用内存变量的方式替换localstorage的实现,否则会出现不可使用的情况,而后续的访问皆是使用localstorage中的数据做判断依据,以下情况需要清理localstorage的账号数据: p>
① 系统登出
② 访问接口提示需要登录
③ 调用登录接口
这种模式多用于单页应用,非单页应用一般会在每次刷新页面先清空账号信息再异步拉取,但是如果当前页面马上就需要判断用户登录数据的话,便不可靠了;处于Hybrid容器中时,因为Native本身就保存了用户信息,封装的接口直接由Native获取即可,这块比较靠谱。
Hybrid的资源
目录结构
Hybrid技术既然是将静态资源存于Native,那么就需要目录设计,经过之前的经验,目录结构一般以2层目录划分:
如果我们有两个频道酒店与机票,那么目录结构是这样的:
1 webapp //根目录 2 ├─flight 3 ├─hotel //酒店频道 4 │ │ index.html //业务入口html资源,如果不是单页应用会有多个入口 5 │ │ main.js //业务所有js资源打包 6 │ │ 7 │ └─static //静态样式资源 8 │ ├─css 9 │ ├─hybrid //存储业务定制化类Native Header图标10 │ └─images11 ├─libs12 │ libs.js //框架所有js资源打包13 │14 └─static15 ├─css16 └─images
最初设计的forward跳转中的topage参数规则是:频道/具体页面=>channel/page,其余资源会由index.html这个入口文件带出。
增量机制
真实的增量机制需要服务器端的配合,我这里只能简单描述,Native端会有维护一个版本映射表:
{ flight: 1.0.0, hotel: 1.0.0, libs: 1.0.0, static: 1.0.0}
这个映射表是每次大版本APP发布时由服务器端生成的,如果酒店频道需要在线做增量发布的话,会打包一个与线上一致的文件目录,走发布平台发布,会在数据库中形成一条记录:
channel | ver | md5 |
flight | 1.0.0 | 1245355335 |
hotel | 1.0.1 | 455ettdggd |
当APP启动时,APP会读取版本信息,这里发现hotel的本地版本号比线上的小,便会下载md5对应的zip文件,然后解压之并且替换整个hotel文件,本次增量结束,因为所有的版本文件不会重复,APP回滚时可用回到任意想去的版本,也可以对任意版本做BUG修复。
结语
github上代码会持续更新,现在界面反正不太好看,大家多多包涵吧,这里是一些效果图:
Hybrid方案是快速迭代项目,快速占领市场的神器,希望此文能对准备接触Hybrid技术的朋友提供一些帮助,并且再次感谢明月同学的配合。
微博求粉
最后,我的微博粉丝及其少,如果您觉得这篇博客对您哪怕有一丝丝的帮助,微博求粉博客求赞! ! !
Hybrid开发效率高、跨平台、底层本Hybrid从业务开发上讲,没有版本问题,有BUG能及时修复
1 BNJS.http.get();//向业务服务器拿请求据【1.0】 1.3版本接口有扩展2 BNJS.http.post();//向业务服务器提交数据【1.0】3 BNJS.http.sign();//计算签名【1.0】4 BNJS.http.getNA();//向NA服务器拿请求据【1.0】 1.3版本接口有扩展5 BNJS.http.postNA();//向NA服务器提交数据【1.0】6 BNJS.http.getCa tgData();//从Native本地获取筛选数据【1.1】
1 BNJSReady(function(){ 2 BNJS.http.post({ 3 url : 'http://cp01-testing-tuan02.cp01.baidu.com:8087/naserver/user/feedback', 4 params : { 5 msg : '测试post', 6 contact : '18721687903' 7 }, 8 onSuccess : function(res){ 9 alert('发送post请求成功! ');10 },11 onFail : function(res){12 alert('发送post请求失败!');13 }14 });15 });
clouda.mbaas.account //释放了clouda全局变量
https://api.weibo.com/2/statuses/public_timeline.json
$.ajax(options) ⇒ XMLHttpRequesttype (默认值:"GET") HTTP的请求方法(“GET”, “POST”, or other)。 url (默认值:当前url) 请求的url地址。 data (默认值:none) 请求中包含的数据,对于GET请求来说,这是包含查询字符串的url地址,如果是包含的是object的话,$.param会将其转化成string。
requestHybrid({ //创建一个新的webview对话框窗口 tagname: 'hybridapi', //请求参数,会被Native使用 param: {}, //Native处理成功后回调前端的方法 callback: function (data) { }});
{ data: {}, errno: 0, msg: "success"}
{ data: {}, errno: 1, msg: "APP版本过低,请升级APP版本"}
1 window.Hybrid = window.Hybrid || {}; 2 var bridgePostMsg = function (url) { 3 if ($.os.ios) { 4 window.location = url; 5 } else { 6 var ifr = $(''); 7 $('body').append(ifr); 8 setTimeout(function () { 9 ifr.remove();10 } , 1000)11 }12 };13 var _getHybridUrl = function (params) {14 var k, paramStr = '', url = 'scheme://';15 url += params.tagname + '?t=' + new Date().getTime(); //时间戳,防止url不起效16 if (params.callback) {17 url += '&callback=' + params.callback;18 delete params.callback;19 }20 if (params.param) {21 paramStr = typeof params.param == 'object' ? JSON.stringify(params.param) : params.param;22 url += '¶m=' + encodeURIComponent(paramStr);23 }24 return url;25 };26 var requestHybrid = function (params) {27 //生成唯一执行函数,执行后销毁28 var tt = (new Date().getTime());29 var t = 'hybrid_' + tt;30 var tmpFn;31 32 //处理有回调的情况33 if (params.callback) {34 tmpFn = params.callback;35 params.callback = t;36 window.Hybrid[t] = function (data) {37 tmpFn(data);38 delete window.Hybrid[t];39 }40 }41 bridgePostMsg(_get HybridUrl(params));42 };43 //获取版本信息,约定APP的navigator.userAgent版本包含版本信息:scheme/xx.xx.xx44 var getHybridInfo = function () {45 var platform_version = {};46 var na = navigator.userAgent;47 var info = na.match(/scheme\/\d\.\d\.\d/);48 49 if (info && info[0]) {50 info = info[0].split('/');51 if (info && info.length == 2) {52 platform_version.platform = info[0];53 platform_version.version = info[1];54 }55 }56 return platform_version;57 };
1 //H5跳Native页面 2 //=>baidubus://forward?t=1446297487682¶m=%7B%22topage%22%3A%22home%22%2C%22type%22%3A%22h2n%22%2C%22data2%22%3A2%7D 3 requestHybrid({ 4 tagname: 'forward', 5 param: { 6 //要去到的页面 7 topage: 'home', 8 //跳转方式,H5跳Native 9 type: 'native',10 //其它参数11 data2: 212 }13 });
1 //=>schema://forward?t=1446297653344¶m=%7B%22topage%22%3A%22hotel%2Fdetail%20%20%22%2C%22type%22%3A%22h2n%22%2C%22id%22%3A20151031%7D 2 requestHybrid({ 3 tagname: 'forward', 4 param: { 5 //要去到的页面 6 topage: 'hotel/detail', 7 //跳转方式,H5跳Native 8 type: 'native', 9 //其它参数10 id: 2015103111 }12 });
1 requestHybrid({ 2 tagname: 'forward', 3 param: { 4 //要去到的页面,首先找到hotel频道,然后定位到detail模块 5 topage: 'hotel/detail ', 6 //跳转方式,H5新开Webview跳转,最后装载H5页面 7 type: 'webview', 8 //其它参数 9 id: 2015103110 }11 });
1 //Native以及前端框架会对特殊tagname的标识做默认回调,如果未注册callback,或者点击回调callback无返回则执行默认方法 2 // back前端默认执行History.back,如果不可后退则回到指定URL,Native如果检测到不可后退则返回Naive大首页 3 // home前端默认返回指定URL,Native默认返回大首页 4 this.header.set({ 5 left: [ 6 { 7 //如果出现value字段,则默认不使用icon 8 tagname: 'back', 9 value: '回退',10 //如果设置了lefticon或者righticon,则显示icon11 //native会提供常用图标icon映射,如果找不到,便会去当前业务频道专用目录获取图标12 lefticon: 'back',13 callback: function () { }14 }15 ],16 right: [17 {18 //默认icon为tagname,这里为icon19 tagname: 'search',20 callback: function () { }21 },22 //自定义图标23 {24 tagname: 'me',25 //会去hotel频道存储静态header图标资源目录搜寻该图标,没有便使用默认图标26 icon: 'hotel/me.png',27 callback: function () { }28 }29 ],30 title: 'title',31 //显示主标题,子标题的场景32 title: ['title', 'subtitle'],33 34 //定制化title35 title: {36 value: 'title',37 //标题右边图标38 righticon: 'down', //也可以设置lefticon39 //标题类型,默认为空,设置的话需要特殊处理40 //type: 'tabs',41 //点击标题时的回调,默认为空42 callback: function () { }43 }44 });
1 this.header.set({ 2 back: function () { }, 3 title: '' 4 }); 5 //语法糖=> 6 this.header.set({ 7 left: [{ 8 tagname: 'back', 9 callback: function(){}10 }],11 title: '',12 });
1 var registerHybridCallback = function (ns, name, callback) {2 if(!window.Hybrid[ns]) window.Hybrid[ns] = {};3 window.Hybrid[ns][name] = callback;4 };5 6 var unRegisterHybridCallback = function (ns) {7 if(!window.Hybrid[ns]) return;8 delete window.Hybrid[ns];9 };
Native Header组件的封装
1 HybridGet = function (url, param, callback) {2 };3 HybridPost = function (url, param, callback) {4 };
1 requestHybrid({ 2 tagname: 'ajax', 3 param: { 4 url: 'hotel/detail', 5 param: {}, 6 //默认为get 7 type: 'post' 8 }, 9 //响应后的回调10 callback: function (data) { }11 });
1 var HybridUI = {}; 2 HybridUI.showLoading(); 3 //=> 4 requestHybrid({ 5 tagname: 'showLoading' 6 }); 7 8 HybridUI.showToast({ 9 title: '111',10 //几秒后自动关闭提示框,-1需要点击才会关闭11 hidesec: 3,12 //弹出层关闭时的回调13 callback: function () { }14 });15 //=>16 requestHybrid({17 tagname: 'showToast',18 param: {19 title: '111',20 hidesec: 3,21 callback: function () { }22 }23 });
1 /* 2 无论成功与否皆会关闭登录框 3 参数包括: 4 success 登录成功的回调 5 error 登录失败的回调 6 url 如果没有设置success,或者success执行后没有返回true,则默认跳往此url 7 */ 8 HybridUI.Login = function (opts) { 9 };10 //=>11 requestHybrid({12 tagname: 'login',13 param: {14 success: function () { },15 error: function () { },16 url: '...'17 }18 });19 //与登录接口一致,参数一致20 HybridUI.logou t = function () {21 };
< /span>
1 var User = {};2 User.isLogin = function () { };3 User.getInfo = function () { };
1 webapp //根目录 2 ├─flight 3 ├─hotel //酒店频道 4 │ │ index.html //业务入口html资源,如果不是单页应用会有多个入口 5 │ │ main.js //业务所有js资源打包 6 │ │ 7 │ └─static //静态样式资源 8 │ ├─css 9 │ ├─hybrid //存储业务定制化类Native Header图标10 │ └─images11 ├─libs12 │ libs.js //框架所有js资源打包13 │14 └─static15 ├─css16 └─images
{ flight: 1.0.0, hotel: 1.0.0, libs: 1.0.0, static: 1.0.0}
WordPress database error: [Table 'yf99682.wp_s6mz6tyggq_comments' doesn't exist]SELECT SQL_CALC_FOUND_ROWS wp_s6mz6tyggq_comments.comment_ID FROM wp_s6mz6tyggq_comments WHERE ( comment_approved = '1' ) AND comment_post_ID = 3859 ORDER BY wp_s6mz6tyggq_comments.comment_date_gmt ASC, wp_s6mz6tyggq_comments.comment_ID ASC