AMP

記住使用者做過的事

關閉專家模式

專家模式

使用專家模式來隱藏為初學者設計的網路開發指南。

有時候,我們希望單一使用者互動能同時影響多個元件。在簡單的情況下,我們可能有多個關閉的手風琴選單,以及一個寫著「展開所有章節」的按鈕。如果使用者按下該按鈕,我們希望同時開啟所有手風琴選單。

同時,在更複雜的情況下,我們可能正在處理購物網站的結帳表單。在這種情況下,我們希望使用者能夠重複使用他們的寄送地址作為帳單地址,或改為顯示地址輸入表單。我們只想向使用者顯示完成表單所需的欄位,因此點擊單一核取方塊可能會導致多個地址輸入欄位出現或消失。

回想一下,在建構我們的起司自行車網站時,我們建立了兩個影像輪播,它們透過事件和動作連結在一起。每當我們在任一輪播中變更投影片時,我們都會更新另一個輪播以指向相同的投影片。這對於動作和事件來說效果很好,因為每個事件的效果數量很少。如果我們想要新增第三個元件,該元件也需要在每次選擇新投影片時更新,該怎麼辦?突然間,事情可能會變得複雜。「展開所有章節」範例中,每次我們新增一個新章節時,我們都必須更新按鈕的事件處理常式以開啟新的手風琴選單。

在程式設計中,當我們需要在多個位置使用相同的值時,我們會使用變數。每當在程式碼中參考變數時,它都會傳回程式碼執行時的值。如果您更新變數的值,則任何參考該變數的程式碼都會看到更新後的值。

我們可以使用類似的方法來處理我們的網站。我們可以將資訊儲存在變數中,並在整個頁面中參考它們。當我們更新這些變數時,頁面將會使用新值更新。

因此,對於我們的輪播範例,我們可以建立一個包含目前選取投影片的變數。每當使用者在其中一個輪播上變更投影片時,我們都會更新變數的值。最後,我們重新設計每個輪播,以便在我們建立的變數更新時更新它們的選取投影片。在我們的「展開所有章節」範例中,我們可以讓按鈕按下事件更新一個變數,並且我們可以在變數更新時更新每個手風琴選單以使其開啟。

這些類型的變數稱為「狀態變數」,而當狀態變數變更時更新元件的過程稱為「繫結」。恰如其分地,開發人員使用 <amp-state> 元件在 AMP 中管理狀態變數,並使用 <amp-bind> 元件在狀態變數變更時更新元件。

在應用程式狀態中儲存資訊

狀態變數會回應使用者動作而更新。我們可以利用它們來追蹤使用者點擊按鈕的次數、使用者在表單欄位中輸入的文字、有關使用者登入帳戶的資訊,或是影像輪播設定為哪張投影片。

但是我們如何設定狀態變數的值?正如我們在進階課程中學到的,我們透過事件和事件處理常式回應使用者動作。為了同步我們的兩個影像輪播,我們在輪播元件上使用了動作 (goToSlide) 來變更選取的投影片。同樣地,AMP 執行階段提供了一個名為 setState 的動作,可讓我們設定狀態變數。AMP.setState 動作會採用 JSON 物件,代表要儲存的狀態變數。因此,舉例來說,以下程式碼將 wasPressed 狀態變數設定為 true 的值

<button on="tap:AMP.setState({wasPressed: true})">
    Press Me
</button>

注意:如果您的狀態包含多個狀態變數,您只需要將 setState 方法傳遞給您想要更新的變數即可。AMP 會智慧地將給定的 JSON 物件合併到現有狀態中,因此無需嘗試自行合併值或每次傳遞每個狀態變數。如果您有興趣,可以閱讀更多關於 AMP 如何在附錄中合併這些值的資訊。

可以為您的狀態變數設定初始值。若要執行此操作,請建立具有特定 ID 的 <amp-state> 元件。我們在該 <amp-state> 元件中建立的狀態會儲存為以該 ID 作為鍵的物件。接下來,在該 <amp-state> 元件內部,新增具有 application/json 類型的指令碼標記。然後,您可以將 JSON 物件新增至該指令碼標記,該物件代表狀態的初始值。

以下範例顯示如何設定初始狀態。請注意,我們在設定狀態變數時如何使用 <amp-state> 元件的 ID

<amp-state id="accordionState"/>
    <script type="application/json">
        {
            "isOpen": false
        }
    </script>
<amp-state>
<button on="tap:AMP.setState({ accordionState: { isOpen: !accordionState.isOpen }})">
    Open/Close All Sections
</button>

以上程式碼符合先前的「展開所有章節」範例。我們為手風琴選單 (上方未顯示) 建立了一個狀態變數 (isOpen)。<amp-state> 元件的 ID 為 accordionState。當我們設定 isOpen 的值時,我們將其設定在名為 accordionState 的物件內。當我們參考 isOpen 的值時,我們必須將其參考為 accordionState.isOpen。清晰的命名慣例對於協助您的開發人員追蹤您的狀態變數的意義非常重要。這種清晰度將使您的程式碼更易於理解,並且更不容易出錯。

注意<amp-state> 具有超出本訓練範圍的其他功能。甚至可以使用從遠端伺服器下載的資料來設定狀態的初始值!或者,您可以將狀態的目前值列印到瀏覽器中的開發人員主控台。請查看 <amp-state>說明文件以取得更多資訊。

使用繫結將狀態連接到元素

繫結是狀態變數的變更可能導致網站可見更新的方式。換句話說,繫結是元件上的屬性與狀態變數之間的連線。

在 AMP 中,我們透過用方括號括住來定義繫結屬性。我們將該屬性設定為等於運算式。每當該運算式中狀態變數的值變更時,就會根據這些新值評估運算式,並且屬性會更新。

例如,如果我們想要控制元素的文字,我們可以繫結到元素的 text 屬性。在以下程式碼中,按下按鈕將導致段落標記包含文字「Hello AMP!」。

<button on="tap:AMP.setState({message: ‘Hello AMP!’})">
    Say Hello!
</button>
<p [text]="message"></p>

[text] 是繫結屬性,每當狀態變數 message 變更時,它都會變更。在這種情況下,運算式只會將段落元素的文字設定為 message 變數的值。

text 屬性可在任何支援文字內容的元素或元件上繫結。其他此類屬性包括 widthheighthiddenclass。大多數 AMP 元件也提供可以繫結到狀態變數的自訂屬性。例如,我們可以繫結到 <amp-carousel> 元件的 slides 屬性來控制活動投影片、<amp-selector> 元件的 selected 屬性來控制選取的項目,以及 <amp-img> 元件的 src 屬性來變更顯示的影像。繫結屬性的完整清單可以在 <amp-bind> 說明文件中找到。

務必注意,繫結在狀態變數的值變更之前不會變更。繫結不會在頁面首次載入時評估。在先前的「Hello AMP」範例中,即使我們使用 <amp-state> 元件載入 message 狀態變數的初始值,段落在初始頁面載入時也是空的。因此,為了確保您的元件在頁面載入時看起來合理,請務必為繫結屬性加入預設值。

若要為繫結屬性新增預設值,請同時包含帶方括號和不帶方括號的屬性。當頁面載入時,它將使用預設屬性。每當繫結由相關狀態變數的變更觸發時,預設屬性將會被覆寫。例如,在以下程式碼中,文字的顏色在初始頁面載入時將為藍色,但在按下按鈕後變更為紅色。

<button on="tap:AMP.setState({messageClass: ‘text-color-red’})">
    Change to Red!
</button>
<p [class]="messageClass" class="text-color-blue">Hello AMP!</p>

到目前為止,我們已經使用了直接參考單一狀態變數的基本運算式,但我們可以使其更精細。運算式是 JavaScript 的子集。它們可以包含靜態值 (例如數字或字串)、狀態變數和一組允許清單函數。說明文件概述了可以新增至運算式的值。

繫結屬性的值會設定為評估運算式時傳回的值。例如,此程式碼顯示如何在建立帳戶期間建立錯誤訊息。以下案例檢查使用者輸入的兩個密碼是否相符

<input type="text" placeholder="Password"
    on="change:AMP.setState({ firstPassword: event.value })" />
<input type="text" placeholder="Re-Enter Password"
    on="change:AMP.setState({ secondPassword: event.value })" />
<p hidden [hidden]="firstPassword == secondPassword">
    The passwords don't match!
</p>

在以上程式碼中,使用者為密碼輸入的文字儲存在兩個狀態變數中:firstPasswordsecondPassword。錯誤訊息預設為隱藏,如果兩個密碼相符,則保持這種狀態。換句話說,只有在密碼文字不相符時才會顯示錯誤!

繫結是建構動態、互動式網站的強大工具。它們協助我們將處理使用者互動的方式分成兩個不同的問題:使用者互動如何影響狀態變數,以及變更狀態變數如何影響網站的內容和/或外觀。我們可以最接近問題最相關的位置來解決這些問題中的每一個。我們使用 setState 動作在事件處理常式中管理使用者互動對狀態的影響,並且我們透過將元件的屬性繫結到狀態變數來更新網站的內容和外觀。

這種在您的應用程式中管理使用者互動的方法可以減少錯誤、讓您更容易在未來變更您的元件,並且讓其他人更容易理解您的程式碼。

繫結是建構豐富、動態、互動式網站的強大工具。它們讓我們能夠將少數狀態變數繫結到多個不同的屬性。此外,它們讓我們能夠將事件中狀態變數的更新與頁面上繫結屬性中可見效果的管理分開。

當僅使用事件和動作時,我們必須知道單一使用者動作將如何轉換為整個頁面的效果。透過狀態和繫結,我們現在將資訊儲存到與使用者動作相關的狀態變數中。然後,我們讓繫結決定這些狀態變數將如何影響整個網站的屬性值。

練習 1:使用狀態讓我們的輪播保持同步

為了練習我們目前學到的內容,讓我們將我們的輪播從使用事件和動作轉換為使用狀態和繫結,以彼此保持同步。提示:我們應該不再使用 <amp-carousel> 動作 goToSlide<amp-selector> 動作 toggle,就像我們在先前的課程中所做的那樣。此外,讓我們在縮圖下方新增一些文字,以追蹤我們選取的投影片。

使用 <amp-state><amp-bind>說明文件,嘗試完成以下每個需求

  • 當使用者在較大的輪播或縮圖輪播中變更投影片時,另一個輪播的投影片也應該變更為相同的投影片。

  • 將目前的投影片索引 (0、1 或 2) 儲存在狀態變數 selectedSlide 中。

  • <amp-selector> 元件之後新增 <p> 標記。

  • 新的 <p> 標記應該包含格式為「投影片 X (共 3 張)」的文字,其中 X 是目前選取的投影片。注意:投影片索引從 0 開始,但投影片計數器文字應該從 1 開始。提示:別忘了為段落標記文字新增初始值!

  • [選用] 將狀態變數儲存在 ID 為 carousel<amp-state> 元件中。

完成後,結果應如下所示

樣式化的縮圖輪播影像

解決方案

包含輪播的頁面部分應如下所示


<amp-state id="carousel">
    <script type="application/json">
        {
            "selectedSlide": 0
        }
    </script>
</amp-state>
<amp-carousel on="slideChange:AMP.setState({carousel: {selectedSlide:event.index}})"
    [slide]="carousel.selectedSlide" lightbox id="imageSlides" layout="responsive"
    width="412" height="309" type="slides" loop>
    <amp-img src="https://cdn.glitch.com/d7f46a57-0ca4-4cca-ab0f-69068dec6631%2Fcheddar-chaser.jpg?1540228205366"
        width="412" height="309" layout="responsive"></amp-img>
    <amp-img src="https://cdn.glitch.com/d7f46a57-0ca4-4cca-ab0f-69068dec6631%2Fcheese.jpg?1540228223785"
        width="412" height="309" layout="responsive"></amp-img>
    <amp-img src="https://cdn.glitch.com/d7f46a57-0ca4-4cca-ab0f-69068dec6631%2Fmouse.jpg?1540228223963"
        width="412" height="309" layout="responsive"></amp-img>
</amp-carousel>
<amp-selector id="ampSelector" [selected]="carousel.selectedSlide"
    on="select:AMP.setState({carousel: {selectedSlide:event.targetOption}})">
    <amp-carousel layout="fixed-height" height="78" class="thumbnail-carousel">
        <amp-img src="https://cdn.glitch.com/d7f46a57-0ca4-4cca-ab0f-69068dec6631%2Fcheddar-chaser-thumb.jpg?1540228250623"
            option="0" selected role="button" tabindex="1" width="96" height="72" layout="fixed"></amp-img>
        <amp-img src="https://cdn.glitch.com/d7f46a57-0ca4-4cca-ab0f-69068dec6631%2Fcheese-thumb.jpg?1540228249992"
            option="1" role="button" tabindex="1"  width="96" height="72" layout="fixed"></amp-img>
        <amp-img src="https://cdn.glitch.com/d7f46a57-0ca4-4cca-ab0f-69068dec6631%2Fmouse-thumb.jpg?1540228249062"
            option="2" role="button" tabindex="1" width="96" height="72" layout="fixed"></amp-img>
    </amp-carousel>
</amp-selector>
<p [text]="’Slide ‘ + (carousel.selectedSlide + 1) + ‘ of 3’">
    Slide 1 of 3
</p>

請記得在 <head> 中包含 <amp-bind> 程式庫

<script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script>

案例研究:分析線上購物頁面

繫結和狀態管理對於電子商務頁面非常重要。想像一下,您前往購物網站尋找要購買的新 T 恤。您先瀏覽選項清單,然後才選定您真正喜歡的樣式和品牌。導覽至產品頁面後,您會看到其他選項的清單。您或許可以在不同的版型、尺寸或顏色之間選擇。

線上購物產品頁面範例

通常,當我們選取這些選項時,頁面會變更以符合我們的選取。圖片可能會更新為綠色女款襯衫或紅色男款襯衫。當我們選擇不同的 T 恤版型或尺寸時,價格可能會變更。其他選項可能會完全售完,導致出現錯誤訊息!

頁面的其他部分也會顯示動態內容。通常,當我們登入電子商務網站的帳戶時,我們可能會在右上角看到我們的姓名。通常會有一個購物車圖示,帶有一個小數字徽章,指示我們的購物車中有多少產品。當我們點擊按鈕購買更多商品時,該數字會更新為購物車中商品的新總數。

若要開始了解電子商務網站的狀態變數中儲存了多少資訊,請思考一下其他人使用相同網站的情況。他們有不同的帳戶資訊和不同的購物車。他們可能會檢視不同的產品或啟用不同的選項。兩個使用者可能看到相同網站的每個差異都將由一個或多個狀態變數描述。

透過我們學到的關於狀態變數和繫結的知識,我們現在開始了解如何建立這樣的頁面。我們需要將每個產品選項的目前值儲存在其自己的狀態變數中。然後,我們需要與各種產品選項對應的定價和供應情況資訊 (例如,綠色男款 XL 襯衫與紅色女款 S 襯衫的價格)。最後,我們繫結動態欄位 (例如價格、缺貨錯誤可見性和產品影像) 以考量每個選取的選項。

練習 2:重新建立線上產品頁面

我們的下一個目標是重新建立一個基本的線上產品頁面,例如我們在案例研究中討論的頁面。我們的產品將是一件 T 恤,有多種選項可供選擇:男款或女款;小、中或大尺寸;以及紅色、藍色或綠色。產品的基本價格將根據使用者的選擇而變更。最後,當使用者選取不同的顏色時,T 恤產品的影像將會更新。

我們正在建構的範例產品頁面

我們不會在我們的 Chico's Cheese Bikes 商店專案內建構此產品頁面。相反地,您可以使用這個 Glitch 作為此練習的起點。注意:別忘了重新混合它,以便您可以編輯!Glitch 包含

  • 一些基本的 CSS 和 HTML,用於配置靜態產品頁面。

  • 狀態變數,描述產品的成本、各種選項的加價以及各種產品影像的 URL (依顏色)。

  • 一個 <amp-state> 元件,用於保存具有初始值的狀態變數。

讓我們討論在給定的 Glitch 入門應用程式中載入的 <amp-state> 元件中產品資訊的結構。在真正的電子商務網站中,我們可能會直接從伺服器載入此資料,但在我們的範例中,我們將直接將資料包含在網站中。

<amp-state id="productData">
    <script type="application/json">
        {
            "basePrice": 14
            "upcharges": {
                "fit": {
                    "Men": 0,
                    "Women": 3
                },
                "size": {
                    "Small": 0,
                    "Medium": 2,
                    "Large": 5
                },
                "color": {
                    "Red": 0,
                    "Blue": 1,
                    "Green": 0
                }
            },
            "images": {
                "Red": "https://...redtshirt.jpg",
                "Blue": "https://...Blue_Tshirt.jpg",
                "Green": "https://...greentshirt.png"
            }
        }
    </script>
</amp-state>

首先,請注意 <amp-state> 元件的 ID 為 productData。這表示項目 ($14) 的基本價格可以在繫結運算式中參考為 productData.basePrice。接下來,在 upcharges 區段中,我們列出 T 恤的各種選項以及它們如何提高項目的價格。

例如,在運算式中參考 productData.upcharges.size.Medium 將傳回中尺寸 T 恤的加價 ($2)。(加價是為產品客製化支付的額外價格。) 我們只需將加價加到 T 恤的基本價格 (productData.basePrice + productData.upcharges.size.Medium = $14 + $2 = $16) 即可取得襯衫的最終價格。最後,images 區段包含指向我們襯衫每種顏色的影像的各種 URL。

使用 <amp-bind><amp-state> 的說明文件以及上述描述,更新給定的基本產品頁面以符合以下需求

  • 當版型、尺寸和顏色選取方塊更新時,它們應該將其新的選取值儲存到 ID 為 optionsData<amp-state> 元件中的對應狀態變數中。

  • T 恤的影像應該與目前選取的顏色選項對應。

  • 包含產品描述 (版型:男款 - 尺寸:小 - 顏色:紅色) 的 <p> 標記應該更新以反映目前選取的產品選項。

  • 包含產品價格美元部分的 <span> 標記應該更新以反映項目的基本價格以及與目前選取的產品選項相關的任何加價。

提示:完成此練習的一項重要技能是使用一組狀態變數從另一個狀態變數中擷取資料。在這種情況下,我們將經常使用 optionsData 狀態變數中的變數來從 productData 狀態變數中擷取資訊。例如,若要找出目前選取的 T 恤尺寸的加價是多少,我們會在我們的繫結運算式中參考 productData.upcharges.size[optionsData.size]。如果 optionsData.size 目前設定為 Small,則先前的運算式等同於 productData.upcharges.size.Small。您可以在這裡閱讀更多關於此參考 JSON 中資訊的方法。

解決方案

解決方案可以在這個 Glitch 範例中找到。包含變更的頁面部分應如下所示


<main>
    <h2>Tina's T-Shirts</h2>
    <div class="filter-sort-selectors">
        <p>Product Fit:</p>
        <select class="product-selector" on="change:AMP.setState({optionsData: { fit: event.value }})">
            <option value="Men">Men's</option>
            <option value="Women">Women's</option>
        </select>
        <p>Product Size:</p>
        <select class="product-selector" on="change:AMP.setState({optionsData: { size: event.value }})">
            <option value="Small">Small</option>
            <option value="Medium">Medium</option>
            <option value="Large">Large</option>
        </select>
        <p>Product Color:</p>
        <select class="product-selector" on="change:AMP.setState({optionsData: { color: event.value }})">
            <option value="Red">Red</option>
            <option value="Green">Green</option>
            <option value="Blue">Blue</option>
        </select>
    </div>
    <div class="product">
      <amp-img src="https://cdn.glitch.com/f87df7df-18be-4e6e-8b0e-1145de279989%2Fredtshirt.jpg?1542275948053"
          [src]="productData.images[optionsData.color]" layout="responsive" width="350" height="350"></amp-img>
      <h2>T-Shirt</h2>
      <p [text]="'Fit: ' + optionsData.fit + ' - Size: ' + optionsData.size + ' - Color: ' + optionsData.color">
          Fit: Men - Size: Small - Color: Red
      </p>
      <p class="price">
          $<span [text]="productData.basePrice +
                         productData.upcharges.fit[optionsData.fit] +
                         productData.upcharges.size[optionsData.size] +
                         productData.upcharges.color[optionsData.color]"> 14</span>.00
      </p>
    </div>
</main>