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方法满足:

  • 包含改变count的语句 —— count
  • 可以通过模版被引用 —— 作为点击回调函数

所以编译后的update内改变count的语句被$$invalidate方法包裹:

// 源代码中的update
function update() {
  count  ;
} 
// 编译后instance中的update
function update() {
  $$invalidate(0, count  , count);
}
  • 更新ctx中保存状态的值,比如Demo2count
  • 标记dirty,即标记App UI中所有和count相关的部分将会发生变化
  • 调度更新,在microtask中调度本次更新,所有在同一个macrotask中执行的$$invalidate都会在该macrotask执行完成后被统一执行,最终会执行组件fragment中的p方法

p方法是Demo2中新的编译产物,除了p之外,create_fragment已有的方法也产生相应变化:

c() {
  h1 = element("h1");
  // count的值变为从ctx中获取
  t = text(/*count*/ ctx[0]);
},
m(target, anchor) {
  insert(target, h1, anchor);
  append(h1, t);
  // 事件绑定
  dispose = listen(h1, "click", /*update*/ ctx[1]);
},
p(ctx, [dirty]) {
  // set_data会更新t保存的文本节点
  if (dirty & /*count*/ 1) set_data(t, /*count*/ ctx[0]);
},
d(detaching) {
  if (detaching) detach(h1);
  // 事件解绑
  dispose();
}

p方法会执行$$invalidate中标记为dirty的项对应的更新函数。

Demo2中,App UI中只引用了状态count,所以update方法中只有一个if语句,如果UI中引用了多个状态,则p方法中也会包含多个if语句:

// UI中引用多个状态 
<h1 on:click="{count0  }">{count0}</h1>
<h1 on:click="{count1  }">{count1}</h1>
<h1 on:click="{count2  }">{count2}</h1>

对应p方法包含多个if语句:

p(new_ctx, [dirty]) {
  ctx = new_ctx;
  if (dirty & /*count*/ 1) set_data(t0, /*count*/ ctx[0]);
  if (dirty & /*count1*/ 2) set_data(t2, /*count1*/ ctx[1]);
  if (dirty & /*count2*/ 4) set_data(t4, /*count2*/ ctx[2]);
},

Demo2完整的更新步骤如下:

  1. 点击H1触发回调函数update
  2. update内调用$$invalidate,更新ctx中的count,标记countdirty,调度更新
  3. 执行p方法,进入dirty的项(即count)对应if语句,执行更新对应DOM Element的方法

以上就是JavaScript开发Svelte实现原理详解的详细内容,更多关于Svelte实现原理的资料请关注Devmax其它相关文章!

JavaScript开发简单易懂的Svelte实现原理详解的更多相关文章

  1. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  2. HTML5数字输入仅接受整数的实现代码

    这篇文章主要介绍了HTML5数字输入仅接受整数的实现代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. amaze ui 的使用详细教程

    这篇文章主要介绍了amaze ui 的使用详细教程,本文通过多种方法给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  4. html5简介_动力节点Java学院整理

    这篇文章主要介绍了html5简介,用于指定构建网页的元素,这些元素中的大多数都用于描述网页内容,有兴趣的可以了解一下

  5. ios 8 Homescreen webapp,关闭和打开iPad停止javascript

    我有一个适用于iPad的全屏HTML5网络应用程序,并且刚刚安装了IOS8来试用它,它一切正常,直到你关闭并重新启动iPad.一旦web应用程序重新启动javascript就会停止并加载新页面不会重新启动它.在iPad上的Safari中打开同一页面时,关闭和打开iPad会继续按预期工作.其他人注意到了这个或想出了一个解决方案吗?解决方法这似乎是我在iOS8.1.1更新中解决的.

  6. iOS 6 javascript与object.defineProperty的间歇性问题

    当访问使用较新的Object.defineProperty语法定义属性的对象的属性时,有没有其他人注意到新iOS6javascript引擎中的间歇性错误/问题?https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty我正在看到javascript失败的情况,说

  7. ios – 如何使用JSExport导出内部类的方法

    解决方法似乎没有办法将内部类函数导出到javascript.我将内部类移出并创建了独立的类,它起作用了.

  8. 静音iOS推送通知与React Native应用程序在后台

    我有一个ReactNative应用程序,我试图获得一个发送到JavaScript处理程序的静默iOS推送通知.我看到的行为是AppDelegate中的didReceiveRemoteNotification函数被调用,但是我的JavaScript中的处理程序不会被调用,除非应用程序在前台,或者最近才被关闭.我很困惑的事情显然是应用程序正在被唤醒,并且它的didReceiveRemoteNotifi

  9. ios – 内存泄漏与UIWebView和Javascript

    清楚地包含一个Javascript文件到我的HTML是使UIWebView泄漏内存.当我重复使用相同的UIWebView对象时,或者每当我有内容实例化一个新的漏洞时,会出现泄漏的事实,导致我认为必须有一些JavaScript文件被loadHTMLString处理,导致泄漏.有人知道如何解决这个问题吗?

  10. iOS应用程序的UI自动化测试如何与乐器和Javascript

    从WWDC2010视频会议中了解iOS应用程序的自动化UI测试,但没有实践.从代码项目project,我们可以有一个例子.这个问题在这里听到有涉及这个的人.任何限制?解决方法我建议从AlexWollmer开始使用thisblogpost.他创建了一个非常有用的JavaScript库:tuneup_jswithtest()函数,它允许测试分离和有用的帮助者以及为自动化仪器编写测试的断言.

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部