Alpine.js 中的 x-data 内部为什么不是响应式的?

huangapple go评论77阅读模式
英文:

Why Alpine.js magic inside x-data isn't reactive?

问题

我在Alpine.js中编写了我的第一个魔术功能。它返回页面滚动比例,一个介于0-1之间的数字,最终带有一个scale(例如100以获取百分比)和一个round

/**
 * @param {import("alpinejs").Alpine} Alpine
 */
export default (Alpine) => {
  const data = Alpine.reactive({ ratio: 0 });

  const calculate = () =>
    (document.documentElement.scrollTop || document.body.scrollTop) /
      ((document.documentElement.scrollHeight || document.body.scrollHeight) -
        document.documentElement.clientHeight) || 0;
  const update = () => (data.ratio = calculate());

  window.addEventListener('load', update);
  window.addEventListener('scroll', update, { passive: true });

  Alpine.magic('pageScroll', () => (scale, round) => {
    let value = data.ratio;
    if (!isNaN(scale)) {
      value *= scale;
    }
    if (!isNaN(round)) {
      value = value.toFixed(round);
    }

    return value;
  });
};

使用x-bind(请参见此示例并滚动页面)运行得很好

<div class="fixed top-0 left-0 w-4 bg-red-500 duration-100"
     x-data x-bind:style="{ height: $pageScroll(100) + '%' }">
</div>

问题出现在我尝试将$pageScroll(100)分配给组件的ratio属性时(以便重用它),魔术不再具有反应性:

<div class="fixed top-0 right-0 w-4 bg-green-500 duration-100"
     x-data="{ scroll: $pageScroll(100) }"
     x-bind:style="{ height: scroll + '%' }"
     x-text="scroll">
</div>

有人知道这是否是正常行为以及如何解决这个问题吗?

由于魔术注册了一个事件监听器,附加到x-bindx-text都会创建更多监听器,从而降低性能。

英文:

I wrote my first magic in Alpine.js. It returns the page scroll ratio, a number between 0-1, eventually with a scale (i.e. 100 to get the percenteage) and a round:

/**
 * @param {import(&quot;alpinejs&quot;).Alpine} Alpine
 */
export default (Alpine) =&gt; {
  const data = Alpine.reactive({ ratio: 0 });

  const calculate = () =&gt;
    (document.documentElement.scrollTop || document.body.scrollTop) /
      ((document.documentElement.scrollHeight || document.body.scrollHeight) -
        document.documentElement.clientHeight) || 0;
  const update = () =&gt; (data.ratio = calculate());

  window.addEventListener(&#39;load&#39;, update);
  window.addEventListener(&#39;scroll&#39;, update, { passive: true });

  Alpine.magic(&#39;pageScroll&#39;, () =&gt; (scale, round) =&gt; {
    let value = data.ratio;
    if (!isNaN(scale)) {
      value *= scale;
    }
    if (!isNaN(round)) {
      value = value.toFixed(round);
    }

    return value;
  });
};

Works just fine using x-bind (see this pen and scroll the page):

&lt;div class=&quot;fixed top-0 left-0 w-4 bg-red-500 duration-100&quot;
     x-data x-bind:style=&quot;{ height: $pageScroll(100) + &#39;%&#39; }&quot;&gt;
&lt;/div&gt;

The problem is when I try to assign the $pageScroll(100) to a ratio property of the component (in order to reuse it), the magic isn't reactive anymore:

&lt;div class=&quot;fixed top-0 right-0 w-4 bg-green-500 duration-100&quot;
     x-data=&quot;{ scroll: $pageScroll(100) }&quot;
     x-bind:style=&quot;{ height: scroll + &#39;%&#39; }&quot;
     x-text=&quot;scroll&quot;&gt;
&lt;/div&gt;

Does anyone know if this is the normal behaviour and how can I solve the problem?

Since the magic register an event listener, attaching to both x-bind and x-text would create more listeners, degrading performances.

答案1

得分: 1

魔法属性在定义 x-data 时不可用,因为 x-data 在组件上下文 (this) 存在之前运行。
这里 有一篇关于这个主题的好文章,我从中摘取了引用的短语。

您可以尝试使用 init() 函数来解决这个问题,我认为这样的解决方案可能有效(我现在无法测试):

&lt;div class=&quot;fixed top-0 right-0 w-4 bg-green-500 duration-100&quot;
     x-data=&quot;{ scroll: 0, scale: 100 }&quot;
     x-init=&quot;scroll = $pageScroll(scale)&quot;
     x-bind:style=&quot;{ height: scroll + '&#39;%&#39; }&quot;
     x-text=&quot;scroll&quot;
&gt;
&lt;/div&gt;
英文:

The magic properties are not available when defining x-data "since x-data is run before the component context (this) exists"
Here a good article on the subject from which I extracted the quoted phrase

You can try to work around the problem using the init() function, I think that a solution like this can work (I can't test it now):

&lt;div class=&quot;fixed top-0 right-0 w-4 bg-green-500 duration-100&quot;
     x-data=&quot;{ scroll: 0, scale: 100 }&quot;
     x-init=&quot;scroll = $pageScroll(scale)&quot;
     x-bind:style=&quot;{ height: scroll + &#39;%&#39; }&quot;
     x-text=&quot;scroll&quot;
&gt;
&lt;/div&gt;

huangapple
  • 本文由 发表于 2023年7月27日 22:35:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/76780814.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定