V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
qiuyk
V2EX  ›  JavaScript

一个魔方计时器小程序以及...热更新

  •  1
     
  •   qiuyk · 2019-04-10 15:16:45 +08:00 · 3158 次点击
    这是一个创建于 2061 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前段时间折腾魔方的时候,发现微信小程序里居然没有一个好用的魔方计时器,作为一个魔方爱好者(伪)和码农(真),那是不能忍的,于是就只能自己动手了。

    熟悉魔方计时器操作的魔友应该很容易就知道 QTimer 怎么用。需要注意的是,QTimer 不鼓励大家随意修改自己的成绩,因此只有在完成的时候,左下角计时 Tab 会出现小红点,此时双击才会出现+2、DNF 和删除的操作。除此以外,QTimer 不允许大家修改和删除已经录入统计的成绩。在完成 5、12、50 和 100 次复原时,右下角统计 Tab 会相应的提醒。


    你以为这样就完了?当然没有,我还试了一下微信小程序热更新的可行性。魔方计时器有个很重要的功能就是生成打乱公式,目前这个模块更新频繁,所以我决定基于 Sval 试试热更新这个模块。

    首先,我将打乱生成的模块进行打包,exports 暴露出需要用到的函数,打包发布到公用的 CDN 上,Unpkg: QTimer

    于是,在 App 初始化的时候,检查热更新模块版本,然后判断是否需要更新,如果需要则再将热更新模块拉回来,存在 LocalStorage 里。

    // utils/patch.js
    const version = wx.getStorageSync('__generate_scramble__version')
    const code = wx.getStorageSync('__generate_scramble')
    
    if (code) {
      // 如果已经缓存,先跑一遍代码
      worker.postMessage({ type: 'run', data: code })
    }
    
    wx.request({
      url: 'https://unpkg.com/qtimer?meta',
      success(res) {
        const integrity = res.data.integrity
        if (integrity && integrity !== version) {
          wx.request({
            url: 'https://unpkg.com/qtimer',
            success(res) {
              wx.setStorageSync('__generate_scramble__version', integrity)
              wx.setStorageSync('__generate_scramble', res.data)
              // 如果有更新,再跑一遍代码,覆盖之前的 exports
              worker.postMessage({ type: 'run', data: res.data })
            }
          })
        }
      }
    })
    

    因为打乱生成是 CPU 密集操作,我把他放在了 worker 里做。那么我们将拉取回来的代码,直接 postMessage 给 worker 去跑。同时,worker 把代码执行的 exports 给暴露出来共主线程调用。

    // workers/hotload.js
    const interpreter = require('interpreter/index.js')
    
    worker.onMessage(req => {
      if (req.type === 'run') {
        interpreter.run(req.data) // 跑代码
      } else {
        const { generateScramble } = interpreter.exports
        const scramble = generateScramble(req.type) // 调用热更新模块的方法生成打乱
        worker.postMessage({ type: req.type, data: scramble })
      }
    })
    

    因为小程序本身禁用了evalnew FunctionsetTimeout等等,所以 interpreter 就是通过 Sval 构造的解释器。

    // workers/interpreter/index.js
    const Sval = require('sval')
    const init = require('init.js')
    
    const interpreter = new Sval()
    
    // 先初始化一下需要用到的方法
    // 免得第一次打开 LocalStorage 里面没有缓存
    // 同时代码还没有拉回来,等拉取到代码会将初始化覆盖掉
    interpreter.run(init)
    
    module.exports = interpreter
    

    最后,愉快地在业务代码里面找 worker 要结果就好了。

    // app.js
    const worker = wx.createWorker('workers/hotload.js')
    App({
      // Worker 调用封装
      getWorkerResult(req, done) {
        worker.postMessage(req)
        worker.onMessage(done)
      }
    })
    
    // pages/timer/index.js
    const app = getApp()
    Page({
      getScramble() {
        // 调用 Worker 生成打乱
        app.getWorkerResult({ type: '3x3' }, res => {
          this.setData({ scramble: res.data })
        })
      }
    })
    

    不过说实话,性能不怎么样,在 xs 上,7 阶打乱生成算法用原生的跑需要 2ms,而用 Sval 跑需要 600ms。对于 QTimer 来说,打乱的生成不影响体验,但是在打乱生成模块稳定以后,还是需要替换成原生实现,打包进小程序里。

    目前我只是将解释器跑在逻辑层,理论上应该也能跑在视图层(wxs),但是对视图(wxml)的影响还十分有限,所以热更新只能对业务逻辑进行更新。当然,你甚至可以十分疯狂地将整个 Page 或者整个 App 的逻辑做热更新,只是是否有必要而已。

    const Sval = require('sval')
    const interpreter = new Sval()
    
    interpreter.import({ Page, wx, app: getApp() })
    interprete.run(`
      Page({
        data: {},
        onShow() {},
        customMethod() {}
      })
    `)
    
    

    Github: QTimer

    Github: Sval

    3 条回复    2019-04-11 19:44:28 +08:00
    pnongrata
        1
    pnongrata  
       2019-04-10 15:30:26 +08:00
    很棒诶。

    不允许删除的考虑是什么
    qiuyk
        2
    qiuyk  
    OP
       2019-04-10 15:40:53 +08:00
    @pnongrata 就是希望大家正视自己的成绩,尽量模拟比赛的环境。不要因为某次成绩较差影响了平均成绩,在事后把某次成绩删掉。

    (其实,只是我比较懒,不想加这个功能 23333 )
    qiuyk
        3
    qiuyk  
    OP
       2019-04-11 19:44:28 +08:00
    还有一个问题就是,目前 worker 不能发送请求,所以只能主线程请求热更新代码,然后 postMessage 给 worker,如果热更新代码太多,会影响到初始化性能,所以还要控制好热更新模块的大小
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2619 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 11:33 · PVG 19:33 · LAX 03:33 · JFK 06:33
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.