您当前的位置:首页 > 网站建设 > javascript
| php | asp | css | H5 | javascript | Mysql | Dreamweaver | Delphi | 网站维护 | 帝国cms | React | 考试系统 | ajax | jQuery | 小程序 |

JavaScript开发简单易懂的Svelte实现原理详解

51自学网 2022-02-21 13:36:42
  javascript

Svelte问世很久了,一直想写一篇好懂的原理分析文章,拖了这么久终于写了。

Demo1

首先来看编译时,考虑如下App组件代码:

<h1>{count}</h1><script>  let count = 0;</script>

这段代码经由编译器编译后产生如下代码,包括三部分:

create_fragment方法

count的声明语句

class App的声明语句

// 省略部分代码…function create_fragment(ctx) {  let h1;   return {    c() {      h1 = element("h1");      h1.textContent = `${count}`;    },    m(target, anchor) {      insert(target, h1, anchor);    },    d(detaching) {      if (detaching) detach(h1);    }  };} let count = 0; class App extends SvelteComponent {  constructor(options) {    super();    init(this, options, null, create_fragment, safe_not_equal, {});  }} export default App;

create_fragment

首先来看create_fragment方法,他是编译器根据AppUI编译而成,提供该组件与浏览器交互的方法,在上述编译结果中,包含3个方法:

c,代表create,用于根据模版内容,创建对应DOM Element。例子中创建H1对应DOM Element

h1 = element("h1");h1.textContent = `${count}`;

m,代表mount,用于将c创建的DOM Element插入页面,完成组件首次渲染。例子中会将H1插入页面:

insert(target, h1, anchor);

insert方法会调用target.insertBefore

function insert(target, node, anchor) {  target.insertBefore(node, anchor || null);}

d,代表detach,用于将组件对应DOM Element从页面中移除。例子中会移除H1

if (detaching) detach(h1);

detach方法会调用parentNode.removeChild

function detach(node) {  node.parentNode.removeChild(node);}

仔细观察流程图,会发现App组件编译的产物没有图中fragment内的p方法。

这是因为App没有变化状态的逻辑,所以相应方法不会出现在编译产物中。

可以发现,create_fragment返回的cm方法用于组件首次渲染。那么是谁调用这些方法呢?

SvelteComponent

每个组件对应一个继承自SvelteComponentclass,实例化时会调用init方法完成组件初始化,create_fragment会在init中调用:

class App extends SvelteComponent {  constructor(options) {    super();    init(this, options, null, create_fragment, safe_not_equal, {});  }}

总结一下,流程图中虚线部分在Demo1中的编译结果为:

fragment:编译为create_fragment方法的返回值

UIcreate_fragment返回值中m方法的执行结果

ctx:代表组件的上下文,由于例子中只包含一个不会改变的状态count,所以ctx就是count的声明语句

可以改变状态的Demo

现在修改Demo,增加update方法,为H1绑定点击事件,点击后count改变:

<h1 on:click="{update}">{count}</h1> <script>  let count = 0;  function update() {    count++;  }</script>

编译产物发生变化,ctx的变化如下:

// 从module顶层的声明语句let count = 0; // 变为instance方法function instance($$self, $$props, $$invalidate) {  let count = 0;   function update() {    $$invalidate(0, count++, count);  }   return [count, update];}

countmodule顶层的声明语句变为instance方法内的变量。之所以产生如此变化是因为App可以实例化多个:

// 模版中定义3个App<App/><App/><App/>// 当count不可变时,页面渲染为:<h1>0</h1><h1>0</h1><h1>0</h1>

count不可变时,所有App可以复用同一个count。但是当count可变时,根据不同App被点击次数不同,页面可能渲染为:

<h1>0</h1><h1>3</h1><h1>1</h1>

所以每个App需要有独立的上下文保存count,这就是instance方法的意义。推广来说,Svelte编译器会追踪<script>内所有变量声明:

  • 是否包含改变该变量的语句,比如count++
  • 是否包含重新赋值的语句,比如count = 1
  • 等等情况

一旦发现,就会将该变量提取到instance中,instance执行后的返回值就是组件对应ctx

同时,如果执行如上操作的语句可以通过模版被引用,则该语句会被$$invalidate包裹。

Demo2中,update方法满足:

万事OK自学网:51自学网_软件自学网_CAD自学网自学excel、自学PS、自学CAD、自学C语言、自学css3实例,是一个通过网络自主学习工作技能的自学平台,网友喜欢的软件自学网站。