Google I/O 2016 प्रोग्रेसिव वेब ऐप्लिकेशन बनाना

आयोवा होम

खास जानकारी

जानें कि हमने वेब कॉम्पोनेंट, Polymer, और मटीरियल डिज़ाइन का इस्तेमाल करके, एक पेज वाला ऐप्लिकेशन कैसे बनाया और उसे Google.com पर प्रोडक्शन में लॉन्च किया.

नतीजे

  • खास ऐप्लिकेशन की तुलना में ज़्यादा यूज़र ऐक्टिविटी (4:06 मिनट मोबाइल वेब बनाम Android 2:40 मिनट).
  • सेवा वर्कर की कैश मेमोरी की मदद से, लौटने वाले उपयोगकर्ताओं के लिए फ़र्स्ट पेंट 450 मिलीसेकंड तक तेज़ हो गया
  • 84% वेबसाइट पर आने वाले लोगों के डिवाइस पर Service Worker काम करता है
  • होमस्क्रीन पर सेव किए गए वीडियो की संख्या में 2015 के मुकाबले 900% से ज़्यादा की बढ़ोतरी हुई.
  • 3.8% उपयोगकर्ता ऑफ़लाइन हो गए, लेकिन 11 हज़ार पेज व्यू जनरेट होते रहे!
  • साइन इन किए हुए 50% उपयोगकर्ताओं ने सूचनाएं चालू की हैं.
  • उपयोगकर्ताओं को 5.36 लाख सूचनाएं भेजी गईं (12% लोगों ने उन्हें वापस लाया).
  • 99% उपयोगकर्ताओं के ब्राउज़र में, वेब कॉम्पोनेंट पॉलीफ़िल के साथ काम किया जाता था

खास जानकारी

इस साल, मुझे Google I/O 2016 के प्रोग्रेसिव वेब ऐप्लिकेशन पर काम करने का मौका मिला. इसे प्यार से "IOWA" नाम दिया गया है. यह मोबाइल फ़र्स्ट है और पूरी तरह से ऑफ़लाइन काम करता है. साथ ही, यह मटीरियल डिज़ाइन से काफ़ी प्रेरित है.

IOWA एक एक पेज वाला ऐप्लिकेशन (एसपीए) है, जिसे वेब कॉम्पोनेंट, पॉलीमर, और Firebase का इस्तेमाल करके बनाया गया है. साथ ही, इसमें App Engine (Go) में लिखा गया बड़ा बैकएंड है. यह सर्विस वर्कर का इस्तेमाल करके, कॉन्टेंट को पहले से कैश मेमोरी में सेव करता है. साथ ही, यह डाइनैमिक तरीके से नए पेजों को लोड करता है, व्यू के बीच अच्छे से ट्रांज़िशन करता है, और पहली बार लोड होने के बाद कॉन्टेंट को फिर से इस्तेमाल करता है.

इस केस स्टडी में, हम फ़्रंटएंड के लिए आर्किटेक्चर से जुड़े कुछ दिलचस्प फ़ैसलों के बारे में बताएंगे. अगर आपको सोर्स कोड में दिलचस्पी है, तो GitHub पर इसे देखें.

GitHub पर देखें

वेब कॉम्पोनेंट का इस्तेमाल करके एसपीए बनाना

हर पेज को कॉम्पोनेंट के तौर पर इस्तेमाल करना

हमारे फ़्रंटएंड का एक मुख्य पहलू यह है कि यह वेब कॉम्पोनेंट पर आधारित है. असल में, हमारे एसपीए में हर पेज एक वेब घटक है:

    <io-home-page date="2016-05-18T17:00:00Z" app="[[app]]"></io-home-page>
    <io-schedule-page date="2016-05-18T17:00:00Z" app="{ % templatetag openvariable % }app}}"></io-schedule-page>
    <io-attend-page></io-attend-page>
    <io-extended-page></io-extended-page>
    <io-faq-page></io-faq-page>

हमने ऐसा क्यों किया? पहली वजह यह है कि यह कोड पढ़ा जा सकता है. पहली बार पढ़ने वाला पाठक के तौर पर, हमारे ऐप्लिकेशन के हर पेज के बारे में साफ़ तौर पर पता चलता है. दूसरी वजह यह है कि एसपीए बनाने के लिए, वेब कॉम्पोनेंट में कुछ अच्छी प्रॉपर्टी होती हैं. <template> एलिमेंट, कस्टम एलिमेंट, और शैडो डीओएम की खास सुविधाओं की मदद से, स्टेटस मैनेजमेंट, व्यू चालू करने, और स्टाइल स्कोपिंग जैसी कई सामान्य समस्याएं हल हो जाती हैं. ये डेवलपर टूल हैं, जिन्हें ब्राउज़र में बनाया जा रहा है. क्यों न इसका फ़ायदा उठाया जाए?

हर पेज के लिए कस्टम एलिमेंट बनाने पर, हमें बिना किसी शुल्क के ये सुविधाएं मिलीं:

  • पेज लाइफ़साइकल मैनेजमेंट.
  • पेज के हिसाब से स्कोप की गई सीएसएस/एचटीएमएल.
  • किसी पेज के लिए खास तौर पर इस्तेमाल की जाने वाली सभी सीएसएस/एचटीएमएल/जेएस को ज़रूरत के हिसाब से बंडल किया जाता है और एक साथ लोड किया जाता है.
  • व्यू का फिर से इस्तेमाल किया जा सकता है. पेज, डीओएम नोड होते हैं. इसलिए, उन्हें जोड़ने या हटाने से व्यू बदल जाता है.
  • आने वाले समय में सेवा देने वाले लोग, सिर्फ़ मार्कअप का पता लगाकर हमारे ऐप्लिकेशन को समझ सकते हैं.
  • जब ब्राउज़र, एलिमेंट की डेफ़िनिशन को रजिस्टर और अपग्रेड करता है, तो सर्वर से रेंडर किए गए मार्कअप को बेहतर बनाया जा सकता है.
  • कस्टम एलिमेंट में इनहेरिटेंस मॉडल होता है. DRY कोड अच्छा कोड होता है.
  • …और भी बहुत कुछ.

हमने IOWA में इन फ़ायदों का पूरा फ़ायदा उठाया. आइए, कुछ जानकारी के बारे में जानते हैं.

पेजों को डाइनैमिक तौर पर चालू करना

<template> एलिमेंट, ब्राउज़र का फिर से इस्तेमाल किया जा सकने वाला मार्कअप बनाने का स्टैंडर्ड तरीका है. <template> में दो तरह की विशेषताएं होती हैं. एसपीए इनका फ़ायदा ले सकते हैं. सबसे पहले, <template> के अंदर मौजूद कोई भी चीज़ तब तक काम नहीं करती, जब तक टेंप्लेट का कोई इंस्टेंस नहीं बनाया जाता. दूसरा, ब्राउज़र मार्कअप को पार्स करता है, लेकिन मुख्य पेज से कॉन्टेंट ऐक्सेस नहीं किया जा सकता. यह मार्कअप का एक ऐसा हिस्सा है जिसे फिर से इस्तेमाल किया जा सकता है. उदाहरण के लिए:

<template id="t">
    <div>This markup is inert and not part of the main page's DOM.</div>
    <img src="profile.png"> <!-- not loaded by the browser -->
    <video id="vid" src="vid.mp4"></video> <!-- doesn't load/start -->
    <script>alert("Not run until the template is stamped");</script>
</template>

Polymer, <template> के अलग-अलग तरह के एक्सटेंशन कस्टम एलिमेंट को बढ़ाता है, जैसे कि <template is="dom-if"> और <template is="dom-repeat">. ये दोनों कस्टम एलिमेंट हैं, जो <template> को अतिरिक्त सुविधाएं देते हैं. वेब कॉम्पोनेंट के एलान वाले तौर-तरीके की वजह से, दोनों ठीक वैसा ही काम करते हैं जैसा आपको उम्मीद होती है. पहला कॉम्पोनेंट, शर्त के आधार पर मार्कअप को स्टैंप करता है. दूसरा, किसी सूची (डेटा मॉडल) में मौजूद हर आइटम के लिए मार्कअप दोहराता है.

IOWA, इस तरह के एक्सटेंशन एलिमेंट का इस्तेमाल कैसे कर रहा है?

आपको याद होगा कि IOWA में हर पेज एक वेब कॉम्पोनेंट होता है. हालांकि, पहले लोड होने पर हर कॉम्पोनेंट का एलान करना बेवकूफी होगी. इसका मतलब है कि ऐप्लिकेशन पहली बार लोड होने पर, हर पेज का एक इंस्टेंस बनाया जाएगा. हम शुरुआती लोड की परफ़ॉर्मेंस को खराब नहीं करना चाहते थे. खास तौर पर, इसलिए, क्योंकि कुछ उपयोगकर्ता सिर्फ़ एक या दो पेजों पर ही जाएंगे.

हमारा समाधान था धोखा देना. IOWA में, हम हर पेज के एलिमेंट को <template is="dom-if"> में रैप करते हैं, ताकि उसका कॉन्टेंट पहले बूट पर लोड न हो. इसके बाद, जब टेंप्लेट का name एट्रिब्यूट यूआरएल से मेल खाता है, तब हम पेजों को चालू कर देते हैं. <lazy-pages> वेब कॉम्पोनेंट हमारे लिए ये सभी लॉजिक मैनेज करता है. मार्कअप कुछ ऐसा दिखता है:

<!-- Lazy pages manages the template stamping. It watches for route changes
        and sets `template.if = true` on the appropriate template. -->
<lazy-pages>
    <template is="dom-if" name="home">
    <io-home-page date="2016-05-18T17:00:00Z"></io-home-page>
    </template>

    <template is="dom-if" name="schedule">
    <io-schedule-page date="2016-05-18T17:00:00Z"></io-schedule-page>
    </template>

    <template is="dom-if" name="attend">
    <io-attend-page></io-attend-page>
    </template>
</lazy-pages>

मुझे इस बात की सबसे ज़्यादा पसंद है कि पेज लोड होने पर, हर पेज को पार्स कर लिया जाता है और वह इस्तेमाल के लिए तैयार हो जाता है. हालांकि, उसकी सीएसएस/एचटीएमएल/जेएस सिर्फ़ तब लागू होती है, जब उसके पैरंट <template> को स्टैंप किया जाता है. वेब कॉम्पोनेंट का इस्तेमाल करके, डाइनैमिक + लेज़ी व्यू.

आने वाले समय में होने वाले सुधार

पेज पहली बार लोड होने पर, हम हर पेज के लिए सभी एचटीएमएल इंपोर्ट को एक साथ लोड कर रहे हैं. एक और सुधार यह होगा कि एलिमेंट परिभाषाओं को लेज़ी लोड सिर्फ़ तब किया जाए, जब उनकी ज़रूरत हो. Polymer में, एचटीएमएल इंपोर्ट को एसिंक्रोनस तरीके से लोड करने के लिए भी एक अच्छा हेल्पर है:

Polymer.Base.importHref('io-home-page.html', (e) => { ... });

IOWA ऐसा नहीं करता है क्योंकि a) हम आलसी हैं और b) यह साफ़ तौर पर नहीं पता कि प्रदर्शन में कितना सुधार हो सकता था. हमारा पहला पेंट पहले से ही ~1 सेकंड का था.

पेज का लाइफ़साइकल मैनेजमेंट

कस्टम एलिमेंट एपीआई, किसी कॉम्पोनेंट के स्टेटस को मैनेज करने के लिए, "लाइफ़साइकल कॉलबैक" तय करता है. इन तरीकों को लागू करने पर, आपको कॉम्पोनेंट के लाइफ़ साइकल में फ़्री हुक मिलते हैं:

createdCallback() {
    // automatically called when an instance of the element is created.
}

attachedCallback() {
    // automatically called when the element is attached to the DOM.
}

detachedCallback() {
    // automatically called when the element is removed from the DOM.
}

attributeChangedCallback() {
    // automatically called when an HTML attribute changes.
}

IOWA में इन कॉलबैक का फ़ायदा लेना आसान था. याद रखें कि हर पेज एक अलग डीओएम नोड होता है. हमारे एसपीए में "नए व्यू" पर जाने के लिए, एक नोड को डीओएम से अटैच करना और दूसरे को हटाना होता है.

हमने सेट अप का काम करने के लिए, attachedCallback का इस्तेमाल किया है. इसमें, इवेंट लिसनर को अटैच किया गया है. जब उपयोगकर्ता किसी दूसरे पेज पर जाते हैं, तो detachedCallback क्लीनअप करता है (लिसनर हटाता है, शेयर की गई स्थिति को रीसेट करता है). हमने नेटिव लाइफ़साइकल कॉलबैक को भी अपने कई कॉलबैक के साथ बड़ा किया है:

onPageTransitionDone() {
    // page transition animations are complete.
},

onSubpageTransitionDone() {
    // sub nav/tab page transitions are complete.
}

ये बदलाव, काम में लगने वाले समय को बढ़ाने और पेज ट्रांज़िशन के बीच होने वाली रुकावट को कम करने के लिए, काम के थे. इस विषय पर ज़्यादा जानकारी बाद में.

सभी पेजों पर आम तौर पर इस्तेमाल होने वाले फ़ंक्शन को बंद करना

इनहेरिटेंस, कस्टम एलिमेंट की एक बेहतरीन सुविधा है. यह वेब के लिए, स्टैंडर्ड इनहेरिटेंस मॉडल उपलब्ध कराता है.

माफ़ करें, लिखने के समय ही Polymer 1.0 में एलिमेंट इनहेरिटेंस अभी लागू करना बाकी है. इस दौरान, Polymer की बहीवियर सुविधा भी उतनी ही काम की थी. व्यवहार, सिर्फ़ मिक्सिन होते हैं.

सभी पेजों पर एक जैसा एपीआई सरफ़ेस बनाने के बजाय, शेयर किए गए मिक्सिन बनाकर कोडबेस को डेटा में व्यवस्थित करना ज़्यादा आसान हो गया है. उदाहरण के लिए, PageBehavior उन सामान्य प्रॉपर्टी/तरीकों के बारे में बताता है जिनकी हमारे ऐप्लिकेशन के सभी पेजों को ज़रूरत होती है:

PageBehavior.html

let PageBehavior = {

    // Common properties all pages need.
    properties: {
    name: { type: String }, // Slug name of the page.
    ...
    },

    attached() {
    // If the page defines a `onPageTransitionDone`, call it when the router
    // fires 'page-transition-done'.
    if (this.onPageTransitionDone) {
        this.listen(document.body, 'page-transition-done', 'onPageTransitionDone');
    }

    // Update page meta data when new page is navigated to.
    document.body.id = `page-${this.name}`;
    document.title = this.title || 'Google I/O 2016';

    // Scroll to top of new page.
    if (IOWA.Elements.Scroller) {
        IOWA.Elements.Scroller.scrollTop = 0;
    }

    this.setupSubnavEffects();
    },

    detached() {
    this.unlisten(document.body, 'page-transition-done', 'onPageTransitionDone');
    this.teardownSubnavEffects();
    }
};

IOWA.IOBehaviors = IOWA.IOBehaviors || {PageBehavior: PageBehavior};

जैसा कि आप देख सकते हैं, PageBehavior ऐसे सामान्य काम करता है जो किसी नए पेज पर जाते हैं. जैसे, document.title को अपडेट करना, स्क्रोल की पोज़िशन को रीसेट करना, और स्क्रोल और सब नेविगेशन इफ़ेक्ट के लिए इवेंट लिसनर सेट अप करना.

अलग-अलग पेज, PageBehavior को डिपेंडेंसी के तौर पर लोड करके और behaviors का इस्तेमाल करके, PageBehavior का इस्तेमाल करते हैं. ज़रूरत पड़ने पर, वे अपनी बुनियादी प्रॉपर्टी/तरीका भी बदल सकते हैं. उदाहरण के लिए, यहां बताया गया है कि हमारे होम पेज का "सबक्लास" क्या बदलता है:

io-home-page.html

<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="PageBehavior.html">
<!-- rest of the import dependencies used by the page. -->

<dom-module id="io-home-page">
    <template>
    <!-- PAGE'S MARKUP -->
    </template>
    <script>
    Polymer({
        is: 'io-home-page',

        behaviors: [IOBehaviors.PageBehavior], // All pages have common functionality.

        // Pages define their own title and slug for the router.
        title: 'Schedule - Google I/O 2016',
        name: 'home',

        // The home page has custom setup work when it's added navigated to.
        // Note: PageBehavior's attached also gets called.
        attached() {
        if (this.app.isPhoneSize) {
            this.listen(IOWA.Elements.ScrollContainer, 'scroll', '_onPageScroll');
        }
        },

        // The home page does its own cleanup when a new page is navigated to.
        // Note: PageBehavior's detached also gets called.
        detached() {
        this.unlisten(IOWA.Elements.ScrollContainer, 'scroll', '_onPageScroll');
        },

        // The home page can define onPageTransitionDone to do extra work
        // when page transitions are done, and thus preventing janky animations.
        onPageTransitionDone() {
        ...
        }
    });
    </script>
</dom-module>

स्टाइल शेयर करना

अपने ऐप्लिकेशन के अलग-अलग कॉम्पोनेंट में स्टाइल शेयर करने के लिए, हमने Polymer के शेयर किए गए स्टाइल मॉड्यूल का इस्तेमाल किया. स्टाइल मॉड्यूल की मदद से, सीएसएस का एक हिस्सा एक बार तय किया जा सकता है और उसे पूरे ऐप्लिकेशन में अलग-अलग जगहों पर फिर से इस्तेमाल किया जा सकता है. हमारे लिए, "अलग-अलग जगहों" का मतलब अलग-अलग कॉम्पोनेंट से है.

IOWA में, हमने shared-app-styles बनाया है, ताकि हम अपने बनाए गए पेजों और अन्य कॉम्पोनेंट में रंग, टाइपोग्राफ़ी, और लेआउट क्लास शेयर कर सकें.

shared-app-styles.html

<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/iron-flex-layout/iron-flex-layout.html">
<link rel="import" href="../bower_components/paper-styles/color.html">

<dom-module id="shared-app-styles">
    <template>
    <style>
        [layout] {
        @apply(--layout);
        }
        [layout][horizontal] {
        @apply(--layout-horizontal);
        }
        .scrollable {
        @apply(--layout-scroll);
        }
        .noscroll {
        overflow: hidden;
        }
        /* Style radio buttons and tabs the same throughout the app */
        paper-tabs {
        --paper-tabs-selection-bar-color: currentcolor;
        }
        paper-radio-button {
        --paper-radio-button-checked-color: var(--paper-cyan-600);
        --paper-radio-button-checked-ink-color: var(--paper-cyan-600);
        }
        ...
    </style>
    </template>
</dom-module>

io-home-page.html

<link rel="import" href="shared-app-styles.html">
<!-- Rest of import dependencies used by the page. -->

<dom-module id="io-home-page">
    <template>
    <style include="shared-app-styles">
        :host { display: block} /* Other element styles can go here. */
    </style>
    <!-- PAGE'S MARKUP -->
    </template>
    <script>Polymer({...});</script>
</dom-module>

यहां <style include="shared-app-styles"></style>, Polymer का सिंटैक्स है, जिसका मतलब है कि "shared-app-styles" नाम के मॉड्यूल में स्टाइल शामिल करें.

ऐप्लिकेशन की स्थिति शेयर करना

अब तक आपको पता चल चुका है कि हमारे ऐप्लिकेशन में मौजूद हर पेज एक कस्टम एलिमेंट है. मैंने लाखों बार कहा है. ठीक है, लेकिन अगर हर पेज एक अलग वेब कॉम्पोनेंट है, तो हो सकता है कि आप खुद से पूछ रहे हों कि हम ऐप्लिकेशन में स्टेटस कैसे शेयर करते हैं.

IOWA, स्टेटस शेयर करने के लिए, डिपेंडेंसी इंजेक्शन (Angular) या Redux (React) जैसी तकनीक का इस्तेमाल करता है. हमने एक ग्लोबल app प्रॉपर्टी बनाई और उससे शेयर की गई सब-प्रॉपर्टी जोड़ीं. app इसे हमारे ऐप्लिकेशन में हर उस कॉम्पोनेंट में इंजेक्ट करके पास किया जाता है जिसे इसके डेटा की ज़रूरत होती है. Polymer की डेटा बाइंडिंग सुविधाओं का इस्तेमाल करके, यह काम आसानी से किया जा सकता है. ऐसा इसलिए, क्योंकि हम बिना कोई कोड लिखे वायरिंग कर सकते हैं:

<lazy-pages>
    <template is="dom-if" name="home">
    <io-home-page date="2016-05-18T17:00:00Z" app="[[app]]"></io-home-page>
    </template>

    <template is="dom-if" name="schedule">
    <io-schedule-page date="2016-05-18T17:00:00Z" app="{ % templatetag openvariable % }app}}"></io-schedule-page>
    </template>
    ...
</lazy-pages>

<google-signin client-id="..." scopes="profile email"
                            user="{ % templatetag openvariable % }app.currentUser}}"></google-signin>

<iron-media-query query="(min-width:320px) and (max-width:768px)"
                                query-matches="{ % templatetag openvariable % }app.isPhoneSize}}"></iron-media-query>

जब उपयोगकर्ता हमारे ऐप्लिकेशन में लॉग इन करते हैं, तो <google-signin> एलिमेंट अपनी user प्रॉपर्टी को अपडेट करता है. यह प्रॉपर्टी app.currentUser से बंधी होती है. इसलिए, जिस पेज को मौजूदा उपयोगकर्ता को ऐक्सेस करना है उसे सिर्फ़ app से बंधना होगा और currentUser सब-प्रॉपर्टी को पढ़ना होगा. यह तकनीक, ऐप्लिकेशन में स्टेटस शेयर करने के लिए अपने-आप काम की है. हालांकि, इसका एक और फ़ायदा यह हुआ कि हमने एक ही साइन इन एलिमेंट बनाया और साइट पर इसके नतीजों का फिर से इस्तेमाल किया. मीडिया क्वेरी के लिए भी यही बात लागू होती है. हर पेज के लिए, साइन इन की प्रक्रिया को दोहराना या मीडिया क्वेरी का अपना सेट बनाना बेकार होता. इसके बजाय, ऐप्लिकेशन के लेवल पर ऐसे कॉम्पोनेंट मौजूद होते हैं जो ऐप्लिकेशन के सभी फ़ंक्शन/डेटा के लिए ज़िम्मेदार होते हैं.

पेज ट्रांज़िशन

Google I/O वेब ऐप्लिकेशन पर नेविगेट करने पर, आपको इसके शानदार पेज ट्रांज़िशन (मटीरियल डिज़ाइन à la) दिखेंगे.

IOWA का पृष्ठ क्रियाशील है.
आईओवा के पेज ट्रांज़िशन की कार्रवाई.

जब उपयोगकर्ता किसी नए पेज पर जाते हैं, तो कई चीज़ें होती हैं:

  1. सबसे ऊपर मौजूद नेविगेशन बार, चुनने के बार को नए लिंक पर स्लाइड करता है.
  2. पेज की हेडिंग धीरे-धीरे फीकी हो जाती है.
  3. पेज का कॉन्टेंट नीचे की ओर स्लाइड करता है और फिर धीरे-धीरे गायब हो जाता है.
  4. उन एनिमेशन को रिवर्स करने से नए पेज का शीर्षक और सामग्री दिखाई देती है.
  5. (ज़रूरी नहीं) नया पेज, शुरू करने से जुड़ा अतिरिक्त काम करता है.

हमारे लिए सबसे बड़ी चुनौती यह थी कि परफ़ॉर्मेंस से समझौता किए बिना इस शानदार ट्रांज़िशन को कैसे पूरा किया जाए. इसमें बहुत सारे डाइनैमिक काम होते हैं और हमारी पार्टी में लगातार रुक-रुककर चलने वाली समस्या को आने का कोई मौका नहीं दिया गया. हमारा समाधान, Web Animations API और Promises का कॉम्बिनेशन था. इन दोनों का एक साथ इस्तेमाल करने से, हमें कई तरह के विकल्प मिले. साथ ही, प्लग और प्ले ऐनिमेशन सिस्टम और das जंक को कम करने के लिए ज़्यादा कंट्रोल मिला.

यह सुविधा कैसे काम करती है

जब उपयोगकर्ता किसी नए पेज पर क्लिक करते हैं या 'वापस जाएं'/'आगे जाएं' बटन दबाते हैं, तो हमारे राऊटर का runPageTransition(), Promises की एक सीरीज़ चलाकर अपना जादू दिखाता है. Promises का इस्तेमाल करके, हमने ऐनिमेशन को ध्यान से ऑर्केस्ट्रेट किया. साथ ही, सीएसएस ऐनिमेशन और डाइनैमिक तौर पर लोड होने वाले कॉन्टेंट के "असाइन-नेस" को तर्कसंगत बनाने में मदद मिली.

class Router {

    init() {
    window.addEventListener('popstate', e => this.runPageTransition());
    }

    runPageTransition() {
    let endPage = this.state.end.page;

    this.fire('page-transition-start');              // 1. Let current page know it's starting.

    IOWA.PageAnimation.runExitAnimation()            // 2. Play exist animation sequence.
        .then(() => {
        IOWA.Elements.LazyPages.selected = endPage;  // 3. Activate new page in <lazy-pages>.
        this.state.current = this.parseUrl(this.state.end.href);
        })
        .then(() => IOWA.PageAnimation.runEnterAnimation())  // 4. Play entry animation sequence.
        .then(() => this.fire('page-transition-done')) // 5. Tell new page transitions are done.
        .catch(e => IOWA.Util.reportError(e));
    }

}

"बेकार चीज़ों को हटाना: सभी पेजों पर सामान्य फ़ंक्शन" सेक्शन से याद करें कि पेज, page-transition-start और page-transition-done डीओएम इवेंट के लिए सुनते हैं. अब आपको यह दिख रहा है कि ये इवेंट कहां ट्रिगर होते हैं.

हमने runEnterAnimation/runExitAnimation हेल्पर के बजाय, Web Animations API का इस्तेमाल किया है. runExitAnimation के मामले में, हम कुछ डीओएम नोड (मास्टहेड और मुख्य कॉन्टेंट एरिया) हासिल करते हैं, हर ऐनिमेशन के शुरू/खत्म होने का एलान करते हैं, और दोनों को साथ-साथ चलाने के लिए GroupEffect बनाते हैं:

function runExitAnimation(section) {
    let main = section.querySelector('.slide-up');
    let masthead = section.querySelector('.masthead');

    let start = {transform: 'translate(0,0)', opacity: 1};
    let end = {transform: 'translate(0,-100px)', opacity: 0};
    let opts = {duration: 400, easing: 'cubic-bezier(.4, 0, .2, 1)'};
    let opts_delay = {duration: 400, delay: 200};

    return new GroupEffect([
    new KeyframeEffect(masthead, [start, end], opts),
    new KeyframeEffect(main, [{opacity: 1}, {opacity: 0}], opts_delay)
    ]);
}

व्यू ट्रांज़िशन को ज़्यादा (या कम) बेहतर बनाने के लिए, ऐरे में बदलाव करें!

स्क्रोल इफ़ेक्ट

पेज को स्क्रोल करने पर, IOWA में कुछ दिलचस्प इफ़ेक्ट दिखते हैं. पहला विकल्प हमारा फ़्लोटिंग ऐक्शन बटन (एफ़एबी) है, जो लोगों को पेज पर सबसे ऊपर वापस ले जाता है:

    <a href="#" tabindex="-1" aria-hidden="true" aria-label="back to top" onclick="backToTop">
      <paper-fab icon="io:expand-less" noink tabindex="-1"></paper-fab>
    </a>

स्मूद स्क्रोल को लागू करने के लिए, Polymer के app-layout एलिमेंट का इस्तेमाल किया जाता है. इन ऐप्लिकेशन में, शानदार स्क्रोलिंग इफ़ेक्ट मिलते हैं. जैसे, स्टिकी/लौटने वाले टॉप नेविगेशन, ड्रॉप शैडो, रंग और बैकग्राउंड ट्रांज़िशन, पैरालैक्स इफ़ेक्ट, और स्मूद स्क्रोलिंग.

    // Smooth scrolling the back to top FAB.
    function backToTop(e) {
      e.preventDefault();

      Polymer.AppLayout.scroll({top: 0, behavior: 'smooth',
                                target: document.documentElement});

      e.target.blur();  // Kick focus back to the page so user starts from the top of the doc.
    }

हमने <app-layout> एलिमेंट का इस्तेमाल, स्टिक नेविगेशन के लिए भी किया है. जैसा कि वीडियो में देखा जा सकता है, जब उपयोगकर्ता पेज को नीचे की ओर स्क्रोल करते हैं, तो यह विज्ञापन हट जाता है. साथ ही, जब वे ऊपर की ओर स्क्रोल करते हैं, तो यह विज्ञापन फिर से दिखने लगता है.

स्क्रोल किए जा सकने वाले नेविगेशन
का इस्तेमाल करके, स्टिकी स्क्रोल नेविगेशन.

हमने <app-header> एलिमेंट का इस्तेमाल, ज़्यादा बदलाव किए बिना किया. ऐप्लिकेशन में इसे जोड़ना और शानदार स्क्रोलिंग इफ़ेक्ट पाना आसान था. बेशक, हम इन्हें खुद लागू कर सकते थे, लेकिन फिर से इस्तेमाल किए जा सकने वाले कॉम्पोनेंट में जानकारी पहले से कोड में होने से, हमें काफ़ी समय की बचत हुई.

एलिमेंट का एलान करें. एट्रिब्यूट की मदद से, इसे पसंद के मुताबिक बनाएं. आपका काम पूरा हुआ!

    <app-header reveals condenses effects="fade-background waterfall"></app-header>

नतीजा

I/O के प्रगतिशील वेब ऐप्लिकेशन के लिए, हमने वेब कॉम्पोनेंट और Polymer के पहले से बने मटीरियल डिज़ाइन विजेट की मदद से, कुछ हफ़्तों में पूरा फ़्रंटएंड बना लिया. नेटिव एपीआई (कस्टम एलिमेंट, शैडो डीओएम, <template>) की सुविधाएं, एसपीए के डाइनैमिक तरीके से काम करती हैं. फिर से इस्तेमाल करने से काफ़ी समय बचता है.

अगर आपको अपना प्रगतिशील वेब ऐप्लिकेशन बनाना है, तो ऐप्लिकेशन टूलबॉक्स देखें. Polymer का ऐप्लिकेशन टूलबॉक्स, Polymer के साथ पीडब्ल्यूए बनाने के लिए, कॉम्पोनेंट, टूल, और टेंप्लेट का एक कलेक्शन है. इसे सेट अप करना आसान है.