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

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

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

问题

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

  1. /**
  2. * @param {import("alpinejs").Alpine} Alpine
  3. */
  4. export default (Alpine) => {
  5. const data = Alpine.reactive({ ratio: 0 });
  6. const calculate = () =>
  7. (document.documentElement.scrollTop || document.body.scrollTop) /
  8. ((document.documentElement.scrollHeight || document.body.scrollHeight) -
  9. document.documentElement.clientHeight) || 0;
  10. const update = () => (data.ratio = calculate());
  11. window.addEventListener('load', update);
  12. window.addEventListener('scroll', update, { passive: true });
  13. Alpine.magic('pageScroll', () => (scale, round) => {
  14. let value = data.ratio;
  15. if (!isNaN(scale)) {
  16. value *= scale;
  17. }
  18. if (!isNaN(round)) {
  19. value = value.toFixed(round);
  20. }
  21. return value;
  22. });
  23. };

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

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

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

  1. <div class="fixed top-0 right-0 w-4 bg-green-500 duration-100"
  2. x-data="{ scroll: $pageScroll(100) }"
  3. x-bind:style="{ height: scroll + '%' }"
  4. x-text="scroll">
  5. </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:

  1. /**
  2. * @param {import(&quot;alpinejs&quot;).Alpine} Alpine
  3. */
  4. export default (Alpine) =&gt; {
  5. const data = Alpine.reactive({ ratio: 0 });
  6. const calculate = () =&gt;
  7. (document.documentElement.scrollTop || document.body.scrollTop) /
  8. ((document.documentElement.scrollHeight || document.body.scrollHeight) -
  9. document.documentElement.clientHeight) || 0;
  10. const update = () =&gt; (data.ratio = calculate());
  11. window.addEventListener(&#39;load&#39;, update);
  12. window.addEventListener(&#39;scroll&#39;, update, { passive: true });
  13. Alpine.magic(&#39;pageScroll&#39;, () =&gt; (scale, round) =&gt; {
  14. let value = data.ratio;
  15. if (!isNaN(scale)) {
  16. value *= scale;
  17. }
  18. if (!isNaN(round)) {
  19. value = value.toFixed(round);
  20. }
  21. return value;
  22. });
  23. };

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

  1. &lt;div class=&quot;fixed top-0 left-0 w-4 bg-red-500 duration-100&quot;
  2. x-data x-bind:style=&quot;{ height: $pageScroll(100) + &#39;%&#39; }&quot;&gt;
  3. &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:

  1. &lt;div class=&quot;fixed top-0 right-0 w-4 bg-green-500 duration-100&quot;
  2. x-data=&quot;{ scroll: $pageScroll(100) }&quot;
  3. x-bind:style=&quot;{ height: scroll + &#39;%&#39; }&quot;
  4. x-text=&quot;scroll&quot;&gt;
  5. &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() 函数来解决这个问题,我认为这样的解决方案可能有效(我现在无法测试):

  1. &lt;div class=&quot;fixed top-0 right-0 w-4 bg-green-500 duration-100&quot;
  2. x-data=&quot;{ scroll: 0, scale: 100 }&quot;
  3. x-init=&quot;scroll = $pageScroll(scale)&quot;
  4. x-bind:style=&quot;{ height: scroll + '&#39;%&#39; }&quot;
  5. x-text=&quot;scroll&quot;
  6. &gt;
  7. &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):

  1. &lt;div class=&quot;fixed top-0 right-0 w-4 bg-green-500 duration-100&quot;
  2. x-data=&quot;{ scroll: 0, scale: 100 }&quot;
  3. x-init=&quot;scroll = $pageScroll(scale)&quot;
  4. x-bind:style=&quot;{ height: scroll + &#39;%&#39; }&quot;
  5. x-text=&quot;scroll&quot;
  6. &gt;
  7. &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:

确定