SvelteJS与ReactJS渲染差异(重绘/重排)

这是我对DOM和浏览器如何工作的幼稚理解

只要DOM中的某些内容(真实dom)发生变化,浏览器就会重新绘制或重排DOM。因此,用更简单的术语来说,每次DOM更改时,浏览器都需要重新计算CSS,进行布局并重新绘制网页。这是在真正的dom中花费时间的原因。

因此,React随此虚拟DOM一起提供,它的实际作用是将更改批量进行批处理,然后调用一次将其应用于实际域。因此,最大程度地减少了回流和重涂。

那斯维尔特呢。如果直接操作DOM,它将如何控制浏览器的重绘/重排。

zhoulang914 回答:SvelteJS与ReactJS渲染差异(重绘/重排)

两个库都将需要对dom进行的更改减至最少。区别在于他们找出最小变化集的方式。

React的方法是在内存中表示dom(虚拟dom)的表示形式。设置状态后,它将再次运行渲染过程以创建另一个虚拟dom。它比较前后的差异,找到更改的内容,然后将所有更改推送到真实的dom。

Svelte的方法是,当您设置变量时,它会设置一个标志,将该变量标记为已更改。它知道哪些变量依赖于其他变量,因此它逐步浏览所有因变量并重新计算它们,从而建立了需要更改的内容的列表。然后将这些更改推送到dom。

,

除了上面的(正确)答案之外:Svelte还可以“编译”您提供给它的代码,因此最终代码可以在没有库运行时的情况下执行(与React相比)。而且它创建了易于阅读的代码,因此绝对有可能了解其内部工作原理。

注意:这将是更长的答案-仍然遗漏了有关斯维尔特(Svelte)幕后发生的事情的许多细节。但我希望这有助于揭开某些幕后神秘面纱。同样,这也是Svelte从v3.16.x开始执行操作的方式。由于这是内部的,因此可能会更改。但是,我仍然发现了解实际情况总是很有意义的。

所以,我们开始。

最重要的是:Svelte教程具有一项有用的功能,可让您查看生成的代码(在“结果”窗格旁边)。乍一看可能有点吓人,但很快您就可以掌握它了。

以下代码将基于此示例(但经过进一步简化):Svelte tutorial - reactivity/assignments

我们的示例组件定义(即 App.svelte )如下所示:

<script>
    let count = 0;

    function handleClick() {
        count += 1;
    }
</script>

<button on:click={handleClick}>{count}</button>

基于该组件定义,Svelte编译器创建一个函数,该函数将创建一个“片段”,该片段接收“上下文”并与之交互。

function create_fragment(ctx) {
    let button;
    let t;
    let dispose;

    return {
        c() {
            button = element("button");
            t = text(/*count*/ ctx[0]);
            dispose = listen(button,"click",/*handleClick*/ ctx[1]);
        },m(target,anchor) {
            insert(target,button,anchor);
            append(button,t);
        },p(ctx,[dirty]) {
            if (dirty & /*count*/ 1) set_data(t,/*count*/ ctx[0]);
        },i: noop,o: noop,d(detaching) {
            if (detaching) detach(button);
            dispose();
        }
    };
}

片段负责与DOM交互,并将随组件实例一起传递。简而言之,

中的代码
  • “ c”将在 create 上运行(在内存中创建DOM元素并设置事件处理程序)
  • “ m”将在 mount 上运行(将元素附加到DOM)
  • “ p”将在更新上运行,即某些事物(包括道具)发生变化
  • “ i” /“ o”与前奏/外奏(即转场)有关
  • “ d”将在 destroy
  • 上运行

注意:诸如 element set_data 之类的功能实际上很容易实现。 例如,功能 element 只是 document.createElement

的包装。
function element(name) {
    return document.createElement(name);
}

上下文(ctx)将包含所有实例变量以及函数。它不过是一个简单的数组。由于Svelte在编译时“知道”每个索引的含义,因此它可以在其他地方硬引用这些索引。

此代码实质上定义了实例上下文:

function instance($$self,$$props,$$invalidate) {
    let count = 0;

    function handleClick() {
        $$invalidate(0,count += 1);
    }

    return [count,handleClick];
}

实例方法和 create_fragment 都将通过另一个函数调用 init 进行调用。它涉及的内容更多,因此,您可以查看此link to the source。而不是在此处复制和粘贴。

$$ invalidate 将确保将 count 变量设置为脏变量并安排更新。当下一次更新运行时,它将查看所​​有“脏”组件并进行更新。这是如何发生的实际上更多是实现细节。如果有兴趣,请在flush function中设置一个断点。

实际上,如果您确实想更深入一点,我建议克隆template应用程序,然后创建一个简单的组件,对其进行编译,然后检查“ bundle.js”。如果您删除源映射或将其禁用,也可以调试实际代码。

因此,例如,如下设置rollup.config.js:

    output: {
        sourcemap: false,format: 'iife',name: 'app',file: 'public/build/bundle.js'
    },plugins: [
        svelte({
            dev: false,

注意:如上所示,我建议还将 dev模式设置为 false ,因为这将创建更简洁的代码。

一个简洁的功能:我们的应用运行之后,您还可以访问 app 变量(由于将其捆绑为一个立即窗口,因此该变量已分配给全局窗口对象-调用函数表达式)。

因此,您可以打开控制台并简单地说

console.dir(app)

会产生类似这样的结果

App
    $$:  
        fragment: {c: ƒ,m: ƒ,p: ƒ,i: ƒ,o: ƒ,…}
        ctx: (2) [0,ƒ]
        props: {count: 0}
        update: ƒ noop()
        not_equal: ƒ safe_not_equal(a,b)
        bound: {}
        on_mount: []
        on_destroy: []
        before_update: []
        after_update: []
        context: Map(0) {}
        callbacks: {}
        dirty: [-1]
        __proto__: Object
    $set: $$props => {…}

一个很酷的功能是您可以自己 $ set 方法来更新实例。例如这样的

app.$set({count: 10})

也有Svelte DevTools试图使Svelte的内部结构更加平易近人。当我亲自试用它们时,它们似乎会以某种方式影响我的应用程序的渲染性能,因此我不会自己使用它们。但是肯定值得一看。

好吧,你在那里。我知道这仍然是技术性的,但是我希望它有助于更​​好地了解Svelte编译代码的功能。

本文链接:https://www.f2er.com/3085833.html

大家都在问