# Somnia 组件编写指南


## Somnia 组件编写指南

一个常见的 shortcode ，处理 js 在 Swup.js 切换后自动加载的问题。

```html
<div x-data="test()" x-text="some"></div>
<script>
    function test() {
        return {
            some: "test"
        }
    }
</script>
```

这段代码出现在 shortcode 后。Swup.js 下的页面，切换页面时不会自动加载其中的 js ，导致 Alpine.js 初始化失败。

## 简码编写简化

一切为了更方便的编写简码！

### 方法 1 手动移动到 x-data 

将函数 return 移动到 `x-data` 里。如 `x-data="{ some: 'test' }"`

### 方法 2 手动移动到 custom.js

将函数移动到 `assets/js/custom.js`

### 方法 3 移动到 head 触发后初始化

在 shortcode 中使用 `x-load="event (somnia:moved)"` 或者 `x-load="idle"`，或者 `x-ingore` 延迟 Alpine.js 初始化。然后编写代码，在 Swup.js 事件触发后，再初始化。

```html
<div x-data="test()" x-text="some" x-load="event (somnia:moved)"></div>
<script>
    console.log("test loaded")
    function test() {
        return {
            some: "test"
        }
    } 
</script>
```
`assets/js/custom.js` 中的函数 `Somnia.prototype.swupPageInitCustom` 函数加上

```js
    // 获取目标容器内的所有脚本元素 遍历每个脚本并将其移动到 head 中
    scriptdocument.querySelectorAll('#content-wrapper script')s.forEach(script => {
        const newScript = document.createElement('script');
        newScript.innerHTML = script.innerHTML;
        // 将脚本元素重新插入到 head 标签内
        document.head.appendChild(newScript);
    });
    document.dispatchEvent(new CustomEvent('somnia:moved', { bubbles: true }));
```    

需要注意的是，这段代码比较粗糙，会导致多次加载多次触发，head 部分堆积重复。

### 方法 4 移动到 x-data 触发后初始化

使用 `x-ingore` 延迟 Alpine.js 初始化。移动相邻脚本内容到 x-data 中，替换元素触发初始化。

`assets/js/custom.js` 中的函数 `Somnia.prototype.swupPageInitCustom` 函数加上

```js{open=true}
    document.querySelectorAll('div[x-ignore]').forEach(div => {
        // 获取下一个相邻元素（即紧随其后的兄弟元素）
        const nextElement = div.nextElementSibling;
        // 如果相邻元素存在并且是脚本
        if (nextElement && nextElement.tagName.toLowerCase() === 'script') {
            // 将 innerText 设置到当前 div 的 x-data 属性上
            div.setAttribute('x-data', nextElement.innerText);
            nextElement.remove();
            div.removeAttribute('x-ignore'); // 移除 x-ignore 属性
            div.parentNode.replaceChild(div, div); // 触发 Alpine.js 重新解析 x-data
        }
    });
```

这种方法脚本保留一个 object 或者函数都可。

```html
<div x-data="test()" x-text="some" x-ignore></div>
<script>
    function test() {
        return {
            some: "test"
        }
    }
</script>
```

### 方法 5 Async Alpine 异步加载

函数移动到单独的 js 文件，async-alpine 异步加载。参考 `Somnia/static/js/comment.mjs` 

- 文档 https://async-alpine.dev/docs/

