🚀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.【特殊函数篇==(十篇一章,持续更新~)】
53 0
|
C++
24.【特殊函数篇==(十篇一章,持续更新~)】(一)
24.【特殊函数篇==(十篇一章,持续更新~)】
63 0
|
7月前
|
JSON JavaScript 前端开发
|
9月前
|
编译器 程序员 Linux
【C++入门(上篇)】C++入门学习
【C++入门(上篇)】C++入门学习
|
缓存 JavaScript 算法
🚀Svelte原理和进阶看这篇就够了🚀1
🚀Svelte原理和进阶看这篇就够了🚀
|
JavaScript
🚀从零开始学习JS基础7️⃣🚀
断点调试是指自己在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。断点调试可以帮助观察程序的运行过程。
134 0
🚀从零开始学习JS基础7️⃣🚀
|
存储 JavaScript 索引
🚀从零开始学习JS基础8️⃣🚀
数组是指一组数据的集合,其中的每个数据被称作元素,在数组中可以存放任意类型的元素。数组是一种将一组数据存储在单个变量名下的优雅方式。数组可以把一组相关的数据一起存放,并提供方便的访问(获取)方式。
136 0
🚀从零开始学习JS基础8️⃣🚀
|
存储 JavaScript 前端开发
🚀从零开始学习JS基础9️⃣🚀
 在 JS 里面,可能会定义非常多的相同代码或者功能相似的代码,这些代码可能需要大量重复使用。虽然 for循环语句也能实现一些简单的重复操作,但是比较具有局限性,此时我们就可以使用 JS 中的函数。     简答来说,函数就是封装了一段可被重复调用执行的代码块。通过此代码块可以实现大量代码的重复使用。
129 0
🚀从零开始学习JS基础9️⃣🚀
|
存储 JavaScript 前端开发
🚀从零开始学习JS基础🔟🚀
在 JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
200 0
🚀从零开始学习JS基础🔟🚀
|
JavaScript
🚀从零开始学习JS基础6️⃣🚀
在一个程序执行的过程中,各条代码的执行顺序对程序的结果是有直接影响的。很多时候我们要通过控制代码的执行顺序来实现我们要完成的功能。简单来说,流程控制就是来控制代码按照一定结构顺序来执行。
109 0
🚀从零开始学习JS基础6️⃣🚀

热门文章

最新文章