LWC Router自分で作る


1.目的

今回自分で開発したSalesforce側使えるLWCRouterを紹介します。
現在ReactやVueなど其々自分のRouterがあり、SPAでの画面遷移でますが、
LWCではRouterようなライブラリがないですが、slotタグ、NavigationMixinと
CurrentPageReferenceを使って、Routerみたいな機能を実装できます。

2.ソース構成図

lwc
    ├─router
    ├─myRouterContainer
    ├─pageA
    ├─pageB

router

router.html
<template>
    <template if:true={isCurrentPageName}>
        <slot></slot>
    </template>
</template>
router.js
import { LightningElement, api, wire } from 'lwc';
import { CurrentPageReference, NavigationMixin } from 'lightning/navigation';
export default class Router extends NavigationMixin(LightningElement) {
    //ルーターパス宣言
    @api path;

    //現在のルーターパス
    @wire(CurrentPageReference)
    currentPageReference;
    /**
     * 現在のページかを判断する
     */
    get isCurrentPageName() {
        const { c__pageName } = this.currentPageReference.state;
        const { path } = this;
        return path === c__pageName;
    }
}
router.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>51.0</apiVersion>
    <isExposed>false</isExposed>
</LightningComponentBundle>

myRouterContainer

myRouterContainer.html
<template>
    <!-- デフォルトページ -->
    <c-router>
        <c-page-a></c-page-a>
    </c-router>
    <!-- ページ -->
    <c-router path="A">
        <c-page-a></c-page-a>
    </c-router>
    <c-router path="B">
        <c-page-b></c-page-b>
    </c-router>
    <!-- ページ追加↑↑↑↑ -->
</template>
myRouterContainer.js
import { LightningElement } from 'lwc';

export default class myRouterContainer extends LightningElement {}
myRouterContainer.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>51.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__Tab</target>
    </targets>
</LightningComponentBundle>

ページA

pageA.html
<template>
    <div class="slds-card" style="height:300px">
        <div>ここはページA</div>
        <lightning-button onclick={navigateToB} label="ページBへ遷移"></lightning-button>
    </div>
</template>
pageA.js
import { LightningElement, wire } from 'lwc';
import { NavigationMixin, CurrentPageReference } from 'lightning/navigation';
export default class PageA extends NavigationMixin(LightningElement) {

    //現在のルーターパス
    @wire(CurrentPageReference)
    currentPageReference;

    /**
     * 画面Bへ遷移
     * @param {*} event 
     */
    navigateToB(event) {
        event.preventDefault();
        this.navigateToNextPage('B');
    }

    /**
     * 画面遷移
     * @param {*} pageName 
     */
    navigateToNextPage(pageName) {
        const { apiName } = this.currentPageReference.attributes;
        this[NavigationMixin.Navigate]({
            type: 'standard__webPage',
            attributes: {
                url: `/lightning/n/${apiName}?c__pageName=${pageName}`
            }
        });
    }
}
pageA.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>51.0</apiVersion>
    <isExposed>false</isExposed>
</LightningComponentBundle>

ページB

pageB.html
<template>
    <div class="slds-card" style="height:300px">
        <div>ここはページB</div>
        <lightning-button onclick={navigateToA} label="ページAへ遷移"></lightning-button>
    </div>
</template>
pageB.js
import { LightningElement, wire } from 'lwc';
import { NavigationMixin, CurrentPageReference } from 'lightning/navigation';
export default class PageB extends NavigationMixin(LightningElement) {

    //現在のルーターパス
    @wire(CurrentPageReference)
    currentPageReference;

    /**
     * 画面Bへ遷移
     * @param {*} event 
     */
    navigateToA(event) {
        event.preventDefault();
        this.navigateToNextPage('A');
    }

    /**
     * 画面遷移
     * @param {*} pageName 
     */
    navigateToNextPage(pageName) {
        const { apiName } = this.currentPageReference.attributes;
        this[NavigationMixin.Navigate]({
            type: 'standard__webPage',
            attributes: {
                url: `/lightning/n/${apiName}?c__pageName=${pageName}`
            }
        });
    }
}
pageB.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>51.0</apiVersion>
    <isExposed>false</isExposed>
</LightningComponentBundle>

3.Salesforce側動作確認

Salesforce側Lightning コンポーネントタブを作成

タブを開く

デフォルトはページAを表示する

ページBへ遷移ボタン押下すると、ページAからページBへ遷移する

ページAへ遷移ボタン押下すると、ページBからページAへ遷移する