###### tags: `PWA`
# [Architecture](https://web.dev/learn/pwa/architecture/) 建構
You make some decisions when developing a PWA, such as whether to create a single page application or a multi-page application, and whether you will host it in the root of your domain or within a folder.
在開發 PWA 時,您需要做出一些決定,例如是創建單頁應用程序還是多頁應用程序,以及是將其託管在域的根目錄還是文件夾中。
Designing your application to make the most out of the technology that makes PWAs reliable, installable, and capable starts with understanding your application and its constraints, and choosing an appropriate architecture for both.
要設計您的應用程序以充分利用使 PWA 可靠、可安裝且功能強大的技術,首先要了解您的應用程序及其約束,並為兩者選擇合適的架構。
## SPA versus MPA SPA 與 MPA
Today, there are two primary architectural patterns in web development: single-page apps, or SPAs, and multi-page apps, or MPAs.
如今,Web 開發中有兩種主要的架構模式:單頁應用程序或 SPA,以及多頁應用程序或 MPA。
Single-page apps are defined by having client-side JavaScript control most or all of the HTML rendering of a page based on data retrieved by or provided to the app. The app overrides the browser's built-in navigation, replacing it with its routing and view handling functionality.
單頁應用程序的定義是讓客戶端 JavaScript 根據應用程序檢索或提供給應用程序的數據控制頁面的大部分或全部 HTML 呈現。該應用程序覆蓋了瀏覽器的內置導航,將其替換為路由和視圖處理功能。
Multi-page apps usually have pre-rendered HTML sent directly to the browser, often enhanced with client-side JavaScript after the browser has finished loading the HTML, and relying on the browser's built-in navigation mechanisms to display subsequent views.
多頁面應用程序通常將預呈現的 HTML 直接發送到瀏覽器,通常在瀏覽器完成加載 HTML 後使用客戶端 JavaScript 進行增強,並依靠瀏覽器的內置導航機制來顯示後續視圖。
Both architectures can be used to create PWAs.
這兩種架構都可用於創建 PWA。
Each has advantages and disadvantages, and selecting the right one for your use case and context is key to providing a fast and reliable experience for your users.
### Single page apps 單頁應用
<p style="font-weight:bold;color:green">Pros 優點</p>
* Mostly atomic in-page updates.
* Client-side dependencies loaded on start-up.
* Subsequent loads are fast, because of cache usage.
* 主要是原子頁內更新。
* 啟動時加載的客戶端依賴項。
* 由於緩存的使用,後續加載速度很快。
<p style="font-weight:bold;color:red">
Cons 缺點
</p>
* High initial load cost.
* Performance depends on device hardware and network connection.
* Additional app complexity is required.
* 高初始負載成本。
* 性能取決於設備硬件和網絡連接。
* 需要額外的應用程序複雜性。
**Single page apps are a good architectural fit if:**
**在以下情況下,單頁應用程序非常適合架構:**
* User interaction is mainly centered around atomic updates of interconnected data displayed on the same page, for instance, a real-time data dashboard or a video-editing app.
用戶交互主要圍繞同一頁面上顯示的互連數據的原子更新,例如,實時數據儀表板或視頻編輯應用程序。
* Your application has client-side-only initialization dependencies, for instance, a third-party authentication provider with a prohibitively high startup cost.
您的應用程序僅具有客戶端初始化依賴項,例如,啟動成本過高的第三方身份驗證提供程序。
* The data required for a view to load relies on a specific client-side-only context, for instance, displaying controls for a piece of connected hardware.
視圖加載所需的數據依賴於特定的僅客戶端上下文,例如,顯示連接硬件的控件。
* The app is small and simple enough that its size and complexity do not have an impact on the cons listed above.
* 該應用程序小而簡單,其大小和復雜性不會對上面列出的缺點產生影響。
**SPAs might not be a good architecture choice if:**
**在以下情況下,SPA 可能不是一個好的架構選擇:**
* Initial load performance is essential. SPAs usually need to load more JavaScript to determine what to load and how to display it. The parsing and execution time of this JavaScript, combined with retrieving content, is slower than sending rendered HTML.
* 初始負載性能至關重要。 SPA 通常需要加載更多的 JavaScript 來確定加載什麼以及如何顯示它。此 JavaScript 的解析和執行時間,加上檢索內容,比發送呈現的 HTML 慢。
* Your app runs mostly on low-to-average-powered devices. Because SPAs depend on JavaScript for rendering, the user experience depends much more significantly on the power of their specific device than it would in an MPA.
* 您的應用程序主要運行在中低功率設備上。由於 SPA 依賴於 JavaScript 進行呈現,因此與 MPA 相比,用戶體驗更依賴於其特定設備的功能。
Because SPAs need to replace the browser's built-in navigation with their routing, SPAs require a minimum level of complexity around efficiently updating the current view, managing navigation changes, and cleaning up previous views that would otherwise be handled by the browser, making them harder overall to maintain and more taxing on the user's device.
由於 SPA 需要用它們的路由替換瀏覽器的內置導航,因此 SPA 需要最低程度的複雜性來有效地更新當前視圖、管理導航更改以及清理瀏覽器原本會處理的先前視圖,從而使它們變得更難總體而言,維護和增加用戶設備的負擔。
## Multi-page apps 多頁應用
<p style="font-weight:bold;color:green">
Pros 優點
</p>
* Mostly full-page updates.
* Initial render speed is critical.
* Client-side scripting can be an enhancement.
* 主要是整頁更新。
* 初始渲染速度至關重要。
* 客戶端腳本可以是一種增強。
<p style="font-weight:bold;color:red">
Cons缺點
</p>
* Secondary views require another server call.
* Context doesn't carry over between views.
* Requires a server or pre-rendering.
* 輔助視圖需要另一個服務器調用。
* 上下文不會在視圖之間延續。
* 需要服務器或預渲染。
**Multi-page apps are a good architectural choice if:**
**在以下情況下,多頁面應用程序是一個很好的架構選擇:**
* User interaction is mainly centered around views of a single piece of data with optional context-based data, for instance, a news or e-commerce app.
用戶交互主要圍繞具有可選的基於上下文的數據的單個數據的視圖,例如,新聞或電子商務應用程序。
* Initial render speed is critical, as sending already rendered HTML to the browser is faster than assembling it from a data request after loading, parsing, and executing a JavaScript-based alternative.
初始渲染速度至關重要,因為將已渲染的 HTML 發送到瀏覽器比在加載、解析和執行基於 JavaScript 的替代方案後從數據請求組裝它更快。
* Client-side interactivity or context can be included as an enhancement after initial load, for instance, layering a profile onto a rendered page or adding secondary client-side context-dependent components.
客戶端交互性或上下文可以作為初始加載後的增強功能包含在內,例如,將配置文件分層到呈現的頁面上或添加輔助客戶端上下文相關組件。
**MPAs might not be a good architecture choice if:**
**在以下情況下,MPA 可能不是一個好的架構選擇:**
* Re-downloading, re-parsing, and re-executing your JavaScript or CSS is prohibitively expensive. This con is mitigated in PWAs with service workers.
重新下載、重新解析和重新執行您的 JavaScript 或 CSS 的成本高得令人望而卻步。在具有服務工作者的 PWA 中,這種情況得到了緩解。
* Client-side context, such as user location, doesn't seamlessly carry over between views, and re-obtaining that context may be expensive. It either needs to be captured and retrieved, or re-requested between views.
客戶端上下文(例如用戶位置)不會在視圖之間無縫轉移,並且重新獲取該上下文可能很昂貴。它要么需要被捕獲和檢索,要么需要在視圖之間重新請求。
Because individual views need to be dynamically rendered by a server or pre-rendered before access, potentially limiting hosting or adding data complexity.
因為單個視圖需要由服務器動態呈現或在訪問前預呈現,可能會限制託管或增加數據複雜性。
## Which one to choose?選擇哪一個
Even with these pros and cons, both architectures are valid for creating your PWA. You can even mix them for different parts of your app, depending on its needs, for instance, having store listings follow an MPA architecture and the checkout flow follow an SPA architecture.
即使有這些優點和缺點,兩種架構都適用於創建您的 PWA。您甚至可以根據需要將它們混合用於應用程序的不同部分,例如,讓商品詳情遵循 MPA 架構,而結帳流程遵循 SPA 架構。
Regardless of choice, the next step is understanding how to best use service workers to provide the best experience.
無論選擇哪種,下一步都是了解如何最好地使用服務人員來提供最佳體驗。
## The power of service worker / Service Worker 的力量
The service worker has a lot of power beyond basic routing and delivery of cached and network responses. We can create complex algorithms that can improve the user's experience and performance.
Service Worker 擁有超越基本路由和緩存和網絡響應交付的強大功能。我們可以創建可以改善用戶體驗和性能的複雜算法。
## Service worker includes (SWI) / Service Worker 包括 (SWI)
An emerging pattern for using service workers as an integral part of a site's architecture is service worker includes (SWI). SWI divides individual assets, usually an HTML page, into pieces based on their caching needs, then stitches them back together in the service worker to improve consistency, performance, and reliability, while reducing cache size.
將 Service Worker 作為網站架構不可或缺的一部分的新興模式是 Service Worker 包含 (SWI)。 SWI 根據緩存需求將單個資產(通常是 HTML 頁面)分成多個部分,然後在 Service Worker 中將它們重新拼接在一起,以提高一致性、性能和可靠性,同時減小緩存大小。

This image is a sample web page. It has five different sections that break the page down into:
此圖像是示例網頁。它有五個不同的部分,將頁面分為:
* Overall layout.
* Global header (top dark bar).
* Content area (middle left lines and image).
* Sidebar (tall medium-dark bar on the middle right).
* Footer (dark bottom bar).
* 整體佈局。
* 全局標題(頂部黑條)。
* 內容區域(左中線和圖像)。
* 側邊欄(中間右側的高中深色條)。
* 頁腳(深色底欄)。
### Overall layout 總體佈局
The overall layout isn't likely to change often and has no dependencies. It's a good candidate for [precaching](https://web.dev/learn/pwa/assets-and-data/#frequently-used-cache-approaches).
總體佈局不太可能經常更改並且沒有依賴性。這是一個很好的預緩存候選者。
### Header and footer 頁眉和頁腳
The global header and footer contain things like the top menu and site footer, and present a particular challenge: if the page were to be cached as a whole, these might change between page loads, depending on when the given page was cached.
全局頁眉和頁腳包含頂部菜單和站點頁腳之類的內容,並提出了一個特殊的挑戰:如果要將頁面作為一個整體進行緩存,則這些內容可能會在頁面加載之間發生變化,具體取決於給定頁面的緩存時間。
By separating them and caching them independently of the content, you can ensure that users will always get the same version, regardless of when they are cached. Because they are infrequently updated, they're good candidates for precaching, too. They have a dependency, though: the site's CSS and JavaScript.
通過將它們分開並獨立於內容進行緩存,您可以確保用戶始終獲得相同的版本,無論它們何時被緩存。因為它們不經常更新,所以它們也很適合預緩存。不過,它們有一個依賴項:站點的 CSS 和 JavaScript。
### CSS and JavaScript / CSS 和 JavaScript
Ideally, the site's CSS and JavaScript should be cached with a stale while revalidate strategy to allow incremental updates without needing to update the service worker, as it is the case with precached assets. Still, they also need to be kept at a minimum version whenever the service worker updates with a new global header or footer. Because of this, their cache should also be updated with the latest version of assets when the service worker installs.
理想情況下,站點的 CSS 和 JavaScript 應該使用陳舊的同時重新驗證策略進行緩存,以允許增量更新而無需更新服務工作者,就像預緩存資產的情況一樣。儘管如此,每當 service worker 使用新的全局頁眉或頁腳更新時,它們也需要保持在最低版本。因此,當服務工作者安裝時,它們的緩存也應該更新為最新版本的資產。
### Content area 內容區
Next is the content area. Depending on the frequency of updates, either network first or stale while revalidate is a good strategy here. Images should be cached with a cache first strategy, as has been previously [discussed](https://web.dev/learn/pwa/serving/#caching-strategies).
接下來是內容區。根據更新頻率,網絡優先或陳舊而重新驗證是一個很好的策略。正如前面所討論的,應該使用緩存優先策略來緩存圖像。
### Sidebar 邊欄
Finally, presuming the sidebar content contains secondary content such as tags and related items, it's not critical enough to pull from the network. A stale while revalidate strategy works for this.
最後,假設側邊欄內容包含標籤和相關項目等次要內容,從網絡中提取還不夠重要。過時的重新驗證策略適用於此。
Now, after going through all that, you may be thinking that you can only do this kind of per-section caching for single-page apps. But, by adopting patterns inspired by [edge side includes](https://en.wikipedia.org/wiki/Edge_Side_Includes) or [server side includes](https://en.wikipedia.org/wiki/Server_Side_Includes) in your service worker, with some advanced service worker features, you can do this for either architecture.
現在,在經歷了所有這些之後,您可能會認為您只能為單頁應用程序執行這種按部分緩存的操作。但是,通過在您的 Service Worker 中採用受邊緣端包含或服務器端包含啟發的模式,以及一些高級 Service Worker 功能,您可以為任何一種架構執行此操作。
### Try it yourself 自己試試
You can try the service worker includes with the next codelab:
您可以在下一個 Codelab 中嘗試使用 Service Worker:
<div style="background-color:#f4fcfe;padding:1rem;line-height:1.9;margin-top:1rem;color:#007b83">
<p style="font-weight:bold;">
<> Try it
</p>
Service worker includes.
</div>
## Streaming responses 流式響應
The previous page could be created using the app shell model in the SPA world, where the app shell is cached, then served, and content is loaded on the client side. With the introduction and wide availability of the [Streams API](https://developer.mozilla.org/zh-CN/docs/Web/API/Streams_API), both app shell and content can be combined in the service worker and streamed to the browser, giving you the caching flexibility of app shell with the speed of MPAs.
可以使用 SPA 世界中的 App Shell 模型創建上一個頁面,其中緩存 App Shell,然後提供服務,並在客戶端加載內容。隨著 Streams API 的引入和廣泛可用性,應用程序外殼和內容都可以在服務工作線程中組合併流式傳輸到瀏覽器,從而為您提供應用程序外殼的緩存靈活性和 MPA 的速度。
It does this because:這樣做是因為:
* Streams can be built asynchronously, allowing different pieces of a stream to come from other sources.
流可以異步構建,允許流的不同部分來自其他來源。
* The requester of a stream can start working on the response as soon as the first chunk of data is available, instead of waiting for the entire item to be complete.
一旦第一個數據塊可用,流的請求者就可以開始處理響應,而不是等待整個項目完成。
* Parsers optimized for streaming, including the browser, can progressively display the content of the stream before it's complete, speeding up the perceived performance of the response.
針對流優化的解析器(包括瀏覽器)可以在流完成之前逐步顯示流的內容,從而加快響應的感知性能。
Thanks to these three properties of streams, architectures built around streaming usually have a faster perceived performance than those that aren't.
由於流的這三個屬性,圍繞流構建的架構通常比那些沒有構建的架構具有更快的感知性能。
Working with the Streams API can be challenging as it's complex and low level. Fortunately, there's a [Workbox module](https://developer.chrome.com/docs/workbox/reference/) that can help with setting up streaming responses for your service workers.
使用 Streams API 可能具有挑戰性,因為它很複雜且級別較低。幸運的是,有一個 Workbox 模塊可以幫助您為服務工作者設置流式響應。
## Domains, origins, and PWA scope 域、來源和 PWA 範圍
Web workers, including service workers, storage, even an installed PWA's window, are all governed by one of the most critical security mechanisms on the web: the same-origin policy. Within the same origin, permissions are granted, data can be shared, and the service worker can talk to different clients. Outside of the same origin, permissions are not automatically granted and data is isolated and not accessible between different origins.
Web 工作者,包括服務工作者、存儲,甚至是已安裝的 PWA 窗口,都受 Web 上最關鍵的安全機制之一:同源策略的約束。在同源內,權限被授予,數據可以共享,service worker 可以與不同的客戶端對話。在同源之外,不會自動授予權限,並且數據在不同源之間是隔離的和不可訪問的。
### Same-origin policy 同源策略
Two URLs are defined as having the exact origin if the protocol, port, and host are the same.
如果協議、端口和主機相同,則兩個 URL 被定義為具有確切來源。
For example:<kbd>https://squoosh.app</kbd>, and <kbd> https://squoosh.app/v2 </kbd>have the same origin, but
<kbd> http://squoosh.app </kbd>, <kbd>https://squoosh.com </kbd>, <kbd> https://app.squoosh.app </kbd> and
<kbd> https://squoosh.app:8080</kbd> are in different origins. Check the [same-origin policy MDN reference](https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy) for more information and examples.
例如:<kbd>https://squoosh.app</kbd>,和<kbd>https://squoosh.app/v2 </kbd> 同源,但是<kbd> http://squoosh.app </kbd>, <kbd>https://squoosh.com </kbd>, <kbd> https://app.squoosh.app </kbd>和<kbd> https://squoosh.app:8080</kbd> 來源不同。查看同源策略 MDN 參考以獲取更多信息和示例。
Changing subdomains isn't the only way a host can change. Each host is made up of a top-level domain (TLD), a secondary level domain (SLD), and zero or more labels (sometimes called subdomains), separated by dots in between and read from right to left in an URL. A change in any of the items results in a different host.
更改子域並不是主機可以更改的唯一方式。每個主機都由一個頂級域 (TLD)、一個二級域 (SLD) 和零個或多個標籤(有時稱為子域)組成,它們之間用點分隔並在 URL 中從右到左讀取。任何項目的更改都會導致不同的主機。
In the window management module, we've already seen how the in-app browser looks when a user navigates to a different origin from an installed PWA.
在窗口管理模塊中,我們已經看到當用戶導航到與安裝的 PWA 不同的來源時,應用內瀏覽器的外觀。
That in-app browser will appear even if the websites have the same TLD and SLD, but with different labels, as they are then considered different origins.
即使網站具有相同的 TLD 和 SLD,但標籤不同,應用內瀏覽器也會出現,因為它們被認為是不同的來源。
One of the key aspects of an origin in a web-browsing context is how storage and permissions work. One origin shares many features among all content and PWAs within it, including:
Web 瀏覽上下文中源的關鍵方面之一是存儲和權限的工作方式。一個源點在其中的所有內容和 PWA 之間共享許多特性,包括:
* Storage quota and data (IndexedDB, cookies, web storage, cache storage).
* Service worker registrations.
* Permissions granted or denied (such as web push, geolocation, sensors).
* Web push registrations.
* 存儲配額和數據(IndexedDB、cookies、網絡存儲、緩存存儲)。
* 服務工作者註冊。
When you move from one origin to another, all the previous access is revoked, so permissions have to be granted again, and your PWA can't access all the data saved in the storage.
當你從一個源點移動到另一個源點時,之前的所有訪問都被撤銷,因此必須重新授予權限,你的 PWA 無法訪問存儲中保存的所有數據。
<div style="padding:1rem;background-color:#fce8e8;color:#a50e0e;margin-bottom:1rem">
<div style="display:flex">
:warning:
<p style="color:#a50e0e;margin-top:-1.5em">
Caution警告
</p>
</div>
<p style="color:#a50e0e;">
Having different subdomains for different parts of a website or different TLDs for other locales, such as <kbd>admin.example.com </kbd>or<kbd>example.co.uk</kbd>, makes building PWAs harder, each subdomain will have isolated storage, its own service worker registration, and web app manifest. It may also lead to a disjointed experience if the subdomains are designed to look like the same app, as one will be displayed in the in-app browser while the main PWA won't.
</p>
</div>
<div style="padding:1rem;background-color:#fce8e8;color:#a50e0e;margin-bottom:1rem">
<div style="display:flex">
:warning:
<p style="color:#a50e0e;margin-top:-1.5em">
Caution警告
</p>
</div>
<p style="color:#a50e0e;">
網站的不同部分有不同的子域或其他區域設置有不同的 TLD,例如 admin.example.comorexample.co.uk,這使得構建 PWA 變得更加困難,每個子域將有獨立的存儲、自己的服務工作者註冊和 Web 應用程序清單.如果子域被設計成看起來像同一個應用程序,它也可能會導致脫節的體驗,因為一個將顯示在應用程序內瀏覽器中,而主 PWA 則不會。
</p>
</div>
[範例](https://storage.googleapis.com/web-dev-uploads/video/RK2djpBgopg9kzCyJbUSjhEGmnw1/V1OWWZnCGpwVcaZE152Z.mp4)
## Resources
超越 SPA:PWA 的替代架構
[Beyond SPAs: Alternative architectures for your PWA](https://developer.chrome.com/blog/beyond-spa/)
漸進式網絡應用程序結構
[Progressive web apps Structure](https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps/App_structure)
流:權威指南
[Streams: The definitive guide](https://web.dev/streams/)