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

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

Svelte运行时原理

现在我们又有了一个新的问题。我们已经可以感知到值的变化,那是怎么将值得变化更新到页面中的了。

你可能马上想到的是create_fragment返回的updata方法啊。这里仅仅是提供了更新页面DOM的方法,那是什么样的时机调用这个更新方法的呢?

init方法

其实,svelte的编译结果是运行时运行的代码。在进入运行时,首先执行init方法,该方法大致流程如下:

  • 💎初始化状态
  • 💎初始化周期函数
  • 💎执行instance方法,在回调函数中标记脏组件
  • 💎执行所有beforeUpdate生命周期的函数
  • 💎执行创建片段create_fragment函数
  • 💎挂载当前组件并执行create_fragement返回的m(mounted)方法
  • 💎执行flush方法

你可以跳过这段代码,不影响阅读

export function init(
  component,
  options,
  instance,
  create_fragment,
  not_equal,
  props,
  append_styles,
  dirty = [-1]
) {
  const parent_component = current_component;
  set_current_component(component);
  const $$: T$$ = component.$$ = {
    fragment: null,
    ctx: [],
    // state
    props,
    update: noop,
    not_equal,
    bound: blank_object(),
    // lifecycle
    on_mount: [],
    on_destroy: [],
    on_disconnect: [],
    before_update: [],
    after_update: [],
    context: new Map(options.context || (parent_component ? parent_component.$$.context : [])),
    // everything else
    callbacks: blank_object(),
    dirty,
    skip_bound: false,
    root: options.target || parent_component.$$.root
  };
  append_styles && append_styles($$.root);
  let ready = false;
  $$.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)) {
            if (!$$.skip_bound && $$.bound[i]) $$.bound[i](value);
            if (ready) make_dirty(component, i);
        }
        return ret;
    })
    : [];
  $$.update();
  ready = true;
  run_all($$.before_update);
  // `false` as a special case of no DOM component
  $$.fragment = create_fragment ? create_fragment($$.ctx) : false;
  if (options.target) {
    if (options.hydrate) {
        start_hydrating();
        const nodes = children(options.target);
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        $$.fragment && $$.fragment!.l(nodes);
        nodes.forEach(detach);
    } else {
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        $$.fragment && $$.fragment!.c();
    }
    if (options.intro) transition_in(component.$$.fragment);
    mount_component(component, options.target, options.anchor, options.customElement);
    end_hydrating();
    flush();
  }
  set_current_component(parent_component);
}

看起来,flush方法很可能才是我们需要的答案。

flush方法

flush的方法主要做了一件事:

遍历需要更新的组件(dirty_components),然后更新它,并且调用afterUpdate方法。

export function flush() {
  // Do not reenter flush while dirty components are updated, as this can
  // result in an infinite loop. Instead, let the inner flush handle it.
  // Reentrancy is ok afterwards for bindings etc.
  if (flushidx !== 0) {
    return;
  }
  const saved_component = current_component;
  do {
    // first, call beforeUpdate functions
    // and update components
    try {
        while (flushidx < dirty_components.length) {
            const component = dirty_components[flushidx];
            flushidx++;
            set_current_component(component);
            update(component.$$);
        }
    } catch (e) {
        // reset dirty state to not end up in a deadlocked state and then rethrow
        dirty_components.length = 0;
        flushidx = 0;
        throw e;
    }
    set_current_component(null);
    dirty_components.length = 0;
    flushidx = 0;
    // then, once components are updated, call
    // afterUpdate functions. This may cause
    // subsequent updates...
    for (let i = 0; i < render_callbacks.length; i += 1) {
        const callback = render_callbacks[i];
        if (!seen_callbacks.has(callback)) {
            // ...so guard against infinite loops
            seen_callbacks.add(callback);
            callback();
        }
    }
    render_callbacks.length = 0;
  } while (dirty_components.length);
  while (flush_callbacks.length) {
    flush_callbacks.pop()();
  }
  update_scheduled = false;
  seen_callbacks.clear();
  set_current_component(saved_component);
}

我们再来看看具体的更新操作update函数做了啥

  • 首先执行所有的before_update方法
  • 然后执行create_fragment返回的p(update)方法
function update($$) {
    if ($$.fragment !== null) {
        $$.update();
        run_all($$.before_update);
        const dirty = $$.dirty;
        $$.dirty = [-1];
        $$.fragment && $$.fragment.p($$.ctx, dirty);
        $$.after_update.forEach(add_render_callback);
    }
}

好了,我们总结下:在运行时

  • 💎首先,初始化状态、初始化周期函数
  • 💎接着,执行instance方法,在回调函数中标记脏组件
  • 💎接着,执行所有beforeUpdate生命周期的函数
  • 💎然后,执行创建片段create_fragment函数
  • 💎接着,挂载当前组件并执行create_fragement返回的m(mounted)方法
  • 💎然后,执行flush方法
  • 💎首先,执行所有的before_update方法
  • 💎然后,执行create_fragment返回的p(update)方法
  • 💎最后,执行afterUpdate方法

总结

好了,今天的分享就这些了,总的来说,Svelte的响应式原理虽然很朴素,但是却拥有了更好的性能,同时也降低了开发者的记忆负担。我觉得这是svelte最成功的地方。

如果你发现文章有错误的地方,请及时告诉我,十分感谢。

今天的分享就到这了,如果你觉得还不错,也可以关注我的微信公众号:萌萌哒草头将军

相关文章
|
C++
24.【特殊函数篇==(十篇一章,持续更新~)】(一)
24.【特殊函数篇==(十篇一章,持续更新~)】
49 0
|
5月前
|
存储
🚀链表理论:基础概念与实战技巧!
【2月更文挑战第8天】
154 43
|
5月前
|
编译器 程序员 Linux
【C++入门(上篇)】C++入门学习
【C++入门(上篇)】C++入门学习
|
5月前
|
存储 开发工具 文件存储
Python的核心知识点整理大全66(已完结撒花)
Python的核心知识点整理大全66(已完结撒花)
98 4
|
10月前
|
存储 安全 编译器
【C++】C++入门详解 II【深入浅出 C++入门 这一篇文章就够了】(下)
【C++】C++入门详解 II【深入浅出 C++入门 这一篇文章就够了】(上)
95 0
|
10月前
|
Java 编译器 测试技术
【C++】C++入门详解 II【深入浅出 C++入门 这一篇文章就够了】(上)
【C++】C++入门详解 II【深入浅出 C++入门 这一篇文章就够了】
54 0
|
12月前
|
缓存 JavaScript 算法
🚀Svelte原理和进阶看这篇就够了🚀1
🚀Svelte原理和进阶看这篇就够了🚀
|
算法 容器
算法-蓝桥基础知识-持续更新中....
算法-蓝桥基础知识-持续更新中....
|
存储 自然语言处理 安全
C++从入门到精通(第一篇) :C++的入门(基础语法的整理)
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作 用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字 污染,namespace关键字的出现就是针对这种问题的。
133 0
C++从入门到精通(第一篇) :C++的入门(基础语法的整理)
|
存储 IDE 编译器
C#学习(第二篇)
在上篇文章中我们学习了C#强大的编程功能、C#发展史、C#开发环境、C#和.Net Framework的关系、C#集成开发环境(IDE)、C#程序结构以及编译并执行程序。而这次我要向大家隆重介绍C#基本语法、C#关键字以及C#数据类型。
C#学习(第二篇)