webpack 如何使用react从构建两个不同的版本

z9ju0rcb  于 2023-02-16  发布在  Webpack
关注(0)|答案(2)|浏览(200)

我正在实现一个管理面板,不想把面板的前端代码暴露给客户端。我认为最好的方法是配置npm run build来创建两个构建版本-一个客户端构建版本和一个管理构建版本。然后后端将根据身份验证控制返回哪个构建版本。
Possible duplicate with an answer,但实际上并没有解释如果你不熟悉构建过程是如何工作的,你将如何做到这一点。此外,webpack Entry Points看起来确实像是将在这里应用的东西,但作为一个不太熟悉webpack的人,有限的非初学者友好的文档有点超出我的理解范围。

    • 关于我的设置的一些信息:**我有和ReactJS/NodeJS SPA。前端和后端是按照monorepo原则配置的,它们都共享node_modulespackage.json.env等。为此,我使用react-app-rewired来更改npm run buildnpm run start命令的路径,而不需要弄乱webpack。

下面是我的文件结构:

back-end/
    ...
front-end/
    public/
    src/
        admin/ <- Would prefer the admin panel front-end to be here if possible
        ...
build/
    ...
build_admin/ <- This is what I want
    ...
node_modules/
    ...
.env
.gitignore
config-overrides.js
package.json
...

"scripts"来自package.json

"scripts": {
    "start": "node ./back-end/server.js",
    "build": "react-app-rewired build",
    "front-end": "set HTTPS=true&&set SSL_CRT_FILE=...&&set SSL_KEY_FILE=...&&react-app-rewired start",
    "back-end": "nodemon ./back-end/server.js",
    "test": "react-app-rewired test",
    "eject": "react-scripts eject"
  },

因此,如果我的方法是可行的-我如何设置npm run build,使两个构建从选择 * src/文件?

    • 选择的意思是,对于客户端构建版本,忽略admin/源文件;对于管理员构建版本,仅使用admin/文件构建。*

在替代解决方案之前需要注意的其他几点:

  • 我想让管理面板在React作为一个SPA,所以节点视图引擎是不是一个选项。
  • 我不想为了运行一个基本的管理面板而启动一个完全独立的应用程序,更不用说处理两个独立应用程序之间共享数据的头痛问题,从而浪费资源。
  • 首先,我避免显示管理面板前端代码的原因不是因为会有硬编码的敏感数据,而是因为您可以根据UI推断出相当多的信息(输入字段、描述、按钮名称、图形等)。
r9f1avp5

r9f1avp51#

到目前为止,还没有回答你最初的问题,如何使两个不同的建设。
一种使人们更难查看管理页面源代码而使您更容易部署的方法是使用Webpack的代码拆分功能。
React提供了一个简单的方法来将你的应用拆分成包,使用React.lazy方法。
对于Webpack代码拆分,您需要版本4及以上
我使用了react 18.2.0,网络包5.75.0
我用npx create-react-app启动了这个应用程序,以提高速度。
我认为react-app-rewired保持默认的cra配置,你应该很好。也许你需要更新一些webpack配置,会看到
如果你正在使用某种路由,你可以从这段代码中得到启发。

应用程序.js文件

import { BrowserRouter, Routes, Route, Link,Navigate } from 'react-router-dom';
import { ProtectedRoute } from './ProtectedRoute';
import * as React from 'react'

// The React.lazy() will normally handle everything for you
const Admin = React.lazy(() => import('./AdminPage'))

function App() {
  return (
    <BrowserRouter>
      <h1>Lazy Router</h1>

      <nav>
        <Link to="/">Home</Link>
        <Link to="/admin">Admin page</Link>
      </nav>
      <Routes>
        <Route path='/' element={<HomePage/>} />
        <Route path='/admin' element={
          <ProtectedRoute
          element={<Admin/>}
          fallback={<Navigate to="/" replace/>}
            />
        }
        />

      </Routes>
    </BrowserRouter>
  );
}

const HomePage = () => {
  return (
    <div>
      Home page
    </div>
  )
}

export default App;

所以你可以把你想要保护的组件,管理面板页面传递给ProtectedRoute组件,它的目的是调用你的API来检查它是否有权限访问管理面板。
我定义了两个 prop ,一个元素,任何你想要的组件,这里是用React Lazy Package 的AdminPanel组件,还有一个后备,如果授权失败,重定向到主页。

受保护的路由.js

import { useEffect, useState } from 'react';
import * as React from 'react'
const ProtectedRoute = (props) => {

    /* You could write your own hook that will performed the authorization call to your API. 
        The hook could returns load and authorized states
        But for simplicity
    */
    // Auhtorization state which reflects your api auth
    const [authorized, setAuthorized] = useState(false)
    // Load state, just to wait until isAuth function returns
    const [load, setLoad] = useState(false)

    // An inelegant function to mimic authorization call to the server
    const isAuth = () => {
        setTimeout(() => {
            setAuthorized(true)
            setLoad(true)
        }, 2000)
    }

    // Call your API when the ProtectedRoute component is mounted
    useEffect(() => {
        isAuth()
    },[])
    // Loader until the authorization complete and eventually your AdminPanel is loaded
    return (
        <React.Suspense fallback={<>...loading</>}>
            {load ?
                authorized ?
                    props.element
                    : props.fallback
                : <>...loading</>}
        </React.Suspense>
    )

}
export { ProtectedRoute }

以及管理页面.js

const AdminPage = () => {
  return (
    <div>
      Admin Page
    </div>
  )
}
export default AdminPage

您将看到adminPanel的块仅在您请求时才下载,即授权成功时。
注意,如果包无法用React.lazy加载,您的react应用程序将完全崩溃,您必须用<MyErrorBoundary></MyErrorBoundary>组件保护它,这在react文档中有解释
这个方法是100%防弹,有些人可以黑客有点React状态?我不会感到惊讶,如果是这样的话。但它仍然使人们难以访问您的管理面板的源代码。
此外,如果您的React Bundle是由您控制的服务器分发的,那么当客户端请求管理页面的Bundle时,您可以检查它的权限。
默认情况下,bundle是用野蛮人的名字命名的,很难过滤,但是我认为你可以根据webpack documentation,在output:{filename:etc..}部分给它们指定有问题的模块的名字。当客户端请求adminpanel.bundle.js文件时,这会更容易过滤和控制。
将编辑我的回应,如果你需要一些精度和发挥后,有点与webpack,但现在我很懒

rkue9o1l

rkue9o1l2#

你一定要使用webpack entry points.config并不困难,我认为它可以通过react-app-rewired来解决(参见扩展配置选项一章)。
为了避免负担过重,我们的想法是使用路径作为入口点名称,使用[name] joker作为输出名称。在您的情况下,您可以尝试从下面的基本配置开始,该配置可能适用于正确解析真实的的入口路径:

entry: {
   './build/index': './front-end/src/index.js',
   './build_admin/index': './front-end/src/admin/index.js'
},
output: {
    path: './',
    filename: '[name].js'
}

它将从第一个入口点在./build/index.js创建一个bundle,从第二个入口点在./build_admin/index.js创建一个bundle。您还可以提供更多详细信息以处理常见依赖项等。
./可能无法解析到你的项目根文件夹,但是你可以根据需要调整它,例如,使用../作为起始文件夹。
如果需要,您可能需要path来正确解析路径。
更多示例请参见此主题:How to set multiple file entry and output in project with webpack?

相关问题