小程序学习五(实现一个简单的基础库)

因为有个公司又是搞小程序容器的,而且老是问我相关问题,不得已只能再次复习一下。
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的生成微信是放在了视图层,放在视图层的话首次渲染速度和事件处理都会方便很多。

非常感谢您花时间阅读我的博客文章。我非常清楚,我还有很多需要学习和改进的地方,但我希望这篇文章能够为您提供一些有用的信息和启发。如果您有任何问题或建议,请随时联系我,我将非常愿意听取您的意见。再次感谢您的阅读和支持!

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值