svelte响应式原理
svelte文件编译为js后的结构源代码:
<p>fullName is {firstName} {lastName}</p>
<p>age is {age}</p>
<button on:click={handleChangeName}>change name</button>
<button on:click={handleChangeAge}>change age</button>
编译后的js代码结构
function create_fragment(ctx) {
const block = {
c: function create() {
// ...
},
m: function mount(target, anchor) {
// ...
},
p: function update(ctx, ) {
// ...
},
d: function destroy(detaching) {
// ...
}
};
return block;
}
function instance($$self, $$props, $$invalidate) {
let firstName = '张';
let lastName = '三';
let age = 18;
function handleChangeName() {
$$invalidate(0, firstName = '王');
$$invalidate(1, lastName = '二');
}
function handleChangeAge() {
$$invalidate(2, age = 28);
}
return ;
}
class Name extends SvelteComponentDev {
constructor(options) {
init(this, options, instance, create_fragment, safe_not_equal, {});
}
}初始化调用init方法
function init(component, options, instance, create_fragment, ...,dirty = [-1]) {
// $$属性为组件的实例
const $$ = component.$$ = {
...
// dirty的作用是标记哪些变量需要更新,
// 在update生命周期的时候将那些标记的变量和对应的dom找出来,更新成最新的值。
dirty,
// fragment字段为一个对象,对象里面有create、mount、update等方法
fragment: null,
// 实例的ctx属性是个数组,存的是组件内的顶层变量、方法等。按照定义的顺序存储
ctx: [],
...
}
// ctx属性的值为instance方法的返回值。
// instance方法就是svelte文件编译script标签代码生成的。
// instance方法的第三个参数为名字叫$$invalidate的箭头函数,
// 在js中修改变量的时候就会自动调用这个方法
$$.ctx = instance
? instance(component, options.props || {}, (i, ret, ...rest) => {
const value = rest.length ? rest : ret;
if ($$.ctx && not_equal($$.ctx, $$.ctx = value)) {
make_dirty(component, i);
}
return ret;
})
: [];
// 调用create_fragment方法
// 并且在后续对应的生命周期里面调用create_fragment方法返回的create、mount、update等方法
$$.fragment = create_fragment ? create_fragment($$.ctx) : false;
}点击change name按钮,修改firstName和lastName的值
let firstName = '张'
let lastName = '三'
let age = 18
function handleChangeName() {
// firstName变量第一个定义,所以这里是0,并且将新的firstName的值传入$$invalidate方法
$$invalidate(0, firstName = '王');
// lastName变量第二个定义,所以这里是1,并且将新的firstName的值传入$$invalidate方法
$$invalidate(1, lastName = '二');
}
// ...再来看看invalidate函数的定义,invalidate函数就是在init时调用instance的时候传入的第三个参数
(i, ret, ...rest) => {
// 拿到更新后的值
const value = rest.length ? rest : ret;
// 判断更新前和更新后的值是否相等,不等就调用make_dirty方法
if ($$.ctx && not_equal($$.ctx, $$.ctx = value)) {
// 第一个参数为组件对象,第二个参数为变量的index。
// 当更新的是firstName变量,firstName是第一个定义的,所以这里的i等于0
// 当更新的是lastName变量,lastName是第二个定义的,所以这里的i等于1
make_dirty(component, i);
}
return ret;
}make_dirty方法的定义
function make_dirty(component, i) {
// dirty初始化的时候是由-1组成的数组,dirty === -1说明是第一次调用make_dirty方法。
if (component.$$.dirty === -1) {
dirty_components.push(component);
// 在下一个微任务中调用create_fragment方法生成对象中的update方法。
schedule_update();
// 将dirty数组的值全部fill为0
component.$$.dirty.fill(0);
}
component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31));
}
再来看看component.$$.dirty[(i / 31) | 0] |= (1
页:
[1]