自从接触微信小程序以来,就希望能把小程序放到web中运行,但自己对VirtualNode,React等都不熟悉,只能慢慢学习。 这几天常使用React-Native做一个小的app,练手。但遇到了很多困难。 比
自从接触微信小程序以来,就希望能把小程序放到web中运行,但自己对VirtualNode,React等都不熟悉,只能慢慢学习。 这几天常使用React-Native做一个小的app,练手。但遇到了很多困难。 比如android很多组件不全的问题,全部需要git,npm去找,然后自己写本地模块。。。 开发效率可以说是低爆了。 读取的资料: 笔记1 让你的「微信小程序」运行在 Chrome 浏览器上
其中介绍了它对微信源代码的分析。 微信小程序的三大组件的处理方法js wxml 和 wcss js被封装为一个包define("app.js", function(require, module){var window={Math:Math} // 兼容babel/ ,location,document,navigator,self,localStorage,history,Caches; App({ onLaunch: function () { //这里是原本xx.js的内容 } }) }); require("app.js"); wxml被转义为function,即一个js文件
这个应该类似是react的jsx(拼写忘了!!!)的。 /*v0.7cc_20160919*/ var $gwxc var $gaic={} $gwx=function(path,global){ function _(a,b){b&&a.children.push(b);} function _n(tag){$gwxc++;if($gwxc>=16000){throw 'enough, dom limit exceeded, you don\'t do stupid things, do you?'};return {tag:tag.substr(0,3)=='wx-'?tag:'wx-'+tag,attr:{},children:[]}} function _s(scope,env,key){return typeof(scope[key])!='undefined'?scope[key]:env[key]} function _wl(tname){console.warn('template `' + tname + '` is being call recursively, will be stop.')} function _ai(i,p,e,me){var x=_grp(p,e,me);if(x)i.push(x);else{console.warn('path `'+p+'` not found from `'+me+'`')}} function _grp(p,e,me){if(p[0]!='/'){var mepart=me.split('/');mepart.pop();var ppart=p.split('/');for(var i=0;i<ppart.length;i++){if( ppart[i]=='..')mepart.pop();else if(!ppart[i])continue;else mepart.push(ppart[i]);}p=mepart.join('/');}if(me[0]=='.'&&p[0]=='/')p='.'+p;if(e[p])return p;if(e[p+'.wxml'])return p+'.wxml';} //以下省略好多字。 wcss被转义为css
使用上述目录下的wcsc文件可以将其转义为一个css文件。 javascript document.dispatchEvent(new CustomEvent("generateFuncReady", { detail: { generateFunc: $gwx('index.wxml') } })) 微信类库在哪里在WXWebview.js中,不过这个文件是打包过的文件。
拆分开是这样的 渲染流程
先发送一个CustomEvent(参考 https://developer.mozilla.org/zh-CN/docs/Web/API/CustomEvent/CustomEvent)。 document.addEventListener("generateFuncReady", function (e) { var generateFunc = e.detail.generateFunc; //就是我们$gwx函数生成的那个函数 wx.onAppDataChange && generateFunc && wx.onAppDataChange(function (e) { //如果不为空,则添加事件响应函数如下 var i = generateFunc((0, d.getData)()); //调用这个函数生成界面(这个d从哪里来的?应该是个页面,因为Page中有data变量) if (i.tag = "body", e.options && e.options.firstRender){ //如果是第一次渲染 e.ext && ("undefined" != typeof e.ext.webviewId && (window.__webviewId__ = e.ext.webviewId), "undefined" != typeof e.ext.downloadDomain && (window.__downloadDomain__ = e.ext.downloadDomain)), v = f(i, !0), b = v.render(), b.replaceDocumentElement(document.body), setTimeout(function () { wx.publishPageEvent(p, {}), r("firstRenderTime", n, Date.now()), wx.initReady && wx.initReady() }, 0); } else { var o = f(i, !1), a = v.diff(o); a.apply(b), v = o, document.dispatchEvent(new CustomEvent("pageReRender", {})); } }) })
这个函数,先是获取了e.detail。就是CustomEvent的第二个参数,我们给的一个对象。
如果wx.onAppDataChange不为空,generateFunc也不为空,那么就添加触发函数给onAppDataChange。 在这个函数中,先调用generateFunc获取了界面,i应该是一颗dom树。 最后,git上给的demo没有跑起来,跑出来是空白一片。
读其他文件发现,$gwx('test.wxml')必须给出正确的wxml文件名和路径才能生成一个文件 这种设计应该是为了能在一个js文件中包含所有的wxml而设计的。 一个微信小程序Mina的兼容框架
还是上一个文章同样的作者,在研究了微信的框架后,决定自己实现一个小程序的兼容框架。
拜读了这篇文章。阅读了其中的代码
这是winv.js的代码。 。。 额,之所以它起这个名字因为是MINA倒过来。。。恶趣味。 window.eventPool = []; window.globalData = {}; const winv = { parser() { }, components: [{ }], setTemplate(template){ //wxml的代码传到这里 this.template = template; }, appRun() { var template = this.template; var domJson = this.stringToDomJSON(template)[0]; //将wxml文件解析为一个json var dom = this.jsonToDom(domJson); //将json转义为自己的dom树 /** 微信为什么要这么干呢? 也许有几个原因。 一个是经过一次转化,可以有效的过滤掉那些它不喜欢的tag。只留下它允许的tag 另一个是不是要处理一下{{}}这种数据标签? **/ document.getElementById('app').appendChild(dom); for (var event in window.eventPool) { window.eventPool[event](); } }, Page (options) { //这是实现微信的那个Page函数,是一个页面管理。 option就是咱们创建的时候见的那个对象 for (var option in options) { //遍历这个对象所有的属性和方法 if ('on' === option.slice(0, 2)) { //如果是on开头的,那就是一个事件。 window.eventPool.push(options[option]); //都塞到eventPool里干嘛?有待商榷啊。。。。 } if ('data' === option) { //如果是data,将值放到globalData里。 这样写似乎只支持一个页面。。。 window.globalData = options[option]; } } }, App (options) { for (var option in options) { if ('on' === option.slice(0, 2)) { //如果是事件, 同样处理 window.eventPool.push(options[option]); } } }, getData: function updateData(key) { //获取数据 return window.globalData[key]; }, utils: { //几个工具方法 removeTemplateTag(str){ return str.substr(2, str.length - 4); }, isTemplateTag(string){ return /{{[a-zA-Z1-9]+}}/.test(string); } }, stringToDomJSON(string){ //添加包装后,将字符串转成json string = '<div class="page"><div class="page__hd">' + string + '</div></div>'; var json = this.nodeToJSON(this.domParser(string)); if (json.nodeType === 9) { json = json.childNodes; } return json; }, domParser(string){ //使用js自带的DomParser将字符串转成dom var parser = new DOMParser(); return parser.parseFromString(string, 'text/xml'); }, nodeToJSON(node){ //一个递归算法,将原始的dom树编程一个json // Code base on https://gist.github.com/sstur/7379870 node = node || this; var obj = { nodeType: node.nodeType }; if (node.tagName) { //如果tag名字存在,添加winv-前缀 obj.tagName = 'winv-' + node.tagName.toLowerCase(); } else if (node.nodeName) { obj.nodeName = node.nodeName; } if (node.nodeValue) {//如果有值,检查是不是模板,是的话,就获取数据进行替换。 //哎!这里还不支持if语句和for,任重道远啊。 obj.nodeValue = node.nodeValue; if(this.utils.isTemplateTag(node.nodeValue)){ obj.nodeValue = this.getData(this.utils.removeTemplateTag(node.nodeValue)); } } var attrs = node.attributes; if (attrs) { var length = attrs.length; var arr = obj.attributes = new Array(length); for (var i = 0; i < length; i++) { var attr = attrs[i]; arr[i] = [attr.nodeName, attr.nodeValue]; } } var childNodes = node.childNodes; if (childNodes) { length = childNodes.length; arr = obj.childNodes = new Array(length); for (i = 0; i < length; i++) { arr[i] = this.nodeToJSON(childNodes[i]); //递归调用,处理子 } } return obj; }, jsonToDom(obj) //JSON转回dom。额。。。react的diff在哪里? 这个算法比上react要差了啊。 { // Code base on https://gist.github.com/sstur/7379870 if (typeof obj == 'string') { obj = JSON.parse(obj); } var node, nodeType = obj.nodeType; switch (nodeType) { case 1: //ELEMENT_NODE node = document.createElement(obj.tagName); var attributes = obj.attributes || []; for (var i = 0, len = attributes.length; i < len; i++) { var attr = attributes[i]; node.setAttribute(attr[0], attr[1]); } break; case 3: //TEXT_NODE node = document.createTextNode(obj.nodeValue); break; case 8: //COMMENT_NODE node = document.createComment(obj.nodeValue); break; case 9: //DOCUMENT_NODE node = document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null); break; case 10: //DOCUMENT_TYPE_NODE node = document.implementation.createDocumentType(obj.nodeName); break; case 11: //DOCUMENT_FRAGMENT_NODE node = document.createDocumentFragment(); break; default: return node; } if (nodeType == 1 || nodeType == 11) { var childNodes = obj.childNodes || []; for (i = 0, len = childNodes.length; i < len; i++) { node.appendChild(this.jsonToDom(childNodes[i])); } } return node; } }; export default winv; 使用方法 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Winv Demo</title> <script src="./dist/winv.js"></script> <link rel="stylesheet" href="./styles/weui.css"> </head> <body> <div id="app"></div> </body> <script> 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(); </script> </html> 还是很像wx的。 App、Page、wxml(template) 关于这个winx框架总体来看,这个框架就是一个玩具框架,它向我们展示了微信小程序的处理流程,但距离真正产品还差很远。
其一: 只支持一个页面。 我昨天在这个项目贡献了一些代码,添加了{{obj.name}}这种语法的支持。
我在考虑要不要在这个框架上做下去。。是否自己搭建一个框架。把react包装一下来实现? |