翼度科技»论坛 编程开发 JavaScript 查看内容

svelte响应式原理

11

主题

11

帖子

33

积分

新手上路

Rank: 1

积分
33
svelte文件编译为js后的结构

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

举报 回复 使用道具