svelte文件编译为js后的结构

源代码:

  1. <script lang="ts">
  2. let firstName = '张'
  3. let lastName = '三'
  4. let age = 18
  5. function handleChangeName() {
  6. firstName = '王'
  7. lastName = '二'
  8. }
  9. function handleChangeAge() {
  10. age = 28
  11. }
  12. </script>
  13. <div>
  14. <p>fullName is {firstName} {lastName}</p>
  15. <p>age is {age}</p>
  16. <div>
  17. <button on:click={handleChangeName}>change name</button>
  18. <button on:click={handleChangeAge}>change age</button>
  19. </div>
  20. </div>

编译后的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. }

二进制运算 demo

  1. // 有采购商权限
  2. purchaser= 1 << 2 => 100
  3. // 有供应商商权限
  4. supplier = 1 << 1 => 010
  5. // 有运营权限
  6. admin = 1 << 0 => 001
  7. user1 = purchaser | supplier | admin => 111
  8. user2 = purchaser | supplier => 110
  9. // 用户是否有admin的权限
  10. user1 & admin = 111 & 001 = true
  11. user2 & admin = 110 & 001 = false

再来看看component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31));。dirty数组中每一位能够标记31个变量是否为dirty。

(i / 31) | 0就是i/31然后取整。

  • 比如i=0,计算结果为0。
  • i=1,计算结果为0。
  • i=32,计算结果为1。

(1 << (i % 31)),1左移的位数为i和31求余的值。

  • 比如i=0,计算结果为1<<0 => 01。
  • i=1,计算结果为1 << 1 => 10。
  • i=32,计算结果为1<<1 => 10。

当i=0时这行代码就变成了component.$$.dirty[0] |= 01,由于dirty数组在前面已经被fill为0了,所以代码就变成了component.$$.dirty[0] = 0 | 01 => component.$$.dirty[0] = 01。说明从右边数第一个变量被标记为dirty。

同理当i=1时这行代码就变成了component.$$.dirty[0] |= 10 =>component.$$.dirty[0] = 0 | 10 => component.$$.dirty[0] = 10。说明从右边数第二个变量被标记为dirty。

create_fragment函数

  1. function create_fragment(ctx) {
  2. let div1;
  3. let p0;
  4. let t0;
  5. let t1;
  6. let t2;
  7. let t3;
  8. let t4;
  9. let p1;
  10. let t5;
  11. let t6;
  12. let t7;
  13. let div0;
  14. let button0;
  15. let t9;
  16. let button1;
  17. let mounted;
  18. let dispose;
  19. const block = {
  20. // create生命周期时调用,调用浏览器的dom方法生成对应的dom。
  21. // element、text这些方法就是浏览器的
  22. // document.createElement、document.createTextNode这些原生方法
  23. c: function create() {
  24. div1 = element("div");
  25. p0 = element("p");
  26. t0 = text("fullName is ");
  27. t1 = text(/*firstName*/ ctx[0]);
  28. t2 = space();
  29. t3 = text(/*lastName*/ ctx[1]);
  30. t4 = space();
  31. p1 = element("p");
  32. t5 = text("age is ");
  33. t6 = text(/*age*/ ctx[2]);
  34. t7 = space();
  35. div0 = element("div");
  36. button0 = element("button");
  37. button0.textContent = "change name";
  38. t9 = space();
  39. button1 = element("button");
  40. button1.textContent = "change age";
  41. },
  42. l: function claim(nodes) {
  43. // ...
  44. },
  45. // 将create生命周期生成的dom节点挂载到target上面去
  46. m: function mount(target, anchor) {
  47. insert_dev(target, div1, anchor);
  48. append_dev(div1, p0);
  49. append_dev(p0, t0);
  50. append_dev(p0, t1);
  51. append_dev(p0, t2);
  52. append_dev(p0, t3);
  53. append_dev(div1, t4);
  54. append_dev(div1, p1);
  55. append_dev(p1, t5);
  56. append_dev(p1, t6);
  57. append_dev(div1, t7);
  58. append_dev(div1, div0);
  59. append_dev(div0, button0);
  60. append_dev(div0, t9);
  61. append_dev(div0, button1);
  62. if (!mounted) {
  63. dispose = [
  64. // 添加click事件监听
  65. listen_dev(button0, "click", /*handleChangeName*/ ctx[3], false, false, false),
  66. listen_dev(button1, "click", /*handleChangeAge*/ ctx[4], false, false, false)
  67. ];
  68. mounted = true;
  69. }
  70. },
  71. // 修改变量makedirty后,下一次微任务时会调用update方法
  72. p: function update(ctx, [dirty]) {
  73. if (dirty & /*firstName*/ 1) set_data_dev(t1, /*firstName*/ ctx[0]);
  74. if (dirty & /*lastName*/ 2) set_data_dev(t3, /*lastName*/ ctx[1]);
  75. if (dirty & /*age*/ 4) set_data_dev(t6, /*age*/ ctx[2]);
  76. },
  77. i: noop,
  78. o: noop,
  79. d: function destroy(detaching) {
  80. // ...
  81. mounted = false;
  82. // 移除事件监听
  83. run_all(dispose);
  84. }
  85. };
  86. return block;
  87. }

再来看看update方法里面的 if (dirty & /*firstName*/ 1) set_data_dev(t1, /*firstName*/ ctx[0]);

当firstName的值被修改时,firstName是第一个定义的变量,i=0。按照上面的二进制计算component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31));,此时dirty[0]= 0 |(1<<0)=01

if (dirty & /*firstName*/ 1) set_data_dev(t1, /*firstName*/ ctx[0]);就变成了if (01 & /*firstName*/ 1) set_data_dev(t1, /*firstName*/ ctx[0]);。此时if条件满足,执行set_data_dev(t1, /*firstName*/ ctx[0]);。这里的t1就是t1 = text(/*firstName*/ ctx[0]);,使用firstName变量的dom。

同理当lastName的值被修改时,lastName是第二个定义的变量,i=1。按照上面的二进制计算component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31));,此时dirty[0]= 0 |(1<<1)=10

if (dirty & /*lastName*/ 2) set_data_dev(t3, /*lastName*/ ctx[1]);就变成了if (10 & /*lastName*/ 2) set_data_dev(t3, /*lastName*/ ctx[1]);。此时if条件满足,执行set_data_dev(t3, /*lastName*/ ctx[1]);。这里的t3就是t3 = text(/*lastName*/ ctx[1]);,使用lastName变量的dom。

set_data_dev方法

  1. function set_data_dev(text2, data) {
  2. data = "" + data;
  3. if (text2.wholeText === data)
  4. return;
  5. text2.data = data;
  6. }

这个方法很简单,判断dom里面的值和新的值是否相等,如果不等直接修改dom的data属性,将最新值更新到dom里面去。

svelte响应式原理的更多相关文章

  1. Vue.js学习 Item12 – 内部响应式原理探究

    深入响应式原理 大部分的基础内容我们已经讲到了,现在讲点底层内容.Vue.js 最显著的一个功能是响应系统 —— 模型只是普通对象,修改它则更新视图.这让状态管理非常简单且直观,不过理解它的原理也很重 ...

  2. Vue.js响应式原理

      写在前面 因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出. 文章的原地址:answershuto/learnV ...

  3. vue.js响应式原理解析与实现

    vue.js响应式原理解析与实现 从很久之前就已经接触过了angularjs了,当时就已经了解到,angularjs是通过脏检查来实现数据监测以及页面更新渲染.之后,再接触了vue.js,当时也一度很 ...

  4. 深入浅出Vue基于“依赖收集”的响应式原理(转)

    add by zhj: 文章写的很通俗易懂,明白了Object.defineProperty的用法 原文:https://zhuanlan.zhihu.com/p/29318017 每当问到VueJS ...

  5. vue深入响应式原理

    vue深入响应式原理 深入响应式原理 — Vue.jshttps://cn.vuejs.org/v2/guide/reactivity.html 注意:这里说的响应式不是bootsharp那种前端UI ...

  6. 深度解析 Vue 响应式原理

    深度解析 Vue 响应式原理 该文章内容节选自团队的开源项目 InterviewMap.项目目前内容包含了 JS.网络.浏览器相关.性能优化.安全.框架.Git.数据结构.算法等内容,无论是基础还是进 ...

  7. Vue 数据响应式原理

    Vue 数据响应式原理 Vue.js 的核心包括一套“响应式系统”.“响应式”,是指当数据改变后,Vue 会通知到使用该数据的代码.例如,视图渲染中使用了数据,数据改变后,视图也会自动更新. 举个简单 ...

  8. [vuejs] 深入响应式原理

    深入响应式原理 现在是时候深入一下了!Vue 最独特的特性之一,是其非侵入性的响应式系统.数据模型仅仅是普通的 JavaScript 对象.而当你修改它们时,视图会进行更新.这使得状态管理非常简单直接 ...

  9. vue 数组 新增元素 响应式原理 7种方法

    1.问题 思考一个问题,以下代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8" ...

  10. vue 数据劫持 响应式原理 Observer Dep Watcher

    1.vue响应式原理流程图概览 2.具体流程 (1)vue示例初始化(源码位于instance/index.js) import { initMixin } from './init' import ...

随机推荐

  1. FPGA常用IP核

    前言: 芯片行业中的IP,一般称为IP(Intellectual Property)核,是具有知识产权核的集成电路芯核的总称.说白了就是厂家实现的具有特定功能工具,然后我们可以直接调用,就相当于是函数 ...

  2. Spring Cloud 整合

    前言 玩SpringCloud之前最好懂SpringBoot,别搞撑死骆驼的事.Servlet整一下变成Spring:SSM封装.加入东西就变为SpringBoot:SpringBoot再封装.加入东 ...

  3. Kafka 如何保证消息消费的全局顺序性

    哈喽大家好,我是咸鱼 今天我们继续来讲一讲 Kafka 当有消息被生产出来的时候,如果没有指定分区或者指定 key ,那么消费会按照[轮询]的方式均匀地分配到所有可用分区中,但不一定按照分区顺序来分配 ...

  4. Alist手动安装并使用教程

    一.官方文档及下载地址 1.官方文档 AList文档 2.下载地址 alist · GitHub 二.下载并解压文件 以Windows为例,下载指定版本的文件. 三.运行 1.解压文件并进入文件夹: ...

  5. HTML5语法总结大全

    参考书籍: <HTML与CSS3基础教程> 参考视频: HTML5完整教学通俗易懂 2023新版前端Web开发HTML5+CSS3+移动web视频教程,前端web入门首选黑马程序员 参考网 ...

  6. 0x02.加密和编码

    识别算法编码类型 看密文位数 看密文特征(数字.字母.大小写.符号等) 看当前密文存在的地方(web.数据库.操作系统等) 密码存储加密 md5:16位和32位由0-9和a-f组成的字符串 ,该加密方 ...

  7. SpringCore完整学习教程7,入门级别

    本章可以说是完结,下一章可能讲kotlin+springboot 本章从第九章开始: 9. Creating Your Own Auto-configuration 如果您在开发共享库的公司工作,或者 ...

  8. HBase应用方案

    HBase性能优化方法:

  9. 笔记3:Tensorflow2.0实战之MNSIT数据集

    最近Tensorflow相继推出了alpha和beta两个版本,这两个都属于tensorflow2.0版本:早听说新版做了很大的革新,今天就来用一下看看 这里还是使用MNSIT数据集进行测试 导入必要 ...

  10. SpringBoot整合Filter过滤器

    话不多说,直接上核心代码 1.先创建一个Filter类 package com.qbb.reggie.filter; import com.alibaba.fastjson.JSON; import ...