路由元件
路由元件是一種特殊類型的內容,當我們使用 component
或 componentUrl
屬性指定路由內容時,路由器可以載入該內容。
它應有助於更好地建構我們的應用程式,將事物放在適當的位置,並以更快速、更清楚且更舒適的方式完成許多事情。
元件函式
元件是一個函式,它接收 props
和 context
,並應傳回呈現函式。
元件呈現函式應傳回帶有元件 HTML 內容的 標記範本字串。
例如
const MyComponent = (props, context) => {
// some component logic
let value = 'foo';
// return render function
return () => context.$h`
<div class="page">
<p>Value is ${value}</p>
</div>
`;
}
元件範本
如上所述,元件呈現函式應傳回帶有元件 HTML 內容的 標記範本字串。它有幾件重要事項需要注意。
所有自閉合標籤都必須關閉!。如果您沒有關閉自閉合標籤,例如 <br>
、<img src="">
、<input ...>
,編譯器將會擲回錯誤。
所有空元素都可以自閉合:
<div class="my-div"></div>
<!-- also valid as -->
<div class="my-div" />
元件屬性
元件函式接收的第一個引數是 props
。此物件將包含您在導覽方法中傳遞的所有屬性,以及所有路由參數。
例如,如果我們有以下路由
{
path: '/blog/:id',
component: MyComponent
}
當我們透過 /blog/34/
URL 導覽到路由時,它將有 props.id
等於 '34'
。
而且當我們使用 API 導覽到元件時,如下所示
router.navigate('/blog/34/', {
props: {
foo: 'bar'
}
})
那麼 props
將會是以下物件:{ id: '34', foo: 'bar' }
此外,屬性還將包含傳遞給自訂元件的屬性,作為屬性。如果自訂元件具有此類屬性
<my-component foo="bar" id="25" user=${{name: 'John'}} number=${30}></my-component>
那麼 $props
將會是
{
foo: 'bar',
id: '25',
user: {
name: 'John'
},
number: 30
}
元件內容
context
物件包含許多有用的輔助程式
屬性 | 說明 |
---|---|
$h | 特殊的 標記範本字串,必須用於包裝元件呈現函式的結果和其內部所有 HTML 項目
|
$el | 物件,其中
|
$ | Dom7 函式庫
|
$f7 | Framework7 應用程式實例
|
$store | 儲存實例。請查看 儲存文件 以取得更多詳細資訊和範例。 |
$f7route | 目前路徑。包含具有路徑 query 、hash 、params 、path 和 url 的物件 |
$f7router | 相關路由器實例
|
$theme | 具有
|
$update(callback) | 此方法表示此元件及其子元件需要使用更新的狀態重新呈現
無法保證 DOM 變更會立即套用,因此如果您依賴 DOM(例如在狀態變更後需要取得 HTML 內容或屬性值),請傳遞 |
$ref(initialValue) | 此方法會建立反應式「變數」,在更新後會自動更新元件,無需呼叫 它會傳回一個物件,其中包含
無法保證 DOM 變更會立即套用,因此如果您依賴 DOM(例如在狀態變更後需要取得 HTML 內容或屬性值),請傳遞 |
$useState(initialValue) | 此方法會建立反應式「狀態」。
對於
對於
對於
例如
|
$tick(callback) | 如果您依賴 DOM 並且需要確保在呼叫 傳遞的 callback 將在 DOM 更新時執行。 此方法會傳回 Promise,該 Promise 也會在 DOM 更新時解析。 因此,您可以像這樣使用它
|
$f7ready(callback) | 只有在您使用 主要應用程式元件 時才需要使用此方法,以確保在應用程式初始化時呼叫 Framework7 API。
|
事件 | |
$on | 將 DOM 事件處理常式附加到元件根元素的函式
當元件毀損時,此類事件處理常式會自動分離 |
$once | 將 DOM 事件處理常式附加到元件根元素的函式。與 |
$emit(事件, 資料) | 在可重複使用的自訂元件中發射自訂 DOM 事件的函式
而在其他父元件中
|
生命週期掛勾 | |
$onBeforeMount | 在元件將被新增到 DOM 之前立即呼叫 |
$onMounted | 在元件被新增到 DOM 之後立即呼叫
|
$onBeforeUpdate | 在元件在 VDOM 將被修補/更新之前立即呼叫 |
$onUpdated | 在元件 VDOM 已被修補/更新之後立即呼叫 |
$onBeforeUnmount | 在元件將被卸載(從 DOM 中分離)之前立即呼叫 |
$onUnmounted | 在元件卸載並銷毀時呼叫 |
因此,範例路由與頁面元件可能如下所示
routes = [
// ...
{
path: '/some-page/',
// Component
component: (props, { $h, $f7, $on }) => {
const title = 'Component Page';
const names = ['John', 'Vladimir', 'Timo'];
const openAlert = () => {
$f7.dialog.alert('Hello world!');
}
$on('pageInit', (e, page) => {
// do something on page init
});
$on('pageAfterOut', (e, page) => {
// page has left the view
});
return () => $h`
<div class="page">
<div class="navbar">
<div class="navbar-bg"></div>
<div class="navbar-inner">
<div class="title">${title}</div>
</div>
</div>
<div class="page-content">
<a @click=${openAlert} class="red-link">Open Alert</a>
<div class="list simple-list">
<ul>
${names.map((name) => $h`
<li>${name}</li>
`)}
</ul>
</div>
</div>
</div>
`;
},
},
// ...
]
元件頁面事件
元件頁面事件處理常式可以在 $on
元件事件處理常式中傳遞。它們通常是 DOM 頁面事件。由於它們是 DOM 事件,它們接受 event
作為第一個引數,並接受 頁面資料 作為第二個引數。它們與一般 DOM 事件的唯一不同點在於,事件處理常式名稱必須以駝峰式大小寫格式指定(page:init
-> pageInit
)
const MyComponent = (props, { $on }) => {
$on('pageMounted', (e, page) => {
console.log('page mounted');
});
$on('pageInit', (e, page) => {
console.log('page init');
});
$on('pageBeforeIn', (e, page) => {
console.log('page before in');
});
$on('pageAfterIn', (e, page) => {
console.log('page after in');
});
$on('pageBeforeOut', (e, page) => {
console.log('page before out');
});
$on('pageAfterOut', (e, page) => {
console.log('page after out');
});
$on('pageBeforeUnmount', (e, page) => {
console.log('page before unmount');
});
$on('pageBeforeRemove', (e, page) => {
console.log('page before remove');
});
}
DOM 事件處理
請注意元件範本中額外的 @
屬性。它是一種將事件監聽器指定給指定元素的簡寫方法。指定的事件處理常式將在元件範圍中搜尋。
此類事件處理常式屬性值必須是函式
const MyComponent = (props, { $h, $update }) => {
let value = 10;
const addValue = (number) => {
value += number;
$update();
}
const onClick = () => {
console.log('click');
}
return () => $h`
<div class="page">
<!-- pass function to attribute -->
<button @click=${onClick}>Button</button>
<!-- also work -->
<button @click=${() => onClick()}>Button</button>
<!-- will not work, attribute value "onClick" is just a string -->
<button @click="onClick">Button</button>
<!-- passing dynamic data will work as expected -->
<button @click=${() => addValue(15)}>Button</button>
</div>
`
}
事件處理常式只在初始呈現時或對於使用 VDOM 修補的元素進行處理。如果您手動將此類元素新增到 DOM 中,它將無法運作!
const MyComponent = (props, { $h, $on }) => {
const onClick = () => {
console.log('click');
}
$on('pageInit', (e, page) => {
// this won't work
page.$el.append('<a @click="onClick">Link</a>');
});
return () => $h`
<div class="page">
</div>
`
}
元件根元素
元件範本或呈現函式必須只傳回單一 HTML 元素。而且它必須是路由器支援的元素
如果您將頁面載入為路由器元件,則路由器元件必須傳回頁面元素
<template> <div class="page"> ... </div> </template>
如果您將模式對話框(可路由模式對話框)載入為路由器元件,則路由器元件必須傳回該模式對話框元素
<template> <div class="popup"> ... </div> </template>
如果您將面板(可路由面板)載入為路由器元件,則路由器元件必須傳回面板元素
<template> <div class="panel panel-left panel-cover"> ... </div> </template>
如果您將標籤內容(可路由標籤)載入為路由器元件,則路由器元件必須傳回標籤的子元素,該子元素將插入到可路由標籤中
<template> <div class="some-element"> ... </div> </template>
單一檔案元件
將所有元件路由指定在同一個路由陣列中並非明智之舉,特別是當我們有許多此類路由時。因此,我們可以使用 componentUrl
,並將元件放入單一檔案中
routes = [
...
{
path: '/some-page/',
componentUrl: './some-page.f7',
},
..
];
在 some-page.f7
中
<!-- component template, uses same tagged template literals -->
<template>
<div class="page">
<div class="navbar">
<div class="navbar-bg"></div>
<div class="navbar-inner">
<div class="title">${title}</div>
</div>
</div>
<div class="page-content">
<a @click=${openAlert}>Open Alert</a>
<div class="list simple-list">
<ul>
${names.map((name) => $h`
<li>${name}</li>
`)}
</ul>
</div>
</div>
</div>
</template>
<!-- component styles -->
<style>
.red-link {
color: red;
}
</style>
<!-- rest of component logic -->
<script>
// script must return/export component function
export default (props, { $f7, $on }) => {
const title = 'Component Page';
const names = ['John', 'Vladimir', 'Timo'];
const openAlert = () => {
$f7.dialog.alert('Hello world!');
}
$on('pageInit', () => {
// do something on page init
});
$on('pageAfterOut', () => {
// page has left the view
});
// component function must return render function
return $render;
}
</script>
這樣一來就乾淨多了。<template>
和 <style>
標籤會自動轉換為匯出元件的相同屬性。
元件函式的結尾必須有 return $render
,因為它會被解析器替換為 <template>
標籤的內容。
與 Webpack 和 Vite 搭配使用
對於 Webpack,有一個特殊的 framework7-loader 外掛程式,它允許將單一檔案元件打包到主程式包中,而不使用 XHR(例如 componentUrl
)每次載入和解析元件檔案。
對於 Vite.js,也有特殊的 rollup-plugin-framework7 外掛程式,用於打包單一檔案元件。
這些外掛程式會解析單一檔案元件的檔案,並在打包過程中將其轉換為純 JS 物件。因此,它有可能提升應用程式的效能,因為不會有執行時期的解析和編譯。
當外掛程式設定好後,我們需要將單一檔案元件儲存在 .f7
(或 Webpack 的 .f7.html
)檔案中,並使用 export default
匯出元件
<template>
<div class="page">
...
</div>
</template>
<script>
export default () => {
let foo = 'bar';
const doThis = () => {
// ...
}
return $render;
}
</script>
也可以匯入必要的相依關係和樣式
<template>
<div class="page">
...
</div>
</template>
<script>
import './path/to/some-styles.css';
import utils from './path/to/utils.js';
export default () => {
let foo = 'bar';
let now = utils.now();
const doThis = () => {
// ...
}
return $render;
}
</script>
然後,我們可以匯入它並加入路由
// routes.js
import NewsPage from './path/to/news.f7';
import ServicesPage from './path/to/services.f7';
export default [
{
path: '/news/',
component: NewsPage,
},
{
path: '/services/',
component: ServicesPage,
}
]
JSX
範本字串在 HTML 文件中沒有良好的語法突顯。但是,當與 webpack 或 Vite 搭配使用時,也可以使用 JSX 語法 來撰寫元件。
為了讓它運作,我們需要將元件儲存在 .f7.jsx
檔案中,並使用 JSX 撰寫它們
export default (props, { $update }) => {
let value = 10;
const items = ['Item 1', 'Item 2'];
const addValue = (number) => {
value += number;
$update();
}
//- render function should returns JSX
return () => (
<div class="page">
<p>The value is {value}</p>
<p>
{/* JSX doesn't support @ in attribute name so event handlers should start from "on" */}
<button onClick={() => addValue(10)}>Add Value</button>
</p>
<ul>
{items.map((item) => (
<li>{item}</li>
))}
</ul>
</div>
)
}
並在 routes.js
中以相同的方式匯入它們
import NewsPage from './path/to/news.f7.jsx';
import ServicesPage from './path/to/services.f7.jsx';
export default [
{
path: '/news/',
component: NewsPage,
},
{
path: '/services/',
component: ServicesPage,
}
]
虛擬 DOM
虛擬 DOM 和所有與 VDOM 相關的功能都可以在 Framework7 版本 3.1.0 中取得。
虛擬 DOM (VDOM) 是一種程式設計概念,其中使用者介面的理想或「虛擬」表示會儲存在記憶體中,並與「真實」DOM 同步。它允許我們將應用程式的檢視表示為其狀態的函數。
VDOM 函式庫稱為 Snabbdom,因為它極為輕量、快速,且非常適合 Framework7 環境。
那麼 Framework7 路由元件 VDOM 呈現是如何運作的呢?元件範本會轉換為 VDOM,而不是直接插入 DOM。稍後,當元件狀態變更時,它會建立新的 VDOM,並將其與先前的 VDOM 進行比較。而且根據該差異,它會透過僅變更需要變更的元素和屬性來修補真實 DOM。而且所有這些都會自動發生!
讓我們看看當我們要求使用者資料時,會自動更新配置的使用者個人資料元件範例
<template>
<div class="page">
<div class="navbar">
<div class="navbar-bg"></div>
<div class="navbar-inner">
<div class="title">Profile</div>
</div>
</div>
<div class="page-content">
${user && $h`
<!-- Show user list when it is loaded -->
<div class="list simple-list">
<ul>
<li>First Name: ${user.firstName}</li>
<li>Last Name: ${user.lastName}</li>
<li>Age: ${user.age}</li>
</ul>
</div>
`}
${!user && $h`
<!-- Otherwise show preloader -->
<div class="block block-strong text-align-center">
<div class="preloader"></div>
</div>
`}
</div>
</div>
</template>
<script>
export default (props, { $on, $f7, $update }) => {
// empty initial user data
let user = null;
$on('pageInit', () => {
// request user data on page init
fetch('https://api.website.com/get-user-profile')
.then((res) => res.json())
.then((data) => {
// update user with new data
user = data;
// trigger re-render
$update();
});
})
return $render;
}
</script>
請注意,直接指定元件狀態不會觸發配置更新。每當您需要更新元件配置時,請使用 $update
!
清單中的金鑰與自動初始化元件
當 VDOM 更新元素清單時,它預設會使用「就地修補」策略。如果資料項目順序已變更,它不會移動 DOM 元素以符合項目的順序,而是會就地修補每個元素,並確保它反映在該特定索引中應該呈現的內容。
此預設模式很有效率,但僅適用於您的呈現輸出不依賴於子元件狀態或暫時 DOM 狀態(例如表單輸入值)時。
若要提供提示給 VDOM,以便它可以追蹤每個節點的身分,並因此重複使用和重新排序現有元素,您需要為每個項目提供唯一的 key
屬性。
在呈現清單時,key
的理想值會是每個項目的唯一 ID
<template>
...
<ul>
${items.map((item) => $h`
<li key=${item.id}>...</li>
`)}
</ul>
...
</template>
<script>
export default () => {
const items = [
{
id: 1,
title: 'Item A'
},
{
id: 2,
title: 'Item B'
},
];
return $render;
}
</script>
與自動初始化元件相同,例如 範圍滑桿、量規 和其他元件,這些元件應在新增至 DOM 時自動初始化(如果它們有 range-slider-init
、gauge-init
),並在從 DOM 中移除時自動銷毀。因此,此類元素也必須使用唯一金鑰來識別。
<template>
<div class="page">
...
<div class="page-content">
${gaugeVisible && $h`
<!-- must have unique key -->
<div key="gauge" class="gauge gauge-init" data-type="circle"
data-value="0.60"
data-value-text="60%"
data-value-text-color="#ff9800"
data-border-color="#ff9800"
></div>
`}
...
<a href="#" class="button" @click=${showGauge}>Show Gauge</a>
</div>
</div>
</template>
<script>
export default (props, { $update }) => {
let gaugeVisible = false;
const showGauge = () => {
gaugeVisible = true;
$update();
}
return $render;
}
</script>
- 請注意,
key
屬性在單一元件中必須是唯一的。 - 如果未指定
key
屬性,且元素具有id
屬性,則會將id
屬性用作虛擬節點唯一金鑰。
innerHTML
如果我們需要插入 HTML 字串(例如從 API 端點接收),我們需要使用特殊的 innerHTML
元素屬性/prop
<template>
<div class="page">
...
<div class="block" innerHTML=${customHTML}></div>
</div>
</template>
<script>
export default (props) => {
const customHTML = '<p>Hello <b>World!</b></p>';
return $render;
}
</script>
在元素上使用 innerHTML
會覆寫其所有子元素。
傳遞到 innerHTML
中的 HTML 內容只是一個字串,例如,元件事件處理常式(如 @click
屬性)將無法運作。
主要應用程式元件
可以將整個應用程式配置成一個元件。
請注意,由於 VDOM 的實作,強烈建議為每個自動初始化的檢視(具有 view-init
類別的檢視)新增唯一的 id
或 key
屬性
若要啟用它,首先,我們應該在 index.html
中保持應用程式根元素為空
<body>
<!-- empty app root element -->
<div id="app"></div>
</body>
然後,我們需要建立主要應用程式元件,例如,使用 Vite 的單一檔案元件
<!-- app.f7 -->
<template>
<div id="app">
${loggedIn.value && $h`
<div class="panel panel-left panel-reveal panel-init">
<!-- every View has unique ID attribute -->
<div class="view view-init" id="view-panel" data-url="/panel/"></div>
</div>
<div class="view view-main view-init" id="view-main" data-url="/"></div>
`}
${!loggedIn.value && $h`
<div class="login-screen modal-in">
<div class="view view-init" id="view-auth" data-url="/auth/"></div>
</div>
`}
</div>
</template>
<script>
export default (props, { $store }) => {
const loggedIn = $store.getters.loggedIn;
return $render;
}
</script>
最後,當我們初始化 Framework7 時,我們需要在初始化時指定應用程式元件
// import main app component
import App from './path/to/app.f7';
var app = new Framework7({
// specify main app component
component: App,
})
或者,如果我們不使用 webpack,我們也可以透過 XHR 載入它
var app = new Framework7({
// load main app component
componentUrl: './path/to/app.f7',
})
另外請注意,主要應用程式元件會在應用程式初始化程序完成之前掛載(新增到 DOM)。因此,如果您需要立即呼叫 Framework7 API,請使用 $f7ready
回呼
<template>
<div id="app">
...
</div>
</template>
<script>
export default (props, { $f7ready, $f7 }) => {
$f7ready(() => {
// now it is safe to call Framework7 APIs
$f7.dialog.alert('Hello!');
})
}
</script>
自訂元件
註冊元件
可以建立自訂可重複使用的元件。我們需要在 Framework7 初始化之前使用下列方法執行此操作
Framework7.registerComponent(tagName, component)- 註冊自訂元件
- tagName - 字串。元件標籤名稱,例如
my-component
(將用作<my-component>
)。自訂元件標籤名稱必須包含連字元/破折號 "
-
" - component - 物件 或 類別。元件函式
請注意,目前只能在路由元件(由路由載入的元件)中使用自訂元件。
Framework7.registerComponent(
// component name
'my-list-item',
// component function
(props, { $h }) => {
let foo = 'bar';
return () => $h`
<li class="item-content" id="${props.id}">...</li>
`
}
)
並在其他元件中使用它,例如
<div class="list">
<ul>
<my-list-item id="item-1"></my-list-item>
</ul>
</div>
請注意,傳遞給自訂元件元素的屬性可在元件 props
中使用。
區域元件
可以在元件中建立區域自訂元件
<template>
<ul>
<!-- use tag names as variables -->
<${ListItem} title="Item 1" />
<${ListItem} title="Item 2" />
<${ListItem} title="Item 3" />
</ul>
</template>
<script>
// create local component
const ListItem = (props, { $h }) => {
return () => $h`<li>${props.title}</li>`;
}
// export main component
export default () => {
return $render;
}
</script>
或者可以匯入它們
<template>
<ul>
<!-- use tag names as variables -->
<${ListItem} title="Item 1" />
<${ListItem} title="Item 2" />
<${ListItem} title="Item 3" />
</ul>
</template>
<script>
// import component
import ListItem from 'path/to/list-item.f7';
// export main component
export default () => {
return $render;
}
</script>
使用 JSX
const ListItem = (props) => {
return (
<li>{props.title}</li>
)
}
/* or
import ListItem from 'path/to/list-item.f7.jsx'
*/
export default () => {
return () => (
<ul>
<ListItem title="Item 1" />
<ListItem title="Item 2" />
<ListItem title="Item 3" />
</ul>
)
}
在 JSX 中,可以在主要元件內建立它
export default () => {
const ListItem = (props) => {
return (
<li>{props.title}</li>
)
}
return () => (
<ul>
<ListItem title="Item 1" />
<ListItem title="Item 2" />
<ListItem title="Item 3" />
</ul>
)
}
事件
您可以在範本中使用相同的 @{event}
語法為自訂元件指定 DOM 事件。事件處理常式實際上會附加到自訂元件根元素。
<template>
<div class="page">
...
<my-button @click="onClick">Click Me</my-button>
</div>
</template>
<script>
return {
// ...
methods: {
onClick: function(e) {
console.log('clicked');
}
},
// ...
}
</script>
插槽
如果我們需要將子元素(或文字)傳遞到自訂元件,我們需要使用插槽。這裡的插槽實作類似於 Web Components 插槽。
使用 slot
標籤,我們可以指定元件子項目的放置位置。例如 my-button
元件範本
<a class="button button-fill">
<slot></slot>
</a>
然後可以像這樣使用
<my-button>Click Me</my-button>
若要指定插槽預設值(當沒有傳遞子項目時),我們只需將其放入 <slot>
標籤內
<a class="button button-fill">
<slot>Default Button Text</slot>
</a>
若要將元素分佈在元件佈局中,我們可以使用命名插槽。例如 my-container
元件的範本
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
我們可以使用以下方式使用它
<my-container>
<h1 slot="header">Title</h1>
<p>Text for main content.</p>
<p>More text for main content.</p>
<p slot="footer">Footer content</p>
</my-container>
元件結果輸出將會是
<div class="container">
<header>
<h1>Title</h1>
</header>
<main>
<p>Text for main content.</p>
<p>More text for main content.</p>
</main>
<footer>
<p>Footer content</p>
</footer>
</div>
範本食譜
條件式渲染
若要在 JavaScript 中實作條件,我們通常使用 if
(if-else
)陳述式。在範本和 JSX 內部,我們無法直接使用它們,而應該使用 JavaScript 運算子。
if
對於 if
陳述式,我們應該使用邏輯 AND(&&
)運算子
<template>
<div class="page">
${someVar && $h`
<p>Text will be visible when "someVar" is truthy</p>
`}
${someVar === 1 && $h`
<p>Text will be visible when "someVar" equals to 1</p>
`}
</div>
</template>
<script>
export default () => {
const someVar = 1;
return $render;
}
</script>
使用 JSX 的方式相同
export default () => {
const someVar = 1;
return () => (
<div class="page">
{someVar && (
<p>Text will be visible when "someVar" is truthy</p>
)}
{someVar === 1 && (
<p>Text will be visible when "someVar" equals to 1</p>
)}
</div>
)
}
if-else
對於 if-else
,我們可以使用三元運算子(?:
)或 &&
和 !
運算子的組合
<template>
<div class="page">
${someVar ? $h`
<p>Text will be visible when "someVar" is truthy</p>
` : $h`
<p>Text will be visible when "someVar" is falsy</p>
`}
{someVar && (
<p>Text will be visible when "someVar" is truthy</p>
)}
{!someVar && (
<p>Text will be visible when "someVar" is falsy</p>
)}
</div>
</template>
<script>
export default () => {
const someVar = 1;
return $render;
}
</script>
使用 JSX 的方式相同
export default () => {
const someVar = 1;
return () => (
<div class="page">
{someVar ? (
<p>Text will be visible when "someVar" is truthy</p>
) : (
<p>Text will be visible when "someVar" is falsy</p>
)}
{someVar && (
<p>Text will be visible when "someVar" is truthy</p>
)}
{!someVar && (
<p>Text will be visible when "someVar" is falsy</p>
)}
</div>
)
}
將陣列對應到元素
若要將陣列對應到元素,我們使用陣列的 .map()
方法
<template>
<div class="page">
<ul>
${items.map((item) => $h`
<li>${item}</li>
`)}
</ul>
</div>
</template>
<script>
export default () => {
const items = [
'item 1',
'item 2',
'item 3',
];
return $render;
}
</script>
使用 JSX 的方式相同
export default () => {
const items = [
'item 1',
'item 2',
'item 3',
];
return () => (
<div class="page">
<ul>
{items.map((item) => (
<li>{item}</li>
))}
</ul>
</div>
)
}