壹影博客.
我在下午4点钟开始想你
lua实现数据响应式思路分享及代码分析
  • 2023-12-17日
  • 1评论
  • 1583围观

lua实现数据响应式思路分享及代码分析

大家都知道在前端vue框架中,我们只需要修改数据,框架就能够自动的帮我们把数据渲染到页面,他实现了数据与页面的解耦,开发者只需要绑定数据 关注业务逻辑

在研究了vue的部分源码后,有这样一个想法,这种思想和做法能不能用到其他的地方?我们能不能在lua中做到数据响应式呢?什么是数据响应式?

数据响应式

本质是当数据变化时会自动运行一些相关函数,这个只是比较简单的说法

问题

光看这本质描述很多人认为这不是很简单么,无非就是监听数据变化呗,在Js中我们可以用Object.defineProperty做jget、set监听,在lua里面我们可以用原表来完成,但问题是如果我们要写一个自己的通用框架还有很多问题要处理,譬如这个get和set的函数我想交给框架去处理,不然每次使用我都要去手写get、set这样非常麻烦,其次就是这个get和set的监听他只能监听到数值的变化、但是框架并不知道是谁在调用、谁在使用,但是为了开发方便 我们需要在get的时候收集记录是哪个函数在用我、在set的时候去执行用我的函数,完成一个响应式的数据变动

其实这个也不是非常新鲜的事情,对于一个对象而言,在vue框架中已经提供了两个专业的名词来描述这种行为,
依赖收集:记录是哪个函数在用我
派发更新:执行用我的函数

那让我们在lua里面实现看看,代码如下

-- 我们用面向对象的思想来封装 响应式数据工具
--定义全局监听类
_G.ObserveClass = {}

--定义全局监听服务类
_G.ObserveServiceClass={}

--定义全局函数调用 --临时调用
_G.__func=null

--实例化监听服务类
function ObserveServiceClass:new()

  --实例化监听类
  data,addListener = ObserveClass:new()
  --监听服务类内部成员变量
  local obj={
     _data= data,--数据
     _function= {},
     _property=addListener
  }

  -- 返回设置了内部变量的原表对象 其中{__index = self}
  --代表如果在obj中无法找到某个属性或方法时,Lua将会在关联的表self中查找
  return setmetatable(obj, {__index = self})
end

--定义监听服务类的getDate方法 当前方法用于获取设置监听后的table
function ObserveServiceClass:getDate()
  --获取  监听服务类 的内部成员变量
  return self._data
end

--定义监听服务类的addListener方法 提供一个key 这个方法是让我们可以手动定义get和set的钩子
--在get的时候执行xxx set的时候执行xxx key是table的key值
function ObserveServiceClass:addListener(key)
      --_function为当前监听服务类的 监听到的函数 也就是收集到的函数
      --如果不存在就创建一个 他是一个二维数组的结构 (两层table)
      if not self._function[key] then
        self._function[key]={}
      end


      --依赖收集:记录是哪个函数在用我
      function records(arr)
        --判断全局函数变量是否有值 并且 arr数组是否存在该函数 如果不存在就写入
        if(_G.__func)then
          table.insert(arr,_G.__func)
        end
      end

      --派发更新:执行用我的函数
      function applyFun(arr)
        for i = 1, #arr do
          arr[i]();
        end
      end

     --钩子 这个函数是用于传递get的执行事件
     -- 获取属性时的回调函数
     function get(value)
       records(self._function[key])
       --print('SET ---- >>> ',value)
     end

     --钩子 这个函数是用于传递set的执行时间
     -- 设置属性时的回调函数
     function set(value)
       applyFun(self._function[key])
       --print('GET ---- >>>  ' ,value)
     end
   --设置回调GET、SET 这个是在监听类里面用的
   self._property(key,"set",set)
   self._property(key,"get",get)
   return true
end

--监听服务类 向外暴露一个自动执行函数 这个函数的意义是
--我们一个包含有受监听的数据的函数 他会在调用之前去拿到调用该变量的fun
--这个fun被挂载到全局 执行完毕后销毁,依靠这种方法就可以在get的时候拿到用我们的函数
function ObserveServiceClass:autoRun(fn)
  _G.__func=fn; --记录函数
  fn();
  _G.__func=null; --最后再将全局变量设置为null
end

--实例化监听类
function ObserveClass:new()
  -- 存储表的数据 自动帮我们创建一个table这个table是会返回给外部用的
  local data = {}

  -- 存储监听的回调函数
  local observers = {}

  --设置元表内容 设置STE、GET钩子
  local mt = {
  __index = function(_, key)
    Get(key)  -- 读取属性时触发相应的观察者回调
    return data[key]
  end,
  __newindex = function(_, key, value)
    local oldValue = data[key]
    data[key] = value
    Set(key, value, oldValue)  -- 设置属性时触发相应的观察者回调
  end
  }

  --设置监听对象 这个函数也会返回到外部进行包装 他接受收table的key
  --  type 字符串 get、set
  -- callback get、set的回调函数
  function addObserver(key,type, callback)
    if not observers[key] then
      observers[key] = {}
    end
    if not observers[key][type] then
      observers[key][type] = {}
    end

     -- 添加观察者回调函数到对应属性的观察者列表中
    table.insert(observers[key][type], callback)
  end

  function Get(key)
  local callbacks = observers[key]["get"]
  if callbacks then
    for _, callback in ipairs(callbacks) do
    -- 触发观察者回调函数,并传递属性值作为参数
    callback(data[key])
    end
  end
  end

  function Set(key, value, oldValue)
  local callbacks = observers[key]["set"]
  if callbacks then
    for _, callback in ipairs(callbacks) do
    -- 触发观察者回调函数,并传递新值和旧值作为参数
    callback(value, oldValue)
    end
  end
  end
  -- 返回一个设置了元表的空表以及添加观察者的方法
  return setmetatable({}, mt), addObserver
end

--使用

--实例化一个观察服务类
observeService = ObserveServiceClass:new()

--服务类里面的data数据table
newData=observeService:getDate()

--设置一个监听的key 当我们去get或 set这个key的时候就会触发事件
observeService:addListener("title")

--定义 当key被修改后需要执行的函数 这个函数会自动执行
function autoFun()
  print(newData.title)
end

--挂在这个自动的函数
observeService:autoRun(autoFun)

--当我们去修改这个title的时候就会自动去执行autoFun
newData.title="测试内容....数据响应式"

最后

这个lua的数据响应式只是简单的实现,还存在一些问题,借助这种思路我们可以将其完善

@Author 壹影

发表评论

WilliambrAsp

Lv.1 @回复 沙发

水微晶玻尿酸 - 八千代


https://yachiyo.com.tw/hyadermissmile-injection/