Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PWA へ至る道 (Compressed Version) / WBNagoya 2018-07

PWA へ至る道 (Compressed Version) / WBNagoya 2018-07

More Decks by Toro_Unit (Hiroshi Urabe)

Other Decks in Programming

Transcript

  1. PWA ΁ࢸΔಓ
    (Compressed Version)
    Toro_Unit 2018.07.07 / [email protected]
    1

    View Slide

  2. 2

    View Slide

  3. • ͜ͷ࿩͸ɺWordBenchେࡕ & ژ౎ & Ionic Japan 5݄ ϑϧε
    Πϯά Ͱ50෼ऑ࿩ͨ͠ωλΛ5෼ʹѹॖͨ͠ϞϊͰ͢ɻ
    • Uncompressed version:
    • https://speakerdeck.com/torounit/wordpress-de-pwa-
    hezhi-rudao
    • ͍Ζ͍Ζͬ͢ඈ͹͠·͢ɻ
    • ෆ໌఺ͳͲ͸ੋඇฉ͍ͯԼ͍͞ɻ
    • ๻͕ਲͬ෷͏લʹ͓ئ͍͠·͢ɻ
    3

    View Slide

  4. $ whoami
    4

    View Slide

  5. Toro_Unit
    ઎෦ ߛ (͏Β΂ ͻΖ͠)
    • Frontend Engineer
    • WordPress Plugin and Theme
    Developer
    • Docker ͱ Haskell ͸͡Ί·ͨ͠ɻ
    Github: @torounit
    Twitter: @Toro_Unit
    5

    View Slide

  6. ࡳຈͷձࣾͰϦϞʔτϫʔΫͯ͠·͢
    6

    View Slide

  7. 7

    View Slide

  8. Plugins and Themes
    • Custom Post Type Permalinks
    • Simple Post Type Permalinks
    • Powerful Posts Per Page (PPPP)
    • Responsive Slide
    • Vanilla
    • and more...
    8

    View Slide

  9. ৴भ WordPress Meetup
    • WordBench ௕໺ΛҾ͖ܧ͙ܗͰɻ
    • WordPressΛࡘʹϐβ৯ͬͯΘʔ
    Θʔ஻Δձͩͱࢥͬͯ·͢ɻ
    • ࣮͸Ҋ֎ओ්཰ߴ͍ɻ
    • vol.1: 7/21 Sat.
    • vol.2: 8/18 Sat.
    • ʢদຊͰϏʔϧࡇ΍ͬͯ·͢ʣ
    9

    View Slide

  10. 10

    View Slide

  11. 11

    View Slide

  12. • ͠ͳͷ1Ͱདྷͨɻ
    • ͠ͳͷӡٳͷͨΊɺࡢ൩ɺಛٸ͋ͣ͞Ͱ౦ژɻࠓே৽װઢ
    Ͱ໊ݹ԰΁ɻ
    • ;ͩΜ͸͠ͳͷͰ͘Δɻ
    1 Ksaaaaaaaaaaaaaa / CC-BY-SA-3.0
    12

    View Slide

  13. ωοτΊͬͪΌ੾ΕΔɻ
    ͭΒ͍ɻ
    13

    View Slide

  14. §1. Progressive Web Apps on WordPress
    14

    View Slide

  15. 15

    View Slide

  16. ͩΕ͔͕ݴͬͨ
    ʮશͯͷαΠτ͸PWAʹͳΔ΂͖ɻʯ
    16

    View Slide

  17. SEYANA
    Θ͔Δ
    17

    View Slide

  18. Progressive Web Apps ͷࡶͳઆ໌
    • WEBαΠτͱΞϓϦͷ͍͍ͱ͜औΓΛͨ͠Α͏ͳϠπɻ
    • WEBαΠτΛͦͷ··ΞϓϦέʔγϣϯʹɻ
    • ΦϑϥΠϯͰ΋ಈ࡞ɻ
    • Android Ͱ΋ iOS Ͱ΋ Windows Ͱ΋αϙʔτ͞ΕͯΔɻ
    • Chrome ΞϓϦ΋͜Εɻ
    18

    View Slide

  19. WordPress ͰͲ͏΍Δʁ
    • HTML/CSS/JS Ͱͭͬͯ͘ɺREST API ͚ͩ࢖͏ɻ
    • ϑϩϯτΤϯυ͸ɺS3 ͱ͔ Netlify ͱ͔ Github Pages ͱ͔ʹ
    ஔ͍ͯɺWordPress ͸ REST API ͚ͩ࢖͏ɻ
    • ϞμϯͳΞϓϩʔνɻ
    19

    View Slide

  20. • ͦΕ WordPress ͡Όͳ͍΄͏ָ͕ͳ৔߹΋ɻ
    • Contentful ͱ͔ Strapi Έ͍ͨͳ Headless CMS ࢖͑͹ָ͔΋ɻ
    • طଘͷ WordPress αΠτͰ͸࢖͑ͳ͍͠ɺӡ༻ϑϩʔ΋ߟ͑௚͞ͳ͍
    ͱɻ
    • ྫ͑͹ϓϨϏϡʔ͕ग़དྷͳ͍ͱ͔ɻ
    • طଘαΠτͷผͷ૭ޱͱͯ͠͸༗༻ɻ
    • WEBαΠτͱ͸ผʹΞϓϦΛ࡞ΔΑ͏ͳέʔεͱ͔ɻ
    • WordPress ͷςʔϚͱ͍͏ΑΓ͸γεςϜશମͱͯͪ͠ΌΜͱઃܭ͢Δ
    ඞཁɻ
    20

    View Slide

  21. طଘͷ WordPress αΠτͰ͸೉͍͠ɻ
    21

    View Slide

  22. ʮશͯͷαΠτ͸PWAʹͳΔ΂͖ɻʯ
    Ͱ͖ͳ͍΍Μ͚ʂʂ
    22

    View Slide

  23. 23

    View Slide

  24. ͱ͍͏Θ͚ͰطଘͷWordPressαΠτΛPWAԽ͢ΔϓϥάΠ
    ϯ࡞Γ·ͨ͠ɻ
    • Smart PWA — WordPress Plugins
    • torounit/smart-pwa
    • GWதϩΫʹՈ͔Βग़ͣʹͣͬͱίʔυॻ͍ͯͨ
    24

    View Slide

  25. ΍ͬͨ͜ͱɻ
    25

    View Slide

  26. /**
    * Service Worker
    *
    * @package Smart_PWA
    */
    ?>
    'use strict';
    const APP_SHELL_CACHE_NAME = 'smart-pwa-';
    const RUNTIME_CACHE_NAME = 'smart-pwa-runtime-cache';
    const NOT_AVAILABLE_KEY = '';
    const PRE_CACHE_ASSETS = JSON.parse( '' );
    const urlsToPreCache = [
    '/',
    NOT_AVAILABLE_KEY,
    ].concat( PRE_CACHE_ASSETS );
    const updateMessage = ( message ) => {
    self.clients.matchAll().then( clients =>
    clients.forEach( client => client.postMessage( message ) ) );
    }
    self.addEventListener( 'install', ( event ) => {
    console.log( '[ServiceWorker] Install' );
    event.waitUntil(
    caches.open( APP_SHELL_CACHE_NAME )
    .then( ( cache ) => {
    console.log( '[ServiceWorker] Caching app' );
    return cache.addAll( urlsToPreCache );
    } )
    );
    } );
    self.addEventListener( 'activate', ( event ) => {
    console.log( '[ServiceWorker] Activate' );
    const cacheWhitelist = [ APP_SHELL_CACHE_NAME ];
    event.waitUntil(
    caches.keys().then( ( cacheNames ) => {
    return Promise.all(
    cacheNames.map( ( cacheName ) => {
    if (cacheWhitelist.indexOf( cacheName ) === - 1) {
    console.log( '[ServiceWorker] Removing old cache', cacheName );
    return caches.delete( cacheName );
    }
    } )
    );
    } )
    );
    } );
    26

    View Slide

  27. self.addEventListener( 'fetch', ( event ) => {
    if (
    event.request.url.indexOf( 'wp-admin' ) === - 1 &&
    event.request.url.indexOf( 'wp-login' ) === - 1 &&
    event.request.url.indexOf( 'customize_changeset_uuid' ) === - 1 &&
    event.request.method === 'GET'
    ) {
    event.respondWith(
    caches.match( event.request ).then( ( responseFromCache ) => {
    //Return static content in cache.
    if (responseFromCache) {
    if ([ 'style', 'script', 'image' ].indexOf( event.request.destination ) > - 1) {
    return responseFromCache;
    }
    }
    //Fetch and save content to cache.
    return fetch( event.request ).then( ( response ) => {
    //Not 200.
    if (! response || response.status !== 200 || response.type !== 'basic') {
    return response;
    }
    let responseToCache = response.clone();
    caches.open( RUNTIME_CACHE_NAME ).then( cache => {
    cache.put( event.request, responseToCache );
    console.log( '[ServiceWorker] Fetched&Cached Data', event.request.url );
    updateMessage( {
    key: 'updateContent',
    value: event.request.url
    } );
    } );
    return response;
    } ).catch( ( reason ) => {
    //when fetching failed, return content in cache.
    if (responseFromCache) {
    console.log( '[ServiceWorker] Cache Matched!', event.request.url, responseFromCache );
    return responseFromCache;
    }
    //if not found in cache, return fallback page.
    if (event.request.mode === 'navigate') {
    return caches.match( NOT_AVAILABLE_KEY ).then( ( response ) => {
    return response;
    } );
    }
    } );
    } )
    );
    }
    } );
    27

    View Slide

  28. • global $wp_style, global $wp_scripts Λ୳ͯ͠ɺenqueue ͞Εͯ
    Δ CSS, JS ΛऔಘɻͦΕΛ PreCache ʹɻ
    • Ұఆ࣌ؒຖʹɺPreCache ͷ࠶औಘ & Worker ͷߋ৽ɻ
    • Transient API ࢖ͬͯΔ͚ͩͰ͕͢ɻ
    • style, script, image ͸ΩϟογϡΛฦ٫ɻhttps://
    fetch.spec.whatwg.org/#requests
    • ͦΕҎ֎ͷ͸ɺfetch࣌ʹΩϟογϡͭͭ͠ɺݟ͔ͭΒͳ͚Ε͹Ωϟο
    γϡΛฦ٫ɻ
    • ͦΕ΋ͩΊͳΒɺݱࡏ͝ར༻௖͚·ͤΜϖʔδΛฦ٫ɻ
    28

    View Slide

  29. লུ
    29

    View Slide

  30. • torounit/smart-pwa ಡΜͰԼ͍͞ɻ
    • ৮ͬͯΈͯԼ͍͞ɻ
    • ๻͕ਲͬ෷͏લʹฉ͍ͯԼ͍͞ɻ
    30

    View Slide

  31. DEMO.
    https://torounit.com
    31

    View Slide

  32. ඍົϙΠϯτ
    • HTMLΛ·Δ͝ͱ֤ϖʔδ͝ͱʹΩϟογϡ͍ͯ͠Δɻ
    • ςʔϚม͑ͨͱ͖ͭΒ͍ɻ ίϯςϯπͱݟͨ໨ͷ෼཭͕ग़དྷແ͍ͷਏ͍ɻ
    • αΠυόʔͳͲڞ௨෦෼ͷมߋ΋ɺΩϟογϡ͞Ε͍ͯΔ΋ͷͱͦ͏Ͱͳ
    ͍΋ͷͰมΘΔɻ
    • ΩϟογϡͷංେԽɻ
    ͜ΕΒͷ໰୊Λղܾ͢Δʹ͸ɺSingle-page Application(SPA)ͳ WordPressςʔ
    Ϛ Λߟ͑ͳ͍ͱ͍͚ͳ͍ɻ
    32

    View Slide

  33. §2. PWA on WordPress Theme
    33

    View Slide

  34. ͱ͍͏Θ͚Ͱͭͬͨ͘ɻ
    34

    View Slide

  35. torounit/Aetherium
    • DEMO: https://aetherium-demo.torounit.com/
    • Vue + Vuex + Workbox
    • SPA + PWA ͳWordPress ςʔϚɻ
    • REST API ͱͷ௨৴͸ɺWordPressʹಉࠝͷΫϥΠΞϯτɻ
    (Backbone.js !!!)
    35

    View Slide

  36. URL ͷ ύʔεਏ͍໰୊ɻ
    • WordPress ͷURL͸ࣗ༝ʹϢʔβʔ͕ઃఆՄೳɻ
    • ͦΕʹԠͯ͡ɺΞʔΧΠϒͷURL͕มΘͬͨΓɻ
    • %postname% ͳ৔߹ɺݻఆϖʔδ(Page)Λ୳ͯ͠ɺଘࡏ͠ͳ
    ͔ͬͨΒɺ౤ߘ(Post)Λ୳͠ʹߦ͘ɻ
    • ΧελϜ౤ߘͳͲΛઃఆ͠ͳ͍ঢ়ଶͰɺ͍͍ͩͨ80ݸ͘Β͍
    ͷϦϥΠτͷϧʔϧ͕ੜ੒͞ΕΔɻ
    36

    View Slide

  37. ͜ΕΛؤுͬͯJSଆͰղऍ͢Δඞཁ͕͋Δɻ
    37

    View Slide

  38. function aetherium_get_permastructs() {
    global $wp_rewrite;
    if ( get_option( 'page_for_posts' ) ) {
    $home_structs = [
    'front-page' => '/',
    'home' => get_page_uri( get_option( 'page_for_posts' ) )
    ];
    } else {
    $home_structs = [ 'home' => '/' ];
    }
    $extra_permastructs = array_map( function ( $permastruct ) {
    return $permastruct['struct'];
    }, $wp_rewrite->extra_permastructs );
    $permastructs = [
    'search' => $wp_rewrite->get_search_permastruct(),
    'author' => $wp_rewrite->get_author_permastruct(),
    'date' => $wp_rewrite->get_date_permastruct(),
    'month' => $wp_rewrite->get_month_permastruct(),
    'year' => $wp_rewrite->get_year_permastruct(),
    ];
    if ( $wp_rewrite->use_verbose_page_rules ) {
    $permastructs['page'] = $wp_rewrite->get_page_permastruct();
    $permastructs['post'] = $wp_rewrite->permalink_structure;
    } else {
    $permastructs['post'] = $wp_rewrite->permalink_structure;
    $permastructs['page'] = $wp_rewrite->get_page_permastruct();
    }
    $permastructs = array_merge( $extra_permastructs, $home_structs, $permastructs );
    $structs = [];
    foreach ( $permastructs as $key => $value ) {
    $struct = trim( preg_replace( '/%([^\/]+)%/', ':$1', $value ), '/\\' );
    $struct = str_replace(
    [ ':year', ':monthnum', ':day', ':post_id' ],
    [ ':year(\\d{4})', ':monthnum(\\d{1,2})', ':day(\\d{1,2})', ':post_id(\\d+)' ],
    $struct
    );
    // for singular
    if ( in_array( $key, get_post_types( [ 'public' => true ] ) ) ) {
    $post_type = get_post_type_object( $key );
    // for pages.
    if ( $post_type->hierarchical ) {
    $structs[] = [ 'name' => $key, 'path' => untrailingslashit( '/' . $struct ) . '(.+?)' . '/(\\d*)?' ];
    continue;
    }
    //for posts.
    $structs[] = [ 'name' => $key, 'path' => untrailingslashit( '/' . $struct ) . '/(\\d*)?' ];
    continue;
    }
    // for hierarchical taxonomies.
    if ( in_array( $key, get_taxonomies( [ 'public' => true ] ) ) ) {
    $taxonomy = get_taxonomy( $key );
    if ( $taxonomy->hierarchical ) {
    $structs[] = [ 'name' => $key, 'path' => untrailingslashit( '/' . $struct ) . '(.+?)' . '/page/:page(\\d*)?' ];
    $structs[] = [ 'name' => $key, 'path' => untrailingslashit( '/' . $struct ) . '(.+?)' ];
    continue;
    }
    }
    //for archives.
    $structs[] = [ 'name' => $key, 'path' => untrailingslashit( '/' . $struct ) . '/page/:page(\\d*)?' ];
    $structs[] = [ 'name' => $key, 'path' => untrailingslashit( '/' . $struct ) ];
    }
    38

    View Slide

  39. ؾ߹͍Ͱύʔε͢Ε͹·͊ͳΜͱ͔ͳΔΒ͍͠ɻ
    • WP_Rewrite ͔ΒύʔϚϦϯΫߏ଄Λऔಘ͠·ͬͯ͘ɺpath-to-
    regexp (vue-router Ͱ࢖ΘΕͯΔURLύʔαʔ)͕ղऍͰ͖Δܗࣜ
    ʹؤுͬͯม׵ͯ͠JSʹ౉͢ɻ
    • ఴ෇ϑΝΠϧͷϖʔδ/ίϝϯτճΓ/ͱ͔͸ߟ͑ͨ͘ͳ͍Ͱ͢...
    • ΧελϜ౤ߘ/λΫιϊϛʔͱ͔͸݁ߏͲ͏ʹ͔ͳΔɻ
    • Custom Post Type Permalinks ͱ͔ύʔϚϦϯΫվม͢ΔϓϥάΠ
    ϯͷ͜ͱ͸஌ͬͨ͜ͱͰ͸ͳ͍ɻʢͦͷ͏ͪࢼ͔͢΋͠Εͳ͍ʣ
    39

    View Slide

  40. ౤ߘͷऔಘ
    • URLΛύʔεͨ͠Β౰વɺͦΕΛݩʹ౤ߘΛऔಘ͠ͳ͚Ε͹
    ͳΒͳ͍ɻ
    40

    View Slide

  41. import moment from 'moment/moment';
    export const date = async({ state }) => {
    let year = state.route.params.year;
    let monthnum = state.route.params.monthnum || '01';
    let day = state.route.params.day || '01';
    let first = `${year}-${monthnum}-${day}T00:00:00`;
    let last = moment( `${year}-${monthnum}-${day}` ).endOf( 'year' ).format( 'YYYY-MM-DDTHH:mm:ss' );
    let posts = await getPosts( createPostsArguments( state, { after: first, before: last }) );
    let queriedObject = {};
    return { queriedObject, posts, hasMore: getCollectionInstance( 'Posts' ).hasMore()
    };
    };
    export const term = async({ state }) => {
    let posts = [];
    let queriedObject = {};
    let taxonomy = state.taxonomies[state.route.name];
    if ( taxonomy ) {
    let restBase = taxonomy.rest_base;
    let Collection = wp.api.getCollectionByRoute( `/wp/v2/${restBase}` );
    let key = taxonomy.slug;
    let slugs = state.route.params[key].split( '/' );
    let slug = slugs.pop();
    let terms = await( new Collection() ).fetch({ data: { slug: slug } });
    queriedObject = terms[0];
    posts = await getPosts( createPostsArguments( state, { [restBase]: queriedObject.id }) );
    return { queriedObject, posts,hasMore: getCollectionInstance( 'Posts' ).hasMore() };
    }
    return { queriedObject: {}, posts: {}, hasMore: false };
    };
    export const home = async({ state }) => {
    let queriedObject = {};
    if ( global.themeSettings.pageOnFront ) {
    queriedObject = await getPost( global.themeSettings.pageForPosts, 'Page' );
    }
    let posts = await getPosts( createPostsArguments( state ) );
    return { queriedObject, posts, hasMore: getCollectionInstance( 'Posts' ).hasMore() };
    };
    export const author = async({ state }) => {
    let queriedObject = await getUserbySlug( state.route.params.author );
    let posts = await getPosts( createPostsArguments( state, { author: queriedObject.id }) );
    return { queriedObject, posts, hasMore: getCollectionInstance( 'Posts' ).hasMore() };
    };
    export const singular = async({ state }) => {
    let posts = [];
    let queriedObject = {};
    // for singular
    if ( state.route.query.preview ) {
    if ( state.route.query.preview_id ) {
    queriedObject = await getPost( state.route.query.preview_id );
    posts = [ queriedObject ];
    } else if ( state.route.query.p ) {
    queriedObject = await getPost( state.route.query.p );
    posts = [ queriedObject ];
    } else if ( state.route.query.page_id ) {
    queriedObject = await getPost( state.route.query.page_id, 'Page' );
    posts = [ queriedObject ];
    }
    } else {
    switch ( state.route.name ) {
    case 'front-page': {
    queriedObject = await getPost( global.themeSettings.pageOnFront, 'Page' );
    posts = [ queriedObject ];
    break;
    }
    case 'page':
    if ( state.route.params.pagename ) {
    let pagenames = state.route.params.pagename.split( '/' );
    let pagename = pagenames.pop();
    posts = await getPosts({ slug: pagename }, 'Pages' );
    if ( 0 < posts.length ) {
    queriedObject = posts[0];
    break;
    }
    if ( ! global.themeSettings.useVerbosePageRules ) {
    break;
    }
    }
    // if not found page, search post.
    case 'post': {
    if ( state.route.params.postname || state.route.params.pagename ) {
    posts = await getPosts({ slug: state.route.params.postname || state.route.params.pagename });
    if ( 0 < posts.length ) { queriedObject = posts[0]; }
    } else {
    queriedObject = await getPost( state.route.params.post_id, 'Post' );
    posts = [ queriedObject ];
    }
    break;
    }
    }
    }
    return { queriedObject, posts, hasMore: false };
    };
    41

    View Slide

  42. const getUserbySlug = async( slug ) => {
    let users = await( new wp.api.collections.Users() ).fetch({ data: { slug: slug } });
    if ( users[0]) { return users[0]; }
    return {};
    };
    const getPost = async( id, type = 'Post' ) => {
    let Model = wp.api.models[type];
    let model = new Model({ id: id });
    return await model.fetch();
    };
    let instances = [];
    const getCollectionInstance = ( type ) => {
    let create = () => {
    if ( instances[type]) { return instances[type]; }
    let Collection = wp.api.collections[type];
    instances[type] = new Collection();
    return instances[type];
    };
    return create();
    };
    const getPosts = async( data, type = 'Posts' ) => {
    return await getCollectionInstance( type ).fetch({ data: data });
    };
    const createPostsArguments = ( state, param = {}) => {
    let page = state.route.params.page || 1;
    let perPage = global.themeSettings.postsPerPage
    let data = { page: page, per_page: perPage };
    return Object.assign( data, param );
    };
    42

    View Slide

  43. import types from '../mutation-types';
    import * as query from './query.js';
    export const initialize = async({ commit, state }) => {
    await fetchSiteOption({ commit, state });
    await fetchTypes({ commit, state });
    await fetchTaxonomies({ commit, state });
    await fetchPosts({ commit, state });
    };
    export const fetchSiteOption = async({ commit }) => {
    let response = await fetch( global.wpApiSettings.root );
    let data = await response.json();
    commit( types.SET_SITE_OPTION, data );
    };
    export const fetchTypes = async({ commit }) => {
    let postTypes = await( new wp.api.collections.Types() ).fetch();
    commit( types.SET_POST_TYPES, postTypes );
    };
    export const fetchTaxonomies = async({ commit }) => {
    let taxnomies = await( new wp.api.collections.Taxonomies() ).fetch();
    commit( types.SET_TAXONOMIES, taxnomies );
    };
    export const fetchPosts = async({ commit, state }) => {
    let result = {
    queriedObject: {},
    hasMore: false,
    posts: []
    };
    let routeName = state.route.name;
    if ([ 'day', 'month', 'year' ].includes( routeName ) ) {
    result = await query.date({ state });
    } else if ([ 'home' ].includes( routeName ) ) {
    result = await query.home({ state });
    } else if ( Object.keys( state.taxonomies ).includes( routeName ) ) {
    result = await query.term({ state, taxonomyName: routeName });
    } else if ([ 'author' ].includes( routeName ) ) {
    result = await query.author({ commit, state });
    } else if ( state.route.query.preview || [ 'front-page', 'page', 'post' ].includes( routeName ) ) {
    result = await query.singular({ state });
    }
    commit( types.SET_QUERIED_OBJECT, result.queriedObject );
    commit( types.SET_HASMORE, result.hasMore );
    commit( types.SET_POSTS, result.posts );
    commit( types.SET_TEMPLATE_TYPE );
    };
    43

    View Slide

  44. ؾ߹͍Ͱ WP_Query::parse_query ͷؾ࣋ͪʹͳΕ͹ͳΜͱ
    ͔ͳΔ͔΋͠Εͳ͍ɻ
    • ؾ߹͍ͰͳΜͱ͔͸ͳΔɻ͜ΕͰ΋શવ଍Γͳ͍͚Ͳɻɻɻ
    • get_post ͱ͔ɺget_posts Έ͍ͨͳͷ΋౰વ JS ଆͰ࣮૷͢Δඞཁɻ
    • wp.api.getCollectionByRoute, wp.api.getModelByRoute ͱ͔࢖͑͹
    ΧελϜ౤ߘͱ͔ͱ΋ઓ͑Δ͸ͣɻ
    • λΫιϊϛʔ͸࣮૷ग़དྷͨؾ͕͢Δɻ
    • Backbone.jsϕʔεͩ͠jQuery΋ಡΈࠐΉ͚Ͳɺೝূͱ͔ߟ͑ͳͯ͘ྑ͍
    ͷ͸ָɻ
    44

    View Slide

  45. Other tips
    • άϩʔόϧͳঢ়ଶʹґଘ͢ΔϞϊ͕ଟ͍ͷͰɺVuex ͱ͔
    Redux ͱ͔࢖ͬͨํָ͕ɻ
    • query_vars ΍ɺqueried_object Έ͍ͨͳͷ͸࡞Βͳ͍
    ͱ೉͍͠ͱࢥ͏ɻ
    45

    View Slide

  46. 46

    View Slide

  47. • Workbox ͕ศརͩͬͨɻ
    • Service Worker Ͱ ΦϑϥΠϯαϙʔτ͢ΔͳΒ͜Εศརɻ
    47

    View Slide

  48. FullSpec ͷ ͍͍ͱ͜Ζ
    • Service Worker ͷ install ޙʹ fetch ͢Ε͹ॳճΞΫηε࣌ͷ
    ίϯςϯπ΋Ωϟογϡग़དྷΔɻ
    • ϑΥʔϧόοΫ࡞Γ΍͍͢ɻTOPϖʔδΛApp Shellʹಥͬࠐ
    ΜͲ͚͹ԿͱͰ΋ͳΔɻ
    • ͔͍͍ͬ͜ɻ
    48

    View Slide

  49. FullSpec ͷ σϝϦοτ
    • ΊͬͪΌ͍ͨ΁Μɻؾ߹͍ ͕ඞཁɻ
    • ςϯϓϨʔτλάతͳϞϊ͸શ࣮ͯ૷͠ͳ͍ͱ͍͚ͳ͍ɻ͖΄Μඇಉظɻ
    • SQL࢖͑ͳ͍ɻ೥ผΞʔΧΠϒͷҰཡͱ͔͸JSͰ͸ਏ͍ɻ
    • ௨ৗͷREST APIͰऔΕͳ͍஋(Φϓγϣϯɺϝλσʔλ)͸APIΛ֦ு͢Δ
    ͔ɺPHP͔ΒJSʹ஋Λ౉͢ͳͲɺͱʹ͔͍͘Ζ͍Ζ΍Βͳ͍ͱ͍͚ͳ͍ɻ
    • JS ͷboilerplateతͳͷ͸࢖͍ʹ͍͘ɻWebpackྗඞཁ
    • jQuery Λ࢖ͬͯγϣʔτίʔυͰŋŋŋΈ͍ͨͳϓϥάΠϯ͸શ෦ࢮ͵ɻ
    49

    View Slide

  50. ·ͱΊ
    • ϒϩάʹશ෦ॻ͍ͨɿ Vue.js ͱ REST API Ͱ WordPress ͷςʔϚΛ࡞ͬͯ·͢ɻ –
    Toro_Unit
    • WordPress ͬΆ͍ΞʔΩςΫνϟͰ PWA ΋·͊ग़དྷΔɻ
    • ςʔϚͰͷ SPA ͸ۀ຿ϨϕϧͳΒɺଟ෼ग़དྷΔɻʢػೳΛݶఆग़དྷΔͷͰ͋Ε͹ʣ
    • ഑෍͢ΔςʔϚͩͱ͠ΜͲͦ͏ɻ
    • WP API Ͱऔͬͯ͘ΔͷͱɺWP_Query Ͱऔͬͯ͘Δͷ͸ඍົʹҧ͏ͷͰɺશͯ
    αϙʔτͬͯͷ͸ΊͬͪΌ͠ΜͲͦ͏ɻ
    • ύʔϚϦϯΫͷਂ෵͸ਂ͍ɻ͍ͬͯ͏͔ WordPress ͷਂ෵͸ਂ͍ɻ
    50

    View Slide

  51. • torounit/smart-pwa
    • ৮ͬͯΈͯԼ͍͞ɻελʔԼ͍͞ɻ
    • ๻͕ਲͬ෷͏લʹฉ͍ͯԼ͍͞ɻ
    51

    View Slide

  52. ͓ΘΓɻ
    Thanks!
    Github: @torounit
    Twitter: @Toro_Unit
    Facebook: fb.me/torounit
    WEB Site: https://torounit.com
    52

    View Slide