Salesforce 以外の LWC
目次
プロジェクトの作成方法
ざっくりアプリケーションの雛形を作成します。
※ このやり方は 2022/03 としてはやや古いやり方です。最新版は、公式レシピ含め、Windows11 で正常に動作しなかったため、説明を諦めます。
$ npx create-lwc-app account-book ⚡⚡⚡⚡⚡ Lightning Web Components ⚡⚡⚡⚡⚡ ? Do you want to use the simple setup? Yes ? Package name for npm account-book ? Select the type of app you want to create Standard web app ? Do you want a basic Express API server? Yes create package.json create .eslintrc.json create .eslintignore create .prettierignore create .prettierrc create .gitignore create .husky\pre-commit create src\client\modules\jsconfig.json create lwc.config.json create lwc-services.config.js create jest.config.js create README.md create src\client\index.html create src\client\index.js create src\client\modules\my\app\app.css create src\client\modules\my\app\app.js create src\client\modules\my\app\app.html create src\client\modules\my\greeting\greeting.css create src\client\modules\my\greeting\greeting.js create src\client\modules\my\greeting\greeting.html create src\client\modules\my\app\__tests__\app.test.js create src\client\modules\my\greeting\__tests__\greeting.test.js create src\client\resources\lwc.png create src\client\resources\favicon.ico create scripts\server.js create src\server\api.js
プロジェクトディレクトリが作成されたので
$ cd account-book $ npm run watch
これで localhost の 3001 ポートでサーバが起動します。
http://localhost:3001/
ディレクトリ構成
ディレクトリ構成は、以下のようになっています。
通常のLWCが Salesforce 前提ですが、OSS 版は Salesforce は無いので、サーバはほぼ自作する必要があります。
- プロジェクトルート
- scripts : 開発サーバ設定が記載されています(基本的に触りません)
- src : ソースディレクトリルート
- client : LWC のソース本体です
- modules : LWC モジュール/コンポーネントを配置します
- module1 : モジュール名を指定します。Salesforce 版 LWC で言う所の「
c-
」部分です。 - module2
- ...
- module1 : モジュール名を指定します。Salesforce 版 LWC で言う所の「
- resources
- index.html : 一番最初に表示される HTML ファイルです。基本的に
<div id="main"></div>
位しか書いていません。 - index.js : 一番最初の画面が表示された祭に実行される JavaScript です。LWC アプリを起動する役割があります。
- modules : LWC モジュール/コンポーネントを配置します
- server : バックエンドサーバソースです。
express
で作成することになります。
- client : LWC のソース本体です
LWC アプリ開発の違い
- メタデータが存在しない
Salesforce 版だと、LWCをどこに使うか等を指定するメタデータがありますが、OSS 版はどこかに埋め込む運用ではないので、そんなものはありません。
制御は全部 JavaScript/JSON で記述します。 - バックエンドが存在しない
Salesforce ではないので、バックエンドは存在しません。
代わりに、ExpressJS がセットアップされるので、これをつかって API サーバを作成し、フロントの LWC 側から Ajax 通信で処理します。※ Tips に関連 - JavaScript への制限がない
Salesforce 版だと、JavaScript の記述には一部制限が入ります。(おそらく好き勝手されないように)
しかし、OSS 版はそうした制限はないので、adapter
やプロトタイプ汚染等、そこそこ凶悪な操作がやりたい放題です。 - モジュール名が自由に命名できる
Salesforce 版だと、カスタムコンポーネントはc-
プレフィックス固定で、コンポーネント名も小文字のハイフン区切り(例 :<c-example-component />
)すが、 OSS 版はその制限がないので<bootstrap-customAlert />
といったそれなりに自由なコンポーネント名で定義できます。 - 共通スタイルは無い
Salesforce 版だと、Lightning Design System
のスタイルが自動適用されますが、OSS 版はそれはありません。
自力で適用する必要があります。※ Tips に関連
Tips
共通スタイルシートを適用する
デフォルトでは、何のスタイルも自動適用されません。
そこで、共通のスタイルを適用するため、次のような手段を用います。
src/styles/common.css
を作成する。src/client/modules/common/BaseElement
を作成する。(※1)- コンポーネント作成時は、この
BaseElement
を継承する。(※2)
※1
import { LightningElement } from 'lwc'; export default class CssCommonElement extends LightningElement { _commonCss() { let _bootstrap = '../styles/common.css'; const styles = document.createElement('link'); styles.href = _bootstrap; styles.rel = 'stylesheet'; return styles; } connectedCallback() { this.template.appendChild(this._commonCss()); } }
※2
import BaseElement from 'common/basBaseElemente'; export default class AddBrand extends BaseElement { /* ... */ }
バックエンドサーバ(Express)で REST API を作成する
デフォルトでは、Express JS サーバは REST API を作成する形ではセットアップされない。
そこで、REST API モードとするために設定を行う
これは src/server/api.js
で次のように記述する。
const express = require('express'); const app = express(); app.use(express.json());
NodeJs で SQLite3 をセットアップ
デフォルトでは、データベースすらセットアップされていない。
そこで、データベースを使えるようにセットアップを行う。
npm install sqlite3 -i
で sqlite3 をインストールする- 後述のような共通 DB アクセスライブラリ(
DBCommon
)を作成する ※1 - データベースアクセスクラス(DAO)を作成していく ※2
こんな感じにしておけば色々使いやすいかも知れない。
※1
const sqlite3 = require("sqlite3") let database class DBCommon { static init() { database = new sqlite3.Database("data.sqlite3") } static get() { return database } } DBCommon.init(); exports.DBCommon = DBCommon;
※2
const common = require("./DBCommon"); const sqlite3 = require("sqlite3") function createTable() { const db = common.DBCommon.get(); return new Promise((resolve, reject) => { try { db.serialize(() => { db.run( `create table if not exists Brand ( id integer primary key AUTOINCREMENT, name varchar(512) not null, account varchar(32) not null, description text default null )` ) }); return resolve(); } catch (error) { return reject(error); } }); } createTable(); class BrandTable { static async select(id, db = common.DBCommon.get()) { return new Promise((resolve, reject) => { db.get(`select id, name, account, description from Brand where id = ?`, id, (err, row) => { if (row) { resolve(row); } else { reject(err); } }); }); } static async insert(brand, db = common.DBCommon.get()) { db.run( `insert into Brand (name, account, description) values ($name, $account, $description)`, { $name: brand.name, $account: brand.account, $description: brand.description } ); } } exports.BrandTable = BrandTable;
通信用アダプタ
こんな通信用クラスを用意しておくと便利です。
function jsonSending(method, url, sendBody) { return new Promise((resolve, reject) => { let req = new XMLHttpRequest(); req.open(method, url); req.onload = function() { if (req.status === 200) { resolve(JSON.parse(req.responseText)); } else { window.console.log(req); reject(new Error(req.statusText)); } }; req.onerror = function() { window.console.log(req); reject(new Error(req.statusText)); }; req.setRequestHeader('Content-Type', 'application/json'); req.send(JSON.stringify(sendBody)); }); } export default class Ajax { /** * HTTP Get request * @param {String} url * @returns Promise(String) */ static async get(url) { return new Promise((resolve, reject) => { let req = new XMLHttpRequest(); req.open('GET', url, true); req.onload = function() { if (req.status === 200) { resolve(JSON.parse(req.responseText)); } else { reject(new Error(req.statusText)); } }; req.onerror = function() { reject(new Error(req.statusText)); }; req.send(); }); } /** * HTTP Post request. * @param {String} url * @param {String} sendBody * @returns Promise(String) */ static async post(url, sendBody) { return jsonSending('POST', url, sendBody); } /** * HTTP PUT request. * @param {String} url * @param {String} sendBody * @returns Promise(String) */ static async put(url, sendBody) { return jsonSending('PUT', url, sendBody); } }
参考情報
-ExpressJs : https://expressjs.com/ja/ 軽量な Javascript Web フレームワーク - LWC : https://lwc.dev/ - テンプレートエンジン : https://developer.salesforce.com/docs/platform/lwr/guide/lwr-templates.html