🚀Svelte原理和进阶看这篇就够了🚀1

简介: 🚀Svelte原理和进阶看这篇就够了🚀

作为后起之秀,Svelte到底是怎么俘获大批开发者的呢?我们先从它的特性开始说起。

Svelte特性

  • 🚀简洁的语法
  • ✈无虚拟DOM
  • 🚗正在的相应式

🚀简洁的语法

官网给出了一个三大框架的同样功能的例子作比较

React写法

import React, { useState } from 'react';
export default () => {
  const [a, setA] = useState(1);
  const [b, setB] = useState(2);
  function handleChangeA(event) {
    setA(+event.target.value);
  }
  function handleChangeB(event) {
    setB(+event.target.value);
  }
  return (
    <div>
      <input type="number" value={a} onChange={handleChangeA}/>
      <input type="number" value={b} onChange={handleChangeB}/>
      <p>{a} + {b} = {a + b}</p>
    </div>
  );
};

Vue写法

<template>
  <div>
    <input type="number" v-model.number="a">
    <input type="number" v-model.number="b">
    <p>{{a}} + {{b}} = {{a + b}}</p>
  </div>
</template>
<script>
  export default {
    data: function() {
      return {
        a: 1,
        b: 2
      };
    }
  };
</script>

Svelte写法

<script>
    let a = 1;
    let b = 2;
</script>
<input type="number" bind:value={a}>
<input type="number" bind:value={b}>
<p>{a} + {b} = {a + b}</p>

结果一目了然了。

Svelte认为,你写的代码越多,造成更多的bug的概率越大。

无虚拟DOM

Svelte放弃了流行的虚拟DOM方案,虽然虚拟DOM足够的快,但是虚拟DOM最致命的问题是不管状态是否发生变化,都会被重新计算并且更新。

React会从应用根节点开始重新加载,Vue会从所在组件开始重新加载。

Svelte回归到了原生JavaScript,在Svelte中,每个组件都有一个对应的JavaScript类,称为“组件实例”。当组件状态发生变化时,Svelte会生成一个新的组件实例,并使用差异算法比较新旧组件实例的DOM结构,然后更新需要更改的部分。

Svelte使用的差异算法与传统的虚拟DOM实现类似,都是将新旧DOM树进行比较,找出需要更新的部分。但是,Svelte使用了一些优化技巧来减少比较的复杂性和DOM操作的数量。

  • 💎使用key标记DOM
  • 💎合并DOM(移位算法)、减少DOM
  • 💎缓存DPOM(可变长缓存)
💎使用key标记DOM

使用“key”属性来帮助Svelte识别相同类型的元素。当Svelte在比较新旧DOM树时遇到相同类型的元素时,它会使用“key”属性来判断这些元素是否相同,并避免进行不必要的更新。这可以减少比较的复杂性和DOM操作的数量,从而提高性能。

💎合并DOM(移位算法)

Svelte还使用了一种称为“移位”算法的技巧来进一步优化差异算法。移位算法是一种将多个连续的DOM操作合并为单个操作的技术,从而减少DOM操作的数量和复杂性。

另外,还针对{{#if}}指令做了优化,Svelte会使用DOM元素的插入和移除来隐藏或显示元素,而不是使用CSSdisplay:none等方式。这种方法也可以减少DOM操作的数量和复杂性。

💎缓存DOM(可变长缓存)

Svelte还使用了一种称为“可变长度缓存”(VLC)的技术来进一步优化差异算法。可变长度缓存是一种将最近使用的元素缓存起来,以便它们可以更快地被访问和使用的技术。当Svelte比较新旧DOM树时,它可以使用VLC缓存来快速查找和访问最近使用的元素,从而减少比较的复杂性和时间复杂度。

所以,Svelte虽然没有虚拟DOM,但是它的性能反而更好。

🚗真正的响应式

什么是响应式?就是当一个值发生改变时,使用这个值的地方做出相应的改变。

如果不同的人设计响应式的功能,它的使用方案也会不尽相同。

例如,早期的Svelte写法如下:

const { count } = this.get();
this.set({
  count: count + 1
});

React的写法

const { count } = this.state;
this.setState({
  count: count + 1
});

hook

const [count, setCount] = useState(props.count)
setCount(count + 1)

Vue3写法

const { count } = defineProps(props)
count ++

这些方案都是基于一些响应式的Api实现的响应式功能。

Svelte意识到最好的API就是根本没有 API。我们可以直接使用。

let count = 0
count +=1

以上就是Svelte的主要特性。总结下:

  • Svelte拥有接近原生JavaScript的写法
  • Svelte没有虚拟DOM,使用原生DOM描述组件
  • Svelte没有Api

Svelte编译原理

既然Svelte没有Api,那到底是怎么追踪变量变化的呢?

接下来我们由简单到复杂,来看看Svelte的编译结果。

首先我们看看,下面的代码会被编译成啥样的:

<h1>Hello world!</h1>
/* App.svelte generated by Svelte v3.59.1 */
import {
  SvelteComponent,
  detach,
  element,
  init,
  insert,
  noop,
  safe_not_equal
} from "svelte/internal";
function create_fragment(ctx) {
  let h1;
  return {
    c() {
      h1 = element("h1");
      h1.textContent = "Hello world!";
    },
    m(target, anchor) {
      insert(target, h1, anchor);
    },
    p: noop,
    i: noop,
    o: noop,
    d(detaching) {
      if (detaching) detach(h1);
    }
  };
}
class App extends SvelteComponent {
  constructor(options) {
    super();  
    init(this, options, null, create_fragment, safe_not_equal, {});
  }
}
export default App;

很明显,组件编译之后,会返回一个继承了SvelteComponent的类,并且在构造函数中执行了init方法,它的其中一个参数为在组件中定义的create_fragment函数。

这个函数会返回一个对象,包含组件对应的的create``mount``update``delete操作。由于上面的代码中是个静态的字符串,所以p对应的值为noopno operate没有操作。

接下来,我们修改下代码如下:

<script>
  let count = 0
</script>
<h1>Hello world!</h1>

此时组件编译之后,仅仅出现了

let count = 0

其余没有变化,(所以代码里没有用到的变量,我们应该即时删除)

接着,我们新增代码如下:

<script>
let count = 0
</script>
<h1 on:click={() => count++}>Hello world!</h1>
function create_fragment(ctx) {
  let h1;
  let mounted;
  let dispose;
  return {
    c() {
      h1 = element("h1");
      h1.textContent = "Hello world!";
    },
    m(target, anchor) {
      insert(target, h1, anchor);
      if (!mounted) {
        dispose = listen(h1, "click", /*click_handler*/ ctx[1]);
        mounted = true;
      }
    },
    // ...
    d(detaching) {
      if (detaching) detach(h1);
      mounted = false;
      dispose();
    }
  };
}
function instance($$self, $$props, $$invalidate) {
  let count = 0;
  const click_handler = () => $$invalidate(0, count++, count);
  return [count, click_handler];
}

我们可以看到在mounted之后使用listen方法新增了一个针对h1click方法的监听事件,并且在delete阶段移除监听事件。

同时多了个实例方法instance,它的返回值是count的实际值,以及修改count的处理函数。请记住这里,后面还会提到。

值得注意的是h1click事件的参数是/*click_handler*/ ctx[1])

function instance($$self, $$props, $$invalidate) {
  let count = 0;
  const click_handler = () => $$invalidate(0, count++, count);
  return [count, click_handler];
}

此时,init方法也发生了改变

// 之前
init(this, options, null, create_fragment, safe_not_equal, {});
// 之后
init(this, options, instance, create_fragment, safe_not_equal, {});

最关键的来了,此时我们继续修改代码如下

<script>
let count = 0
</script>
<h1 on:click={() => count++}>Hello world!{count}</h1>

再去查看编译结果,create_fragment发生了重大变化

function create_fragment(ctx) {
  let h1;
  let t0;
  let t1;
  let mounted;
  let dispose;
  return {
    c() {
      h1 = element("h1");
      h1.textContent = "Hello world!";
      t0 = text("Hello world!");
      t1 = text(/*count*/ ctx[0]);
    },
    m(target, anchor) {
      insert(target, h1, anchor);
      append(h1, t0);
      append(h1, t1);
      if (!mounted) {
        dispose = listen(h1, "click", /*click_handler*/ ctx[1]);
        mounted = true;
      }
    },
    // ...
    d(detaching) {
      if (detaching) detach(h1);
      mounted = false;
      dispose();
    }
  };
}

值得注意的是,t1的值为/* count */ ctx[0]

instance的返回值为[count, click_handler]

结合前面的内容,得出一个明显的结论:instance的返回值就是create_fragment的参数!

好了,啰里吧嗦这么多,我们终于可以讨论开头的问题了

既然Svelte没有Api,那到底是怎么追踪变量变化的呢?

svelte在编译时,会检测所有变量的赋值行为,并将变化后的值和赋值的行为,作为创建片段的参数。

这就是svelte朴素的编译原理。

相关文章
|
3月前
|
存储 Python
Python编程入门:从零开始的代码之旅
【9月更文挑战第4天】本文将带领初学者步入Python的世界,通过简明的语言和直观的例子,逐步揭示编程的乐趣。我们将一起构建基础的数据结构,探索控制语句的奥秘,并实现简单的函数。无论你是编程新手还是希望巩固基础,这篇文章都是你理想的起点。让我们开始吧,一步步将代码块搭建成思维的宫殿!
39 2
|
7月前
|
搜索推荐 开发者
【Uniapp 专栏】探究 Uniapp 组件化开发的奥秘
【5月更文挑战第12天】Uniapp的组件化开发模式正引领移动应用开发潮流,提升开发效率并简化维护。通过将应用拆分为独立、可复用的组件,开发者能快速构建和优化功能,降低出错风险。基础组件满足基本需求,自定义组件则针对特定业务场景。Uniapp提供简洁的组件定义、通信支持及组件库管理,促进数据共享和功能协同。然而,组件设计需考虑通用性、扩展性和依赖管理。组件化开发在Uniapp中日益重要,为开发者创造更多价值,激发创新潜力。
91 4
【Uniapp 专栏】探究 Uniapp 组件化开发的奥秘
|
7月前
|
存储 移动开发 前端开发
【Uniapp 专栏】Uniapp 架构设计与原理探究
【5月更文挑战第12天】Uniapp是一款用于跨平台移动应用开发的框架,以其高效性和灵活性脱颖而出。它基于HTML、CSS和Vue.js构建视图层,JavaScript处理逻辑层,管理数据层,实现统一编码并支持原生插件扩展。通过抽象平台特性,开发者能专注于业务逻辑,提高开发效率。尽管存在兼容性和复杂性挑战,但深入理解其架构设计与原理将助力开发者创建高质量的跨平台应用。随着技术进步,Uniapp将继续在移动开发领域扮演重要角色。
244 1
【Uniapp 专栏】Uniapp 架构设计与原理探究
|
6月前
|
算法 开发工具 数据安全/隐私保护
练手必备!Python编程实战—23个有趣的实战项目带你快速进阶
Python的练手项目有哪些值得推荐? 已经有6.4W关注,700W次浏览,回答都有450条了,本来遇到这种问题我是不会回答的,毕竟已经有太多人给出了答案,我再去回答就没什么意义了。 但想了想确实有很多刚学Python的并不清楚从哪里去找项目来练手,于是就有了这篇文章,基于这个目的,我也是找了好久,最后还是选择了分享这份手册,毕竟里面有细致的讲解,确实更适合练手一些。
|
JavaScript 开发者
🚀Svelte原理和进阶看这篇就够了🚀2
🚀Svelte原理和进阶看这篇就够了🚀
|
7月前
|
编译器 程序员 Linux
【C++入门(上篇)】C++入门学习
【C++入门(上篇)】C++入门学习
|
XML IDE 编译器
【C++】C++ 基础进阶【二】开发技巧
C++基础进阶,关于开发环境开发工具的一些便捷使用方式,提高生产力
171 0
【C++】C++ 基础进阶【二】开发技巧
|
前端开发 C# 数据库管理
(3) MasaFramework 入门第三篇,使用MasaFramework
(3) MasaFramework 入门第三篇,使用MasaFramework
111 0
(3) MasaFramework 入门第三篇,使用MasaFramework
|
前端开发 JavaScript 中间件
我学会了,react上手知识点(上篇)
本篇文章记录的是上手react并做好一个React项目的大概知识点,比如jsx本质、生命周期、组件嵌套、父子通信、组件通信、插槽机制、跨组件通信、setState、react性能优化、ref、受控和非受控组件、高阶组件、React中使用样式。
152 0
我学会了,react上手知识点(上篇)
|
前端开发 JavaScript API
渐进式手敲Vue3.0框架 - 2万字以上 - 持续更新
为了更好的理解Vue3源码我计划使用渐进式的方法完成一个简写版的Vue框架。
174 0

热门文章

最新文章