クラフトCMSの複数のページのサイトでCreate Reactionアプリを使用する方法


TLDr :カスタマイズCMSは、CMSによって供給された複数のページのサイトのフロントエンド財団としてそれを使用するアプリケーションを作成します.
すでに使用方法をカバーする記事がたくさんありますCraft CMS 反応するが、それらは主にGraphSQLの使用に集中します.このポストでは、我々は偉大な使用方法を参照してくださいCreate React App テンプレートは、クラフトCMSと一緒に、両方の世界のベストを得るために:コミュニティ最適化された反応アプリ、バトルテストフロントエンドツール、ページごとに異なるバンドルを持つ複数のエントリポイントをテストした.
我々はすべてのプロセスを通して一緒に行くだろう、私たちの前にかなりの旅があるので、始めましょう.

I recommend to have at least basic knowledges about Craft CMS, PHP, CRA, and a good comprehension of Webpack

  • Installing CRA with a template
  • Installing Craft CMS
  • Customize CRA using craco
  • Change dev and build paths
  • Add support for multiple entry points
  • Add Tailwind and tweak Postcss
  • Add another plugin
  • Injecting the js and css files associated with each template when necessary
  • CRAをテンプレートでインストールする

    Since v3.3 react-scripts support templates , 我々は素晴らしいを使用しますreact boilerplate template 特にv5-with-cra branch これは、typescriptサポートを追加しますが、必須ではありませんany templates , またはあなたのニーズに応じてすべてのテンプレートは、とにかく継続することができます:

    For what follows our project will be named cracraft, and CRA refer to Create react app


    $ npx create-react-app --template cra-template-rb cracraft
    
    修正されたテンプレートのエラー
    このエラーに遭遇した場合、
    A template was not provided. This is likely because you're using an outdated version of create-react-app.
    Please note that global installs of create-react-app are no longer supported.
    
    まず、すべてのグローバルインストールされたCRAのバージョンを削除します.
  • with npm uninstall -g create-react-app
  • or yarn global remove create-react-app
  • それから、糸があなたにこれを与えるならばerror This module isn't specified in a package.json file. 彼を盲目に信じないでください.
    $ readlink which `create-react-app`
    
    そして、あなたがパスを得るならば、それはあなたのシステムに残るCRAパッケージのもう一つのバージョンです;だから、それを削除してみてください.
    インストールされたら、ディレクトリにCDを実行するnpm start すべてがスムーズに実行していることを確認します.

    CMSのインストール

    Installing Craft CMS is quite simple, thanks to composer , しかし、小さな問題があります.非空のディレクトリにインストールすることはできません.
  • 最初の一時的なディレクトリにクラフトをインストールcomposer create-project craftcms/craft cracraft-temp
  • そして、一旦終了したら、以前にCRAをインストールしたディレクトリ内のすべてのファイルを移動し、一時的に削除しますcracraft-temp ディレクトリ
  • ここでは、インストールプロセスの残りの部分に従ってください.https://craftcms.com/docs/3.x/installation.html#step-2-set-the-file-permissions
    このセットアップの目的は、TwigテンプレートでCRAを統合するだけでなく、1ページのアプリケーションのために仕事をするでしょう、しかし、いくつかのページが本当に複雑なウィジェットまたは複雑なアプリケーションを含むことができた複数のページでウェブサイトのために、そして、他のものはちょうどJavaScriptのいくつかの線またはJavaScriptなしを必要とします.もっと柔軟性が必要だ.
    しかし、CRAとして設計されているとしても、それは本当に柔軟性がありません、それは意味があります、それはスパであるために作られたので、1つのHTMLページがあります、そして、あなたは全部の束または何も注入しません.この時点で、我々は現在、片面では、CMSによって供給された複数のページのサイトを持っている一方、CRAによって供給されたスパで、我々は2つの融合する必要があります.

    CRACOを使用したカスタマイズCRA

    Let’s customize CRA to play well with Craft CMS, the goal is to tweak the Webpack configuration without ejecting, and thus keep the advantages of being able to update either CRA or the template.
    There are a couple of options for customization:

  • Create React App Configuration Override
  • Rescripts
  • React app rewired
  • フォークリプトスクリプト
  • クラコは私の好みを持っています.なぜなら、異なるパーツの調整を処理する方法が好きなので、WebPackの設定を公開して欲しい.プロジェクトに追加します.
    $ npm install @craco/craco --save
    
    次に、ルートディレクトリ、すべての修正を含むファイルを作成しますcraco.config.js .
    そして、最終的にスタートを更新して、反応スクリプトの代わりにcracoを使用するスクリプトコマンドを構築してください.
    インパッケージ.JSON
    "scripts": {
      "start": "craco start",
      "build": "craco build",
      "test": "craco test",
      ...
    },
    

    If you want to know more about the full details of the craco installation, it’s here


    それは、いくつかのコードを書く時間です.
    ここでは、
  • いくつかのdevを変更し、我々のクラフトフォルダの構造に合わせてパスを構築する
  • 複数のエントリポイントを設定する別のページに別のバンドルを注入できるようにする
  • Webpackプラグインの追加と変更
  • 風船と微調整ポストを加える
  • 必要に応じて各ページに関連するjsファイルとcssファイルを取得する
  • 我々はCRAとクラフトCMSの間で変数を共有するでしょう.env ファイル
    WDS_SOCKET_HOST=localhost
    WDS_SOCKET_PORT=3000
    PUBLIC_PATH="http://localhost:3000/"
    MANIFEST_PATH=/asset-manifest.json
    FAST_REFRESH=true
    

    FAST_REFRESH enable react-refresh, it’s not mandatory to enable it, but it’s allow to improve components updates


    変更とビルドパス

    Assuming that the src directory contains all the javascript and styles sources, and that we want to output the result of the build step in web/dist :

    cracraft
    ├── src
    │   ├── styles
    │   │   ├── **/*.css
    │   ├── app
    │   │   ├── **/*.tsx
    │   ├── js
    │   │   ├── **/*.js
    │   └── ...
    ├── web
    │   ├── dist
    │   └── ...
    ├── templates
    ├── craco.config.js
    ├── .env
    └── ...
    

    We have to tell Webpack where our files are and where we want the output, both for dev and build mode:

    In craco.config.js

    const {
      whenDev,
      whenProd,
    } = require('@craco/craco');
    
    module.exports = function ({ env }) {
      return {
        webpack: {
          configure: (webpackConfig, { env, paths }) => {
            whenDev(() => {
              webpackConfig.output.publicPath = process.env.PUBLIC_PATH;
            });
    
            whenProd(() => {
              const buildPath = `${paths.appPath}/web${process.env.PUBLIC_PATH}`;
    
              paths.appBuild = buildPath;
              webpackConfig.output.path = buildPath;
              webpackConfig.output.publicPath = process.env.PUBLIC_PATH;
    
              return webpackConfig;
            });
          }
        },
        plugins: [],
        style: {}
      }
    }
    

    Using a local domain like .local

    To avoid CORS errors between your local domain and the dev server, add a header to the webpack dev server, using craco’s ability to change the configuration of the dev server.

    In craco.config.js

    ...
    plugins: [
      {
        plugin: {
          overrideDevServerConfig: ({
            devServerConfig,
            cracoConfig,
            pluginOptions,
            context: { env, paths, proxy, allowedHost },
          }) => {
            devServerConfig.headers = {
              'Access-Control-Allow-Origin': '*',
            };
            return devServerConfig;
          },
        }
      },
    ],
    ...
    

    複数のエントリポイントのサポートを追加する

    CRA doesn’t support multiple entry points out of the box, so we have to reconfigure Webpack to add some. Let’s say we have 3 different pages:

    • home where we want to use Typescript
    • editor a page containing the react SPA
    • about a page that need only a simple javascript snippet

    NB: Dont forget to create these pages and their associated Twig templates

    In craco.config.js add our 3 entry points

    ...
    module.exports = function ({ env }) {
      return {
        webpack: {
          configure: (webpackConfig, { env, paths }) => {
            const entries = {
              index: [...webpackConfig.entry],
              home: [`${paths.appSrc}/js/home.ts`],
              about: [`${paths.appSrc}/js/about/index.js`],
            };
            ...
          }
        }
      }
    }
    

    Note: We keep index as name for the entry point of the react SPA

    It won’t work yet, because the ManifestPlugin already used in CRA will cause a problem, it is configured 単一のエントリポイントをサポートします.そして、Webpackプラグインの設定を上書きするためには、それを置き換える必要があります.
    プラグインをインストールします
    $ npm i ManifestPlugin -D
    
    プラグインの新しいインスタンスを作成し、プラグイン配列の既存のものを置き換えます.
    クラコで.設定.js
    ...
    module.exports = function ({ env }) {
      return {
        webpack: {
          configure: (webpackConfig, { env, paths }) => {
            ...
            // Substitute ManifestPlugin:
            const pluginPosition = webpackConfig.plugins.findIndex(
              ({ constructor }) => constructor.name === 'ManifestPlugin',
            );
    
            const multipleEntriesManifestPlugin = new ManifestPlugin({
              fileName: 'asset-manifest.json',
              publicPath: paths.publicUrlOrPath,
              generate: (seed, files, entrypoints) => {
                const manifestFiles = files.reduce((manifest, file) => {
                  manifest[file.name] = file.path;
                  return manifest;
                }, seed);
    
                // Keep the existing entry point
                const indexEntrypointFiles = entrypoints.index.filter(
                  fileName => !fileName.endsWith('.map'),
                );
    
                let { index, ...pagesAllEntryPointFiles } = entrypoints;
                // Create our pages entry points
                const pagesEntryPointFiles = Object.keys(
                  pagesAllEntryPointFiles,
                ).reduce((filtered, entryKey) => {
                  filtered[entryKey] = pagesAllEntryPointFiles[entryKey].filter(
                    fileName => !fileName.endsWith('.map'),
                  );
                  return filtered;
                }, {});
    
                return {
                  files: manifestFiles,
                  entrypoints: indexEntrypointFiles,
                  pages: pagesEntryPointFiles,
                };
              },
            });
    
            webpackConfig.plugins.splice(
              pluginPosition,
              1,
              multipleEntriesManifestPlugin,
            );
            ...
          }
        }
      }
    }
    
    そして、ホップ!それは完了です、我々はちょうどプラグインを置き換えました.
    我々はほとんど完了している、私たちの新しいエントリポイントをサポートするために残っている1つのステップがあります、私たちはwebpackHotDevClient それぞれがサポートするHMR
    クラコで.設定.js
    ...
    whenDev(() => {
      webpackConfig.output.publicPath = process.env.PUBLIC_PATH;
      webpackConfig.optimization.runtimeChunk = 'single';
    
      const webpackHotDevClientPath = require.resolve(
        'react-dev-utils/webpackHotDevClient',
      );
      Object.keys(entries).forEach(entryKey => {
        if (!entries[entryKey].includes(webpackHotDevClientPath)) {
          entries[entryKey].unshift(webpackHotDevClientPath);
        }
      });
    });
    ...
    
    簡単なヒント:カスタマイズ
    CRAをカスタマイズし、トリッキーなバグに遭遇する場合は、まだ任意のNODEJSアプリのようなプロセスをデバッグすることができますを覚えて--inspect NPMスクリプトコマンドへのフラグcraco --inspect build

    風船と微調整ポストを加える

    Once everything is in place and the dev server plus the build step are running without any errors, we can customize further to integrate all our needs, for the demonstration we’ll add a favicon plugin, customize Postcss and use Tailwind CSSフレームワーク.
    まず最初に、TarwindとPostCSSはとても簡単です.必要なパッケージを追加することから始めます.
    $ npm i -D postcss-import postcss-preset-env tailwindcss
    
    プロジェクトのルートディレクトリでtailwind.config.js ファイル.
    クラコで.設定.PostScriptの設定を追加する
    ...
    style: {
      postcss: {
        plugins: [
          require('postcss-import')({
            plugins: [require('stylelint')],
            path: ['./node_modules'],
          }),
          require('tailwindcss')('./tailwind.config.js'),
          require('postcss-preset-env')({
            autoprefixer: {},
            features: {
              'nesting-rules': true,
            },
          })
        ],
      },
    },
    ...
    
    そして、それを完璧にするために、我々はTrewindで使用されるいくつかの正統的な規則でチルアウトするStyleLintを指示する必要があります.
    これらの規則を.stylelintrc 設定ファイル:
    "rules": {
        "at-rule-no-unknown": [ true, {
          "ignoreAtRules": [
            "screen",
            "extends",
            "responsive",
            "tailwind"
          ]
        }],
        "block-no-empty": null
      }
    

    別のプラグインを追加

    Next, add the Favicons Webpack Plugin, here it’s even simplier because we just have to push it in the Webpack config plugin array, provided by craco, like this:

    whenProd(() => {
      ...
    
      webpackConfig.plugins.push(
        new FaviconsWebpackPlugin({
          logo: './src/img/favicon-src.png',
          prefix: 'img/favicons/',
          cache: true,
          inject: 'force',
          favicons: {
            appName: 'Cracraft',
            appDescription: 'Create react app and Craft play well together',
            developerName: 'Dev name',
            developerURL: '[email protected]',
            path: 'web/dist/',
          },
        }),
      );
    });
    

    Note that when you add a plugin which is already used in the CRA config you have to replace it, using the same trick as the one used previously for the ManifestPlugin.

    必要に応じて各テンプレートに関連するJSファイルとCSSファイルを注入する

    Ooook now that CRA is customized, there is one last step to link it to Craft CMS: we need to retrieve the content of the different endpoints, and, since the manifest file is a plain json file, it’s easy to read it and get the parts we need.

    How are we going to do that?

    • Quick answer: this can be done with a Twig function
    • Long answer: there is a better way to do it, but we’ll talk about it in another post, as this one is starting to be quite long (congrats if you're still reading from the beginning).

    So, lets write a simple Twig function that will load our manifest file and create the HTML tags.

    First install a PHP implementation of JsonPath
    $ composer require galbar/jsonpath
    
    Twig拡張子を宣言するファイルで、すべての依存関係をインポートします.
    use craft\helpers\Html;
    use craft\helpers\Template;
    use craft\helpers\Json as JsonHelper;
    use JsonPath\JsonObject;
    
    そして、マニフェストファイルの内容を取得し、探しているチャンクのパスを返す関数を追加しますそれをgetentrypointchunksと呼びましょう、そして、それは$jsonPath Param、コードを通してください.
    public function getEntryPointChunks(string $path)
    {
        $publicPath = getenv('PUBLIC_PATH');
        $manifestPath = getenv('MANIFEST_PATH');
        $manifestContent = file_get_contents($publicPath.$manifestPath);
        $manifest = JsonHelper::decodeIfJson($manifestContent);
        $jsonObject = new JsonObject($manifestContent);
    
        $moduleList = $jsonObject->get($jsonPath);
    
        if (!$moduleList) {
            return null;
        }
    
        // Ensure flat array, ex: if [*] is forgotten in the json path to an array
        if (is_array($moduleList)) {
            $flattened = [];
            array_walk_recursive($moduleList, function ($item) use (&$flattened) {
                $flattened[] = $item;
            });
    
            $moduleList = $flattened;
        }
    
        $moduleTags = [];
        foreach ($moduleList as $k => $m) {
            if (strpos($m, '.hot-update.js') === false) {
                $moduleName = preg_replace('/^\//', '', $m);
                if (preg_match('/\.css(\?.*)?$/i', $moduleName)) {
                    $moduleTags[] = Html::cssFile("$publicPath/$moduleName");
                } elseif (preg_match('/\.js(\?.*)?$/i', $moduleName)) {
                    $moduleTags[] = Html::jsFile("$publicPath/$moduleName");
                } elseif (preg_match('/\.(svg|png|jpe?g|webp|avif|gif)(\?.*)?$/i', $moduleName)) {
                    $moduleTags[] = Html::img("$publicPath/$moduleName");
                } else {
                    $moduleTags[] = "$publicPath/$moduleName";
                }
            }
        }
    
        return Template::raw(implode("\r\n", $moduleTags));
    }
    
    そして最後に、任意のtwigテンプレートから関数を呼び出します.
    {# Inject the spa react app #}
    {{getEntryPointChunks('$.entrypoints')}}
    
    と別のページ
    {# Inject vanilla javascript #}
    {{getEntryPointChunks('$.pages.about')}}
    

    NB: don’t forget to start the development server before accessing your Craft site $ npm start


    そして、それはこれです、これは終わりです、我々はマルチエントリーポイントセットアップとカスタマイズ可能なWebpack構成でCRAとCraft CMSの間で(ほぼ)完全な融合を持ちます.

    次回は、このセットアップをクラフトCMSですべてを統合するより良い方法で完了する予定です.なぜなら、Twig機能は仕事をしていますが、改善の余地があるからです.
    読書のおかげで、何か新しいことを学び、それはあなたを助ける願っています.