仿写vue - 实现数据拦截和简单的依赖收集
lua简单仿写vue:step1 粗略实现操作拦截和响应
step1 拦截赋值和取值操作
拦截数据访问和获取关系
function newProxy(model)
local t = {}
local raw = {}
if type(model) ~= "table" then
return model
end
for k, v in pairs(model) do
raw[k] = newProxy(v)
end
setmetatable(t, {
__index = function(t, key)
print("get key " .. key)
return raw[key]
end,
__newindex = function(t, key, value)
local oldValue = raw[key]
print("set key:" .. key)
raw[key] = newProxy(value)
end,
__len = function()
return #raw
end
})
return t
end
model = {
test1 = "test1",
test2 = "test2"
}
model = newProxy(model)
print(model.test1)
model.test2 = "test2_modify"
model.test3 = "test3"
model.test4 = {
test5 = "test5"
}
model.test4.test5 = "test6"
model[#model + 1] = "test4"
model[#model + 1] = 4
table.insert(model, 5)
重写__index、__newindex实现对raw数据的获取和赋值拦截,重写__len方法实现数组赋值和table.insert的获取,并且实现深度proxy;
step2 构建依赖关系
基于dep实现依赖关系
local Dep = {}
local TrackedMarkers = {
wasTracked = 0,
newTracked = 1,
}
local createTrack = function(effects)
local dep = new
end
Dep.__index = Dep
function Dep.Dep()
local t = setmetatable({}, Dep)
t.listeners = {}
return t
end
function Dep:addWatcher(watcher)
table.insert(self.listeners, watcher)
end
function Dep:removeWatcher(watcher)
local index = nil
for k,v in pairs(self.listeners) do
if v == watcher then
index = v
break
end
end
table.remove(table, index)
end
function Dep:notify(newValue, oldValue)
for _, v in ipairs(self.listeners) do
v(newValue, oldValue)
end
end
Dep.Target = nil
return Dep
对于每一个proxy获取一个新的proxy和dep
local Dep = require("Dep")
-- 测试代码
function dumpTable(t)
local res = ""
if t == nil then
return "nil"
end
if type(t) == "table" then
for k, v in pairs(t) do
res = res.."\n"..k..":"..dumpTable(v)
end
else
res = res..tostring(t)
end
return res
end
function newProxy(model)
local t = {}
local raw = {}
-- 捕获的依赖关系
local dep = Dep.Dep()
if type(model) ~= "table" then
return model
end
for k, v in pairs(model) do
raw[k] = newProxy(v)
end
setmetatable(t, {
__index = function (t, key)
-- watch
if Dep.Target then
print("add watcher "..key)
dep:addWatcher(Dep.Target)
end
return raw[key]
end,
__newindex = function (t, key, value)
local oldValue = raw[key]
dep:notify(value, oldValue)
print("key:"..key)
print("value:"..dumpTable(value))
print("oldValue:"..dumpTable(oldValue))
raw[key] = newProxy(value)
end
})
return t
end
local function doWatch(effect, callback)
Dep.Target = callback
effect()
Dep.Target = nil
end
step3 测试代码
local model = {
test1 = "test1",
test2 = "test2",
test3 = {
test4 = "test3.test4"
}
}
local modela = {
test1 = "test1",
test2 = "test2",
test3 = {
test4 = "test4"
}
}
model = newProxy(model)
modela = newProxy(modela)
local t = nil
doWatch(function()
model.
t = model.test3.test4
modela.test4 = model.test3
end,
function ()
print("do watch when deep value change")
end)
model.test3.test4 = "change test4"
model.test3 = "change test3"
doWatch(function() print("Do watch function ")
local test = model[1]end
, function()
print("model callback") end)
model[2] = 1
model[1] = 2
table.insert(model, 4)
for k, v in pairs(model) do
print(k)
end
TODO:还需要做的地方
目前需要处理的地方主要有几点:
1、这种简单的dep:notify的方式暂时无法解决自增的问题,需要记录当前回调避免递归爆栈;
2、现在还没能实现数组部分的传递,暂时只能实现hash部分的操作拦截;
3、pairs和ipairs操作还是在proxy上操作,后续需要尽量让proxy的操作和model的操作尽可能一致;
4、dep的回调可能会出现多次调用,所以最好实现Set;