因为有个公司又是搞小程序容器的,而且老是问我相关问题,不得已只能再次复习一下。
os: 其实只要理解了vdom,然后再了解一点编译相关的知识之后好像也没啥需要特别了解的。。。
html页面
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><div id="container"></div><script>const myWorker = new Worker("minapp/service.js");window.myWorker = myWorker;myWorker.addEventListener('message', function (e) {if (e.data.type === 'view') {__render(JSON.parse(e.data.data))}})// 小程序的js文件,正常应该是直接执行到service中的,不需要使用这种方式myWorker.postMessage({type: 'js',pageName: 'page/index',data: `Page({data: {cc1: '点击修改'},click: function () {console.log('click')this.setData({cc1: 'asd'})},click1: function () {wx.showModal()}
})` });</script><script src="minapp/view.js"></script></body></html>
上图中Worker表示独立的线程,主要运行servic.js和小程序的js文件。
view.js
import {init,classModule,propsModule,styleModule,eventListenersModule,h,datasetModule
} from "snabbdom";const patch = init([classModule,propsModule,styleModule,eventListenersModule,datasetModule
]);let nowVnode = document.getElementById("container");function postMessage(type, name, pageName) {myWorker.postMessage({type: type,name: name,pageName: pageName})
}window.__render = function (newVnode) {newVnode.data.on = {click: function (e) {const dataset = e.target.datasetif (dataset && dataset.pageName && dataset.bindtap) {postMessage('event', dataset.bindtap, dataset.pageName)}e.stopPropagation();}}patch(nowVnode, newVnode);nowVnode = newVnode
}
视图层的功能其实非常简单。
1、接收服务端的vnode然后渲染
2、将用户操作产生的事件发送到service中(因为用户的js代码是在service中执行的,开发者需要知道click方法是否执行了)
service.js
import {h
} from "snabbdom";global.__PAGES = {}self.__WXSCODE = {'page/index': {'m1': function () {const module = { exports: {} };var msg = "hello wxs";module.exports.message = msg;return module.exports}}
}// 编译器将wxml转换为下面的形式然后注入到html中
self.__APPCODE__ = {'page/index': ["div", {}, [['h1', { _data: { _text: 'cc1' }, bindtap: 'click' }, ''],['h1', {}, [['span', { bindtap: 'click1' }, '点击调用wx.showModal']]],['h1', { _data: { _wxscode: () => {return self.__WXSCODE['page/index']['m1'].bind(null)().message } } }, '']]]
}let __NOWPAGE = ''global.Page = function (params) {global.__PAGES[__NOWPAGE] = {...params,setData: (data) => {// 这里可以加个diffrender(self.__APPCODE__[__NOWPAGE], {...params.data,...data}, __NOWPAGE)}}render(self.__APPCODE__[__NOWPAGE], params.data, __NOWPAGE)
}
global.wx = {showModal: function () {console.log('showModal')}
}addEventListener("message", (event) => {if (event.data.type === 'js') {__NOWPAGE = event.data.pageNameeval(event.data.data)} else if (event.data.type === 'event') {// 事件处理const pageArg = global.__PAGES[event.data.pageName]if (pageArg && typeof pageArg[event.data.name] === 'function') {pageArg[event.data.name].bind(pageArg)()}}
});function renderChild(element, pageData, pageName) {let arr = []if (element[1].bindtap) {element[1].dataset = { bindtap: element[1].bindtap, pageName: pageName }}if (typeof element[2] === 'string') {let text = element[2]if (element[1]._data) {if (typeof element[1]._data._text === 'string') {if (pageData[element[1]._data._text]) {text = pageData[element[1]._data._text]}} else if(typeof element[1]._data._wxscode === 'function') {text = element[1]._data._wxscode()}}arr.push(h(element[0], element[1], text))} else if (Array.isArray(element[2])){let newArr = []for (let index = 0; index < element[2].length; index++) {const ele = element[2][index];newArr.push(renderChild(ele, pageData, pageName)[0])}arr.push(h(element[0], element[1], newArr))}return arr
}function render(element, pageData, key) {const vnode = h('div', { props: { className: key }}, renderChild(element, pageData, key)[0])postMessage({type: 'view',data: JSON.stringify(vnode)})
}
service中需要关注的有:
1、__PAGES保存了Page函数的第一个参数
2、__APPCODE__应该是由编译器生成的,这里为了记录就直接放了最终的结果。
3、vnode由render函数生成并发送给视图层渲染。
非常感谢您花时间阅读我的博客文章。我非常清楚,我还有很多需要学习和改进的地方,但我希望这篇文章能够为您提供一些有用的信息和启发。如果您有任何问题或建议,请随时联系我,我将非常愿意听取您的意见。再次感谢您的阅读和支持!