从map组件说起
在今天公布的开发文档里,我们知道使用一个地图组件的时候是这样子的:
<map longitude="23.099994" latitude="113.324520" markers="{{markers}}" covers="{{covers}}" style="width: 375px; height: 200px;"></map>
在之前的文件里,我们提到过这个文件是wxml
文件,然后我们要用wxcc将其转换为virtual dom
中的方法,如:
它就会返回一个js的方法,如:
插播一句:上面有一个count,很有意思$gwxc > 16000,这个就是dom数的count。超了就来个异常:enough, dom limit exceeded, you don't do stupid things, do you?
,中文意思就是:你个愚蠢的人类,你是一个前端开发人员吗?
随后,在浏览器里调试一下:
在微信中是要这样调用的:
就会返回下面的结果:
看来这个名为wx-map
的标签就是微信下的map标签,它是wx-page
的children。然后让我们在WAWebview
中搜索一下,就会发现一个很有意思的代码:
它的behaviors中有一句:wx-native
,这莫非就是传说中的native组件:
顺便再看一个video是不是也是一样的:
好了,你那么聪明,我就这么说一半好了,剩下你自己去猜。
可以肯定的是:
-
map标签在开发的时候会变成HTML + CSS
-
map标签在微信上可以使用类似于Cordova的形式调用 Native组件
再接着说,virtual dom的事,回到示例代码里的map.js:
js里只放置了data,剩下的都是依据上面的值变动的observer
,如:
-
_updatePosition
-
_hiddenChanged
-
latitudeChanged
-
longitudeChanged
-
scaleChanged
-
coversChanged
-
...
这种代码的感觉比React更进了一步的节奏,本来你还需要编码来观察state,现在只需要state变动了就可以了。。。23333....,你们这些程序员都会被fire的。
好了,这里差不多就这样了~~。
重新审视WXWebview.js
于是,我重新逛逛WXWebview.js,发现这个文件里面不只有component
的内容,还有:
-
reportSDK
-
webviewSDK ??
-
virtual_dom
-
exparser
-
wx-components.js
-
wx-components.css
等等,你是不是已经猜到我在说什么了,上一篇中我们说到了PageFrame:
在之前的想法里,我觉得我必须要集齐上面的SDK,才能招唤中神龙。后来,我看到了这句:
如果不是开发环境就使用WAWebview.js
,在开发环境中使用使用xxSDK
,那么生产环境是怎么回事?如果是在开发环境会去下载最新的SDK,好像不对~~,哈哈。。
我猜这部分,我需要一个内测id,才能猜出这个答案。
有意思的是,IDE会对比version.json
,然后去获取最新的,用于预览或者区分对待开发者?
上面已经解释清楚了WAWebview的功能了,那么WAService.js呢——就是封装那些API的,如downloadFile
:
这一点上仍然相当有趣,在我们开发的时候仍然是WAWebview
做了相当多的事,而它和WAService的打包是分离的。
那么,我们从理论上来说,只需要有WAWebview就可以Render页面了。
「微信小程序」的开发框架体验起来,还不错——自带了UI框架。但是问题是他的IDE,表现起来相当的糟糕——其实主要是因为,我当时买WebStorm License买了好多年。所以,我觉得他的IDE真不如我这个付费好用。
而且,作为一个拥护自由和开源的 「GitHub 中国区首席Markdown程序员」。微信在「微信小程序」引导着Web开向封闭,我们再也不能愉快地分享我们的代码了。
如果我们放任下去,未来的Web世界令人堪忧。
好了,废话说完了,本文只是一个Demo的介绍。文章太长不想看,可以直接看Demo哈哈:
GitHub: https://github.com/phodal/weapp-webdemo
预览:http://weapp.phodal.com/
真实世界下的MINA三基本元素
「微信小程序」的背后运行的是一个名为MINA框架。在之前的几篇文章里,我们介绍得差不多了。现在让我们来作介绍pipeline:
Transform wxml和wxss
当我们修改完WXML、WXSS的时候,我们需要重新编译项目才能在浏览器上看到效果。这时候后台就会执行一些transform动作:
-
wcc来转换wxml为一个genrateFun,执行这个方法将会得到一个virtual dom
-
wxss就会转换wxss为css——这一点有待商榷。
wcc和wxss,可以从vendor目录下获取到,在“微信web开发者工具”下敲入help
,你就会得到下面的东东:
运行openVendor()
,你就会得到上面的wcss、wxss、WAService.js、WAWebview.js四个文件了。
Transform js文件
对于js文件来说,则是一个拼装的过程,如下是我们的app.js文件:
它在转换后会变成:
我假装你已经知道这是什么了,反正我也不想、也不会解释了~~。同理于:
至于它是如何replace或者apend到html中,我就不作解释了。
MINA如何运行?
为了运行一个Page,我们需要有一个virtual dom,即用wcc转换后的函数,如:
然后在我们的html中加一个script,如
就会凑发这个事件了。我简单的拆分了WXWebview.js得到了几个功能组件:
-
define.js,这里就是定义AMD模块化的地方
-
exparser.js,用于转换WXML标签到HTML标签
-
exparser-behvaior.js,定义不同标签的一些行为
-
mobile.js,应该是一个事件库,好像我并不关心。
-
page.js,核心代码,即Page、App的定义所在。
-
report.js,你所说的一切都能够用作为你的呈堂证供。
-
virtual_dom.js,一个virtual dom实现结合wcc使用,里面应该还有component.css,也可能是叫weui
-
wa-wx.js,定义微信各种API以及WebView和Native的地方,和下面的WX有冲突。
-
wx.js,同上,但是略有不同。
-
wxJSBridge.js,Weixin JS Bridge
于是,我就用上面的组件来定义不同的位置好了。当我们触发自定义的generateFuncReady
事件时,将由virtual_dom.js来接管这次Render:
因此,这里就是负责DOM初始化的地方了,这里得到的Dom结果是这样的:
而我们写的wxml是这样的:
很明显view会被转换为wx-view,text会被转换为wx-text等等,以此类推。这个转换是在virtual dom.js中调用的,调用的方法就是exparser。
遗憾的是我现在困在 data初始化上面了~~,这里面有两套不同的事件系统,有一些困扰。其中有一个是:WeixinJSBridge、还有一个是app engine中的事件系统,两个好像不能互调。。。
使用WebStorm开发
在浏览器上运行之前,我们需要简单的mock一些方法,如:
-
window.webkit.messageHandlers.invokeHandler.postMessage
-
window.webkit.messageHandlers.publishHandler.postMessage
-
WeixinJSCore.publishHandler
-
WeixinJSCore..invokeHandler
然后把 config.json
中的一些内容变成__wxConfig
,如:
如这里我们的appname是哈哈哈哈哈哈哈
——我家在福建。
然后在我们的html中引入各个js文件,啦啦。
我们还需要一个自动化的glup脚本来watch wxml和wxss的修改,然后编译,如:
Virtual Dom的表现形式
为了将真实的DOM转换为Virtual DOM,我们需要将DOM以一定的形式保存下来,如MINA的:
<map longitude="23.099994" latitude="113.324520" markers="{{markers}}" covers="{{covers}}" style="width: 375px; height: 200px;"></map>
如:
又或者是React中的:
当然我们也可以自己实现一个比较简单的DOM转为Virtual DOM,如将
转换为接近原生的:
原生的Parser
我会假装你已经知道了浏览器相关的很多细节,我也假装我已经对这些细节很清晰。下图一份Webkit浏览器的早期架构图:
如果我们使用JS实现一个将WXML将换为DOM JSON,我们就需要间接通过JavaScript Engine(即JSCore )来转换这个JSON文件。当有大量的DOM的时候,这就不是一件轻松的事了。所以,在WCC的生成代码里对DOM的数量限制为16000
。
我们可以用原生的接口来将WX DOM转换为JSON,但是我们没有办法用原生的接口来将DOM JSON转换DOM——毕竟我们还有大量的数据和绑定函数。
而这一点对于混合应用来说,就特别有帮助:
如果这个插件可以用在Cordova上,那么它将改善混合应用的性能。
数据绑定
当我们触发了generateFunc方法的时候:
我们调用下面的方法去初始化我们的DOM,并把数据传输进去:
函数绑定
MINA的函数绑定机制是由函数名来决定的,如:
对于其他类型的绑定则是:
PS:我突然就不想看这个if else经过minify以后的代码了,太恶心了。。。
如,我们的wxml:
<map longitude="23.099994" latitude="113.324520" markers="{{markers}}" covers="{{covers}}" style="width: 375px; height: 200px;"></map>
我们的propKey是bindtap,我们的propValue是bindViewTap,随后我们就会根据当前的函数名去创建相应的事件。
在「微信小程序」带领Web走向封闭之前,让我们创造一个Neo的种子。如果有可能的话,那么有一天,它终将成为Neo。
从微信小程序开始内测时,很多人(也包括我)都在考虑这样的问题:「微信小程序」正在让Web走向封闭。我的第一反应是:创建一个兼容「微信小程序」的Web框架——它即可以在微信上运行,也可以在Web上,还有作为一个混合应用运行。
在微信web开发者工具里,它封装了足够多的细节。我们只需要写一些我们不知道它们是如何真正工作的代码,流量都这样被截胡了。虽然,我们无法改变这个即将发生的事实,但是我们可以向那些愿意走向开放的人一个更好的解决方案。
因为「微信小程序」的框架是叫MINA,所以让我们称呼这个框架为WINV。
设计构思
基本的设计点有:
-
兼容微信小程序的语法——它并没有多少复杂的语法。只是简单的Virtual DOM操作,以及事件绑定
-
尽可能兼容大部分的微信API,兼容所有的微信API几乎是不可能的。
-
提供一个Virtual DOM转换的混合应用插件。
在之前的文章里,我们提到了MINA框架的基本原理,也差不多就是组件:
-
WXML转JSON Virtual DOM组件
-
Virtual DOM组件,并在这其中提供双向绑定
-
UI组件转换器,即将WXML转换为Web浏览器中的标签
-
UI组件,需要有一套UI组件,最好是和小程序保持一致,如WEUI
-
AMD组件,提供模块化需求
-
APP引擎,需要有Page模块和APP模块,来处理页面逻辑,还有Route。
一个WINV框架的Demo
计划了好几天的Demo,终于写完了,并且可以出来溜溜了~~。
这份代码在GitHub上,欢迎试玩:https://github.com/phodal/winv
并创建一个更好的出来,毕竟国庆要和我们家 ‘花仲巴’出去玩。
好了,看我们的代码,这还只是一个丑陋的原型,但是差不多可以解释了。
var App = winv.App;var Page = winv.Page;
App({
onLaunch: function() {
console.log('On Launch');
}
});Page({
data: {
motto: 'hello, world'
},
onLoad: function() {
console.log('On Load');
}
});
winv.setTemplate('<view class="container"><text class="user-motto">{{motto}}</text></view>')
winv.appRun();
它在页面上的运行结果就是,输出一个 hello, world
。顺便吐槽一句,微信小程序自带的hello world不是标准的hello world。Wiki上说:
但是需要注意的是,Hello World的标准程序是“hello, world”,没有感叹号,全部小写,逗号后面有空格,与现在流行的写法并不一致。
出于原型的原因考虑,没有像MINA一样,使用大量的事件来触发,只是简单的Run:
var domJson = this.stringToDomJSON(this.template)[0];
var dom = this.jsonToDom(domJson);
document.getElementById('app').appendChild(dom);
for (var event in window.eventPool) {
window.eventPool[event]();
}
第一步,将上面的Template转化为JSON格式的Template,由DOMParser将其转换为DOM,并在这个时候添加一个Page标签。然后在转换的时候,顺便做一些更新的数据操作。
第二步,这个JSON DOM在转换成真实的DOM的时候,应该要添加事件绑定,只是还没有实现。
第三步,上面的DOM会被放到app ID里,结果就变成了
<winv-div class="page">
<winv-div class="page__hd">
<winv-view class="container">
<winv-text class="user-motto">hello, world</winv-text>
</winv-view>
</winv-div>
</winv-div>
一看就知道还有好多坑要填。
第四步,则是调用上面的on方法,写得比较简单、粗暴。
至于,对事件和数据的判断还是和MINA一致:
if('on' === option.slice(0, 2))
简单,而又粗暴。