如何在 index.html 以外的任何其他页面上呈现 React 组件

IT技术 reactjs react-component
2021-04-25 11:11:47

我创建了一个基本的react应用程序

npx create-react-app

我可以index.jsindex.html.

但是,这是我也可以呈现它们的唯一页面,例如,我不能在login.html页面或除其他页面之外的任何其他页面上使用相同的组件index.html

我正在用npm start.

3个回答

React 通常用于单页面应用程序,因此您只需在单个页面中渲染应用程序,然后使用路由(react-router通常)来模拟浏览器导航。

但是,您可以在多个页面上呈现它……您只需要在其他页面上复制您的激活 JavaScript

只需将其复制到您的login.html(取自默认的 create-react-app 输出):

<noscript>
  You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<script type="text/javascript" src="/static/js/bundle.js"></script>

你为什么需要这个?请记住,就其自身的性质而言,React 声称自己是 SPA(单页应用程序)。这应该足以让您了解单个页面是(应该是)index.html。

无论如何,您可以通过查看由 create-react-app 创建的默认 index.html 文件以及 manifest.json 文件来实现。

从 manifest.json 开始,你应该有这个 JSON:

{
  "short_name": "React App",
  "name": "Create React App Sample",
  "icons": [
    {
      "src": "favicon.ico",
      "sizes": "64x64 32x32 24x24 16x16",
      "type": "image/x-icon"
    }
  ],
  "start_url": "./index.html",
  "display": "standalone",
  "theme_color": "#000000",
  "background_color": "#ffffff"
}

或类似的东西。您可以设置一个“start_url”属性,它是 create-react-app webpack dev-server 的起点,默认情况下,当您在运行它的本地地址(通常为 localhost:3000)请求任何路径时,它会为您服务)。通过更改此属性,您可以使其服务于您想要的页面,例如 login.html。

现在,看看 index.html 默认代码,你应该有这样的东西:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    <title>React App</title>
  </head>
  <body>
    <noscript>
      You need to enable JavaScript to run this app.
    </noscript>
    <div id="root"></div>
  </body>
</html>

如您所见,在页面的末尾有一个

<div id="root"></div>

这正是 React 渲染 ReactDOM.render(jsx, ElementID) jsx 的地方。事实上,如果你查看 index.js,ReactDOM 是这样的:

ReactDOM.render(
    <Something />,
, document.getElementByID('root'));

可以看到ElementID('root')就是你要渲染jsx()的div元素的id。如果你创建一个新页面,比如 login.html,复制粘贴 index.html 内容并更改元素 ID,然后你可以选择在那里呈现你的内容,同时更改 ReactDOM.render() 的引用 ElementID。处理这个你应该得到你想要的结果,但如前所述,在我看来你不应该需要这个。

要使用 以外的文件index.html,您需要在 2 个文件中的四个位置进行更改。

这些文件是project_directory\public\manifest.jsonproject_directory\node_modules\react-scripts\config\paths.js

  1. project_directory\public\manifest.json
  • 变更前
{
  "short_name": "React App",
  "name": "Create React App Sample",
  "icons": [
    {
      "src": "favicon.ico",
      "sizes": "64x64 32x32 24x24 16x16",
      "type": "image/x-icon"
    },
    {
      "src": "logo192.png",
      "type": "image/png",
      "sizes": "192x192"
    },
    {
      "src": "logo512.png",
      "type": "image/png",
      "sizes": "512x512"
    }
  ],
  "start_url": ".",
  "display": "standalone",
  "theme_color": "#000000",
  "background_color": "#ffffff"
}

  • 更改后
{
  "short_name": "React App",
  "name": "Create React App Sample",
  "icons": [
    {
      "src": "favicon.ico",
      "sizes": "64x64 32x32 24x24 16x16",
      "type": "image/x-icon"
    },
    {
      "src": "logo192.png",
      "type": "image/png",
      "sizes": "192x192"
    },
    {
      "src": "logo512.png",
      "type": "image/png",
      "sizes": "512x512"
    }
  ],
  "start_url": "./yourNewHtmlFile.html",  // change only this line
  "display": "standalone",
  "theme_color": "#000000",
  "background_color": "#ffffff"
}
  1. project_directory\node_modules\react-scripts\config\paths.js
  • 变更前

      // @remove-on-eject-begin
      /**
       * Copyright (c) 2015-present, Facebook, Inc.
       *
       * This source code is licensed under the MIT license found in the
       * LICENSE file in the root directory of this source tree.
       */
      // @remove-on-eject-end
      'use strict';
    
      const path = require('path');
      const fs = require('fs');
      const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath');
    
      // Make sure any symlinks in the project folder are resolved:
      // https://github.com/facebook/create-react-app/issues/637
      const appDirectory = fs.realpathSync(process.cwd());
      const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
    
      // We use `PUBLIC_URL` environment variable or "homepage" field to infer
      // "public path" at which the app is served.
      // webpack needs to know it to put the right <script> hrefs into HTML even in
      // single-page apps that may serve index.html for nested URLs like /todos/42.
      // We can't use a relative path in HTML because we don't want to load something
      // like /todos/42/static/js/bundle.7289d.js. We have to know the root.
      const publicUrlOrPath = getPublicUrlOrPath(
      process.env.NODE_ENV === 'development',
      require(resolveApp('package.json')).homepage,
      process.env.PUBLIC_URL
      );
    
      const buildPath = process.env.BUILD_PATH || 'build';
    
      const moduleFileExtensions = [
      'web.mjs',
      'mjs',
      'web.js',
      'js',
      'web.ts',
      'ts',
      'web.tsx',
      'tsx',
      'json',
      'web.jsx',
      'jsx',
      ];
    
      // Resolve file paths in the same order as webpack
      const resolveModule = (resolveFn, filePath) => {
      const extension = moduleFileExtensions.find(extension =>
        fs.existsSync(resolveFn(`${filePath}.${extension}`))
      );
    
      if (extension) {
        return resolveFn(`${filePath}.${extension}`);
      }
    
      return resolveFn(`${filePath}.js`);
      };
    
      // config after eject: we're in ./config/
      module.exports = {
      dotenv: resolveApp('.env'),
      appPath: resolveApp('.'),
      appBuild: resolveApp(buildPath),
      appPublic: resolveApp('public'),
      appHtml: resolveApp('public/index.html'), 
      appIndexJs: resolveModule(resolveApp, 'src/index'),
      appPackageJson: resolveApp('package.json'),
      appSrc: resolveApp('src'),
      appTsConfig: resolveApp('tsconfig.json'),
      appJsConfig: resolveApp('jsconfig.json'),
      yarnLockFile: resolveApp('yarn.lock'),
      testsSetup: resolveModule(resolveApp, 'src/setupTests'),
      proxySetup: resolveApp('src/setupProxy.js'),
      appNodeModules: resolveApp('node_modules'),
      swSrc: resolveModule(resolveApp, 'src/service-worker'),
      publicUrlOrPath,
      };
    
      // @remove-on-eject-begin
      const resolveOwn = relativePath => path.resolve(__dirname, '..', relativePath);
    
      // config before eject: we're in ./node_modules/react-scripts/config/
      module.exports = {
      dotenv: resolveApp('.env'),
      appPath: resolveApp('.'),
      appBuild: resolveApp(buildPath),
      appPublic: resolveApp('public'),
      appHtml: resolveApp('public/index.html'),
      appIndexJs: resolveModule(resolveApp, 'src/index'),
      appPackageJson: resolveApp('package.json'),
      appSrc: resolveApp('src'),
      appTsConfig: resolveApp('tsconfig.json'),
      appJsConfig: resolveApp('jsconfig.json'),
      yarnLockFile: resolveApp('yarn.lock'),
      testsSetup: resolveModule(resolveApp, 'src/setupTests'),
      proxySetup: resolveApp('src/setupProxy.js'),
      appNodeModules: resolveApp('node_modules'),
      swSrc: resolveModule(resolveApp, 'src/service-worker'),
      publicUrlOrPath,
      // These properties only exist before ejecting:
      ownPath: resolveOwn('.'),
      ownNodeModules: resolveOwn('node_modules'), // This is empty on npm 3
      appTypeDeclarations: resolveApp('src/react-app-env.d.ts'),
      ownTypeDeclarations: resolveOwn('lib/react-app.d.ts'),
      };
    
      const ownPackageJson = require('../package.json');
      const reactScriptsPath = resolveApp(`node_modules/${ownPackageJson.name}`);
      const reactScriptsLinked =
      fs.existsSync(reactScriptsPath) &&
      fs.lstatSync(reactScriptsPath).isSymbolicLink();
    
      // config before publish: we're in ./packages/react-scripts/config/
      if (
      !reactScriptsLinked &&
      __dirname.indexOf(path.join('packages', 'react-scripts', 'config')) !== -1
      ) {
      const templatePath = '../cra-template/template';
      module.exports = {
        dotenv: resolveOwn(`${templatePath}/.env`),
        appPath: resolveApp('.'),
        appBuild: resolveOwn(path.join('../..', buildPath)),
        appPublic: resolveOwn(`${templatePath}/public`),
        appHtml: resolveOwn(`${templatePath}/public/index.html`),
        appIndexJs: resolveModule(resolveOwn, `${templatePath}/src/index`),
        appPackageJson: resolveOwn('package.json'),
        appSrc: resolveOwn(`${templatePath}/src`),
        appTsConfig: resolveOwn(`${templatePath}/tsconfig.json`),
        appJsConfig: resolveOwn(`${templatePath}/jsconfig.json`),
        yarnLockFile: resolveOwn(`${templatePath}/yarn.lock`),
        testsSetup: resolveModule(resolveOwn, `${templatePath}/src/setupTests`),
        proxySetup: resolveOwn(`${templatePath}/src/setupProxy.js`),
        appNodeModules: resolveOwn('node_modules'),
        swSrc: resolveModule(resolveOwn, `${templatePath}/src/service-worker`),
        publicUrlOrPath,
        // These properties only exist before ejecting:
        ownPath: resolveOwn('.'),
        ownNodeModules: resolveOwn('node_modules'),
        appTypeDeclarations: resolveOwn(`${templatePath}/src/react-app-env.d.ts`),
        ownTypeDeclarations: resolveOwn('lib/react-app.d.ts'),
      };
      }
      // @remove-on-eject-end
    
      module.exports.moduleFileExtensions = moduleFileExtensions;
    
  • 更改后

      // @remove-on-eject-begin
      /**
       * Copyright (c) 2015-present, Facebook, Inc.
       *
       * This source code is licensed under the MIT license found in the
       * LICENSE file in the root directory of this source tree.
       */
      // @remove-on-eject-end
      'use strict';
    
      const path = require('path');
      const fs = require('fs');
      const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath');
    
      // Make sure any symlinks in the project folder are resolved:
      // https://github.com/facebook/create-react-app/issues/637
      const appDirectory = fs.realpathSync(process.cwd());
      const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
    
      // We use `PUBLIC_URL` environment variable or "homepage" field to infer
      // "public path" at which the app is served.
      // webpack needs to know it to put the right <script> hrefs into HTML even in
      // single-page apps that may serve index.html for nested URLs like /todos/42.
      // We can't use a relative path in HTML because we don't want to load something
      // like /todos/42/static/js/bundle.7289d.js. We have to know the root.
      const publicUrlOrPath = getPublicUrlOrPath(
      process.env.NODE_ENV === 'development',
      require(resolveApp('package.json')).homepage,
      process.env.PUBLIC_URL
      );
    
      const buildPath = process.env.BUILD_PATH || 'build';
    
      const moduleFileExtensions = [
      'web.mjs',
      'mjs',
      'web.js',
      'js',
      'web.ts',
      'ts',
      'web.tsx',
      'tsx',
      'json',
      'web.jsx',
      'jsx',
      ];
    
      // Resolve file paths in the same order as webpack
      const resolveModule = (resolveFn, filePath) => {
      const extension = moduleFileExtensions.find(extension =>
        fs.existsSync(resolveFn(`${filePath}.${extension}`))
      );
    
      if (extension) {
        return resolveFn(`${filePath}.${extension}`);
      }
    
      return resolveFn(`${filePath}.js`);
      };
    
      // config after eject: we're in ./config/
      module.exports = {
      dotenv: resolveApp('.env'),
      appPath: resolveApp('.'),
      appBuild: resolveApp(buildPath),
      appPublic: resolveApp('public'),
      appHtml: resolveApp('public/yourNewHtmlFile.html'),  // change this line
      appIndexJs: resolveModule(resolveApp, 'src/index'),
      appPackageJson: resolveApp('package.json'),
      appSrc: resolveApp('src'),
      appTsConfig: resolveApp('tsconfig.json'),
      appJsConfig: resolveApp('jsconfig.json'),
      yarnLockFile: resolveApp('yarn.lock'),
      testsSetup: resolveModule(resolveApp, 'src/setupTests'),
      proxySetup: resolveApp('src/setupProxy.js'),
      appNodeModules: resolveApp('node_modules'),
      swSrc: resolveModule(resolveApp, 'src/service-worker'),
      publicUrlOrPath,
      };
    
      // @remove-on-eject-begin
      const resolveOwn = relativePath => path.resolve(__dirname, '..', relativePath);
    
      // config before eject: we're in ./node_modules/react-scripts/config/
      module.exports = {
      dotenv: resolveApp('.env'),
      appPath: resolveApp('.'),
      appBuild: resolveApp(buildPath),
      appPublic: resolveApp('public'),
      appHtml: resolveApp('public/yourNewHtmlFile.html'),  // change this line
      appIndexJs: resolveModule(resolveApp, 'src/index'),
      appPackageJson: resolveApp('package.json'),
      appSrc: resolveApp('src'),
      appTsConfig: resolveApp('tsconfig.json'),
      appJsConfig: resolveApp('jsconfig.json'),
      yarnLockFile: resolveApp('yarn.lock'),
      testsSetup: resolveModule(resolveApp, 'src/setupTests'),
      proxySetup: resolveApp('src/setupProxy.js'),
      appNodeModules: resolveApp('node_modules'),
      swSrc: resolveModule(resolveApp, 'src/service-worker'),
      publicUrlOrPath,
      // These properties only exist before ejecting:
      ownPath: resolveOwn('.'),
      ownNodeModules: resolveOwn('node_modules'), // This is empty on npm 3
      appTypeDeclarations: resolveApp('src/react-app-env.d.ts'),
      ownTypeDeclarations: resolveOwn('lib/react-app.d.ts'),
      };
    
      const ownPackageJson = require('../package.json');
      const reactScriptsPath = resolveApp(`node_modules/${ownPackageJson.name}`);
      const reactScriptsLinked =
      fs.existsSync(reactScriptsPath) &&
      fs.lstatSync(reactScriptsPath).isSymbolicLink();
    
      // config before publish: we're in ./packages/react-scripts/config/
      if (
      !reactScriptsLinked &&
      __dirname.indexOf(path.join('packages', 'react-scripts', 'config')) !== -1
      ) {
      const templatePath = '../cra-template/template';
      module.exports = {
        dotenv: resolveOwn(`${templatePath}/.env`),
        appPath: resolveApp('.'),
        appBuild: resolveOwn(path.join('../..', buildPath)),
        appPublic: resolveOwn(`${templatePath}/public`),
        appHtml: resolveOwn(`${templatePath}/public/yourNewHtmlFile.html`),  // change this line
        appIndexJs: resolveModule(resolveOwn, `${templatePath}/src/index`),
        appPackageJson: resolveOwn('package.json'),
        appSrc: resolveOwn(`${templatePath}/src`),
        appTsConfig: resolveOwn(`${templatePath}/tsconfig.json`),
        appJsConfig: resolveOwn(`${templatePath}/jsconfig.json`),
        yarnLockFile: resolveOwn(`${templatePath}/yarn.lock`),
        testsSetup: resolveModule(resolveOwn, `${templatePath}/src/setupTests`),
        proxySetup: resolveOwn(`${templatePath}/src/setupProxy.js`),
        appNodeModules: resolveOwn('node_modules'),
        swSrc: resolveModule(resolveOwn, `${templatePath}/src/service-worker`),
        publicUrlOrPath,
        // These properties only exist before ejecting:
        ownPath: resolveOwn('.'),
        ownNodeModules: resolveOwn('node_modules'),
        appTypeDeclarations: resolveOwn(`${templatePath}/src/react-app-env.d.ts`),
        ownTypeDeclarations: resolveOwn('lib/react-app.d.ts'),
      };
      }
      // @remove-on-eject-end
    
      module.exports.moduleFileExtensions = moduleFileExtensions;
    

就是这样