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

news/2024/7/20 1:49:52 标签: 小程序, 学习, apache

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

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


http://www.niftyadmin.cn/n/5394759.html

相关文章

状态机-----

1.原理 同步的意思就是状态的跳转都是在时钟的作用下跳转的&#xff0c;有限是指状态机中状态的个数是有限的。两种状态机的共同点都是状态的跳转只和输入有关&#xff0c;区别就是如果最后的输出只和当前状态有关而与输入无关&#xff0c;则是moore型状态机。如果最后的输出不…

【Java EE初阶二十五】简单的表白墙(一)

1. 前端部分 1.1 前端代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"wid…

绝对路径拼接漏洞 [NISACTF 2022]babyupload

打开题目 最开始以为是文件上传的漏洞 结果发现无论我们上传什么文件都会显示bad filename 去网上看了大佬的wp知道 我们直接去看源代码得到提示 /source 那我们去访问一下这个路径看看 得到一个下载文件 用记事本打开得到 源代码如下 from flask import Flask, request, r…

动态规划之第 N 个泰波那契数/三步问题【leetCode】【算法】

动态规划动态规划之第 N 个泰波那契数/三步问题 动态规划LeetCode题目第 N 个泰波那契数求解1求解2&#xff08;滚动数组&#xff09; 三步问题求解1求解2&#xff08;滚动数组&#xff09; 动态规划 如果问题是由重叠的子问题构成的&#xff0c;那就可以用动态规划&#xff08…

dpvs 笔记

20、 基于ECMP的多活负载均衡策略 当使用ospf/ECMP来实现高可用&#xff0c;所以keepalived不需要配置vrrp功能。keepalived只使用后端服务健康检查功能。 Equal-Cost Multi-Path Routing (ECMP) ECMP根据SIP-DIP对来选择路由 keepalived 健康检查机制说明 keepalived TCP chec…

Python之词频统计(自然语言处理)

背景 词频统计是指对一段文本中每个单词出现的次数进行计数分析。这种分析有助于了解文本的重点词汇、主题或作者的写作风格。如果你有一个特定的文本或想要分析某些内容的词频&#xff0c;你可以提供文本&#xff0c;我可以为你进行简单的词频统计。 例如&#xff0c;如果你…

职业规划,平面设计类职业岗位的任职资格

平面设计师是指具有平面设计能力&#xff0c;并且能够掌握基本知识&#xff0c;从事设计业务的技术型人才。他们需要了解客户端需求&#xff0c;从品牌需要的角度&#xff0c;设计和策划广告全案&#xff0c;运用相应媒介和品牌信息&#xff0c;从而满足客户的需要&#xff0c;…

Open CASCADE学习|视图

目录 Mainwin.h Mainwin.cpp Mainwin.h ​#pragma once#include <QtWidgets/QMainWindow>#include "Displaywin.h"#include "OCC.h"class Mainwin : public QMainWindow{ Q_OBJECTpublic: Mainwin(QWidget* parent nullptr); ~Mainwin();​pri…