如何在 Astro 中的组件之间共享状态?

Dan*_*des 5 javascript astrojs

我相信我在代码中采用了错误的方法,如何在按钮单击中设置客户端首选项,该按钮单击用作全局 astro 组件中的道具?或者我应该怎么做?我知道这是可能的,因为 astro js 本身在他们的文档网站中这样做了!(下面是我的尝试的解释)

我目前正在开发一个 Astro Js 项目,这是我的个人作品集,我希望它向访问者展示一个选择首选语言的选项,所以最初我在所有 astro 组件中传递了一个语言属性。因此,我尝试在索引或布局文件中创建一个按钮,我的所有组件都会在其中接收其道具,以更改此值,但我很快意识到这不是应该如何完成的:

index.astro:(错误)

---
let lang = "pt-br";
---
<div id="langBtn">
  <span id="brbtn">BR</span><span>|</span><span id="enbtn">EN</span>
</div>
<Layout title="Daniel Folio" lang={lang}>
  <Hero lang={lang} />
</Layout>

<script>
  function handleClick() {
    if(lang === "pt-br") {
       lang = "en";
    } else {
       lang = "pt-br";
    }
  }
  document
    ?.getElementById("langBtn")
    ?.addEventListener("click", handleClick);
</ script>
Run Code Online (Sandbox Code Playgroud)

我意识到我们无法在客户端访问或更改服务器端变量。因此,我尝试使用 Astro cookie 来检查语言 cookie 是否存在,否则创建一个,然后单击按钮时更改其值,但这也是不可能的,因为 Astro.cookies 在客户端不可用。

was*_*ila 16

介绍

介绍将重点关注如何在 Astro 中的组件之间共享状态?解决方案部分将重点介绍如何在 Astro 中使用 cookie 同步客户端和服务器组件

在客户端组件之间共享状态和事件

const devices = {
  poster:{}
}}
function get_devices(){
  return devices
}
export{
  get_devices
}

Run Code Online (Sandbox Code Playgroud)

完整项目示例https://github.com/MicroWebStacks/astro-home-control/blob/2107f94d0ba9ca76b43777d8ca24eae99b3e78cc/src/libs/power_state.js#L7

使用 vanilla js 共享状态和事件

  • Vanilla js :还可以在客户端组件之间使用自定义事件,其中 html 元素是事件的目标,如本例所示
...
event(panel,"update",data[name])
...
element.addEventListener("update",(event)=>{
      panel_set_state(name,event.detail)
    })

Run Code Online (Sandbox Code Playgroud)

完整项目示例:https://github.com/MicroWebStacks/astro-home-control/blob/2107f94d0ba9ca76b43777d8ca24eae99b3e78cc/src/pages/index.astro#L54

客户端和服务器之间共享状态

服务器通过页面重新加载到客户端

  • 通过直接在 html 标签或 html 属性中嵌入变量<p class="value">{init}</p>(最简单且最常见)

  • 通过使用<script define:vars={{init}}>这可能是解决方案的一部分,但它不处理客户端->服务器反向链接,并且存在缺点

    • 传递的变量将在脚本中进行硬编码,该脚本特定于组件实例(如果您有 10 个组件,则正文中每个脚本都有 10 个脚本,并具有相应的组件实例值!!!)这就是它的翻译方式 在此输入图像描述

    • 因此,脚本是内联的,不再捆绑、延迟加载(不再在头部资产中)

    • 没有缓存,在每个页面中获取,...

服务器到客户端无需重新加载页面

对于动态服务器更改变量,可以使用服务器发送事件https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events

我不会在这里详细介绍它,因为这个问题不需要它,但它可以与 Astro 一起使用,并且可以在此处找到示例https://github.com/MicroWebStacks/astro-examples#03_sse-counter

在相同的范围内,也可以使用 websocket,但是对于这个问题来说,如果复杂性不是问题,那么也可以从客户端和服务器访问数据库。

服务器和客户端的持久性和同步

使用客户端存储

这是客户坚持其存储选择的一种可能的解决方案,缺点是

  • 如果存储只有客户端知道,服务器应答会闪烁
  • 或需要开发更多代码来处理客户端提交和服务器端端点捕获,这需要处理会话和用户管理!

对于那些对无闪烁状态高级示例感兴趣的人,请在此处与 sessionStorage 和 url 参数共享一个工作示例https://github.com/MicroWebStacks/astro-examples#14_client-storage-counter

使用cookie

这是最简单的方法,看解决方案

解决方案:如何使用cookie在Astro中同步客户端和服务器

问题帖子得到了正确的调查,然后陷入了如何使用 cookie 客户端:

这就是在客户端上设置和获取 cookie 所需的全部内容,只需几行普通的 javascript,没有依赖性。请参阅此问题/答案使用 JavaScript 设置 cookie 并获取 cookie

这里针对计数器进行了缩小和定制,但对于过期处理,请参阅引用的问题

    function get_counter(){
        const entry = document.cookie.split(';').find(entry=>entry.replace(' ','').startsWith('counter='))
        if(entry){
            return parseInt(entry.split('=')[1])
        }else{
            return 0
        }
    }
    function set_counter(counter){
        document.cookie = `counter=${counter}`
        console.log(`new counter value = ${counter}`)
    }
Run Code Online (Sandbox Code Playgroud)

客户端持久计数器示例

为了使示例更简单,我在此处使用了计数器而不是语言,这是一个完整的示例

服务器端cookie的使用

在Astro中,计数器cookie可以如下使用

let counter = 0
const cookie = Astro.cookies.get("counter")
if(cookie?.value){
    counter = cookie.value
}
console.log(`index.astro> cookie counter = ${counter}`)
Run Code Online (Sandbox Code Playgroud)

潜在问题

  • 确保 cookie 在部署主机中传递非常重要,因为这可能是阻止此解决方案的问题,可以在示例中使用服务器控制台日志进行检查