因为有个公司又是搞小程序容器的,而且老是问我相关问题,不得已只能再次复习一下。
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.dataset
if (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) => {
// 这里可以加个diff
render(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.pageName
eval(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函数生成并发送给视图层渲染。
ps: 其中vnode的生成微信是放在了视图层,放在视图层的话首次渲染速度和事件处理都会方便很多。
非常感谢您花时间阅读我的博客文章。我非常清楚,我还有很多需要学习和改进的地方,但我希望这篇文章能够为您提供一些有用的信息和启发。如果您有任何问题或建议,请随时联系我,我将非常愿意听取您的意见。再次感谢您的阅读和支持!