svelte响应式原理

这篇具有很好参考价值的文章主要介绍了svelte响应式原理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

svelte文件编译为js后的结构

源代码:

  <script lang="ts">
    let firstName = '张'
    let lastName = '三'
    let age = 18

    function handleChangeName() {
      firstName = '王'
      lastName = '二'
    }

    function handleChangeAge() {
      age = 28
    }
  </script>

  <div>
    <p>fullName is {firstName} {lastName}</p>
    <p>age is {age}</p>
    <div>
      <button on:click={handleChangeName}>change name</button>
      <button on:click={handleChangeAge}>change age</button>
    </div>
  </div>

编译后的js代码结构

  function create_fragment(ctx) {
  	const block = {
  		c: function create() {
  			// ...
  		},
  		m: function mount(target, anchor) {
  			// ...
  		},
  		p: function update(ctx, [dirty]) {
  			// ...
  		},
  		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 [firstName, lastName, age, handleChangeName, handleChangeAge];
  }

  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[0] : ret;
  			if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = 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[0] : ret;
      // 判断更新前和更新后的值是否相等,不等就调用make_dirty方法
  	if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = 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[0] === -1说明是第一次调用make_dirty方法。
  	if (component.$$.dirty[0] === -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));
  }

二进制运算 demo

  // 有采购商权限
  purchaser= 1 << 2 => 100
  // 有供应商商权限
  supplier = 1 << 1 => 010
  // 有运营权限
  admin =    1 << 0 => 001

  user1 = purchaser | supplier | admin => 111
  user2 = purchaser | supplier => 110

  // 用户是否有admin的权限
  user1 & admin = 111 & 001 = true
  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函数

function create_fragment(ctx) {
  let div1;
  let p0;
  let t0;
  let t1;
  let t2;
  let t3;
  let t4;
  let p1;
  let t5;
  let t6;
  let t7;
  let div0;
  let button0;
  let t9;
  let button1;
  let mounted;
  let dispose;

  const block = {
    // create生命周期时调用,调用浏览器的dom方法生成对应的dom。
    // element、text这些方法就是浏览器的
    // document.createElement、document.createTextNode这些原生方法
    c: function create() {
      div1 = element("div");
      p0 = element("p");
      t0 = text("fullName is ");
      t1 = text(/*firstName*/ ctx[0]);
      t2 = space();
      t3 = text(/*lastName*/ ctx[1]);
      t4 = space();
      p1 = element("p");
      t5 = text("age is ");
      t6 = text(/*age*/ ctx[2]);
      t7 = space();
      div0 = element("div");
      button0 = element("button");
      button0.textContent = "change name";
      t9 = space();
      button1 = element("button");
      button1.textContent = "change age";
    },
    l: function claim(nodes) {
      // ...
    },
    // 将create生命周期生成的dom节点挂载到target上面去
    m: function mount(target, anchor) {
      insert_dev(target, div1, anchor);
      append_dev(div1, p0);
      append_dev(p0, t0);
      append_dev(p0, t1);
      append_dev(p0, t2);
      append_dev(p0, t3);
      append_dev(div1, t4);
      append_dev(div1, p1);
      append_dev(p1, t5);
      append_dev(p1, t6);
      append_dev(div1, t7);
      append_dev(div1, div0);
      append_dev(div0, button0);
      append_dev(div0, t9);
      append_dev(div0, button1);

      if (!mounted) {
        dispose = [
          // 添加click事件监听
          listen_dev(button0, "click", /*handleChangeName*/ ctx[3], false, false, false),
          listen_dev(button1, "click", /*handleChangeAge*/ ctx[4], false, false, false)
        ];

        mounted = true;
      }
    },
    // 修改变量makedirty后,下一次微任务时会调用update方法
    p: function update(ctx, [dirty]) {
      if (dirty & /*firstName*/ 1) set_data_dev(t1, /*firstName*/ ctx[0]);
      if (dirty & /*lastName*/ 2) set_data_dev(t3, /*lastName*/ ctx[1]);
      if (dirty & /*age*/ 4) set_data_dev(t6, /*age*/ ctx[2]);
    },
    i: noop,
    o: noop,
    d: function destroy(detaching) {
      // ...
            mounted = false;
      // 移除事件监听
      run_all(dispose);
    }
  };

  return block;
}

再来看看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方法

	  function set_data_dev(text2, data) {
	    data = "" + data;
	    if (text2.wholeText === data)
	      return;
	    text2.data = data;
	  }

这个方法很简单,判断dom里面的值和新的值是否相等,如果不等直接修改dom的data属性,将最新值更新到dom里面去。文章来源地址https://www.toymoban.com/news/detail-760585.html

到了这里,关于svelte响应式原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • svelte-ui:基于svelte3桌面pc版组件库

    历时快两个月的svelte-ui组件库终于见面了。整体功能效果比较类似element-ui组件库,因为在开发之初就借鉴了其设计思路。所以用起来就比较熟悉。 svelte-ui共整理设计了32+组件,都是平时比较常用且出现频率比较高的一些组件。 引入组件 快速使用 在UI展示及使用语法上比较靠

    2024年02月12日
    浏览(33)
  • YOLO相关原理(文件结构、视频检测等)

    超参数进化是一种使用了genetic algorithm(GA)遗传算法进行超参数优化的一种方法。                   images文件夹内的文件和labels中的文件存在一一对应关系 activation functions 四种激活函数 对于视频检测,使用YOLO进行模型训练时怎样对视频进行标注呢? 以下是对视频进行标

    2024年02月13日
    浏览(22)
  • 操作系统原理 —— 文件的逻辑结构(二十三)

    这里说的 逻辑结构 ,就是指在用户看来,文件内部的数据应该是如何组织起来的,而 物理结构 指的是在操作系统看来,文件的数据是如何被存放的。 从 逻辑结构 结构来看,我们可以打开一个记事本,里面的文字内容从用户的角度来看就是无结构的,但是又从 Excel 来看,

    2024年02月08日
    浏览(53)
  • 理解.exe文件的结构原理即运行过程

    最近从网上整理了一些关于.exe文件的资料。 目录 一、什么是exe? 二、exe文件结构及原理 三、运行exe 1.操作系统创建进程,主线程 2.系统程序检查.exe文件头。 3.连接器嵌入exe文件头信息 4.导入所有需要的dll 5.初始化c/c++运行时库,初始化运行库的全局变量,内存分配之类的

    2024年02月07日
    浏览(20)
  • STDF - 基于 Svelte 和 Tailwind CSS 打造的移动 web UI 组件库,Svelte 生态里不可多得的优秀项目

    Svelte 是一个新兴的前端框架,组件库不多,今天介绍一款 Svelte 移动端的组件库。 关于 STDF STDF 是一个移动端的 UI 组件库,主要用来开发移动端 web 应用。和我之前介绍的很多 Vue 组件库不一样,STDF 是基于近来新晋 js 框架 Svelte 开发的,Svelte 是一个和 Vue / React 类似的 JavaS

    2024年02月12日
    浏览(39)
  • ip_vs 原理解析 (四)hook 后的开始 一

    本章重点: k8s 如何利用 ip_vs 实现源 IP 会话亲和性。 根据优先级依次是 ip_vs_reply4,ip_vs_remote_request4 全是查 conn_out_get,没有再 ACCEPT; 说明,这个 hook 是处理回复的 ip_vs,然后全是 nat 模式的 ipvs。即处理 realserver 回复报文时的处理。 conn_out_get 对应对应协议的连接查询,如

    2024年02月11日
    浏览(31)
  • win10录屏文件在哪?如何找到录制后的文件

    在工作和学习中,我们会遇到需要使用录屏工具录制电脑屏幕的情况,很多小伙伴在录制完win10电脑屏幕之后,找不到录制的视频文件。win10录屏文件在哪? 今天小编教大家如何找到电脑录屏文件和录制win10电脑屏幕的方法,如果您也找不到录制的视频文件,可以阅读下文学习

    2023年04月09日
    浏览(35)
  • 前端自测运行vue打包后的dist文件

    在Vue项目中,dist目录是代码打包之后生成的文件夹,其中包含了静态资源文件和打包后的JavaScript、CSS等文件。如果要在本地运行打包后的项目文件,可以使用简单的静态服务器来启动。 下面介绍一种使用Node.js中的http-server模块搭建本地服务器的方法: 先查看是否安装node 如

    2024年02月06日
    浏览(33)
  • 45、springboot 文件上传到指定磁盘路径 及 上传成功后的文件回显

    需求: 写一个文件上传的功能,把文件上传到指定的文件夹。 然后上传成功后的文件回显 MultipartAutoConfiguration:处理文件上传的类。 MultipartProperties :属性处理类,用来读取配置文件中的 spring.servlet.multipart.* 开头的配置属性 (1)前端页面的文件请求以“multipart/form-data”编

    2024年02月10日
    浏览(28)
  • Vivado生成压缩后的FPGA bit文件方法详解

    Vivado生成压缩后的FPGA bit文件方法详解 当我们使用Xilinx公司的FPGA开发环境Vivado进行开发时,通常会需要将设计好的程序烧录到目标板上进行测试和验证。而这个过程中,需要将设计好的FPGA bit文件通过一些方式传输到目标板上。 但是,FPGA bit文件通常都比较大,如果直接传输

    2024年02月06日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包