UNPKG

utquidem

Version:

The meta-framework suite designed from scratch for frontend-focused modern web development.

360 lines (256 loc) 10.7 kB
--- title: 自定义入口和服务器端路由 sidebar_label: 自定义入口和服务器端路由 sidebar_position: 1 --- 使用 Modern.js 时,通常情况下是不需要自己指定入口的,框架会根据项目目录结构得到默认的入口。 在一些特殊情况下,例如目录结构有要求时,Modern.js 也提供了自定义入口的方式。 ## 默认入口机制 默认会遍历当前项目 `src/` 目录,根据目录结构来判断当前项目是单入口还是多入口。 :::tip 提示 - 可以通过 [`source.entriesDir`](/docs/apis/config/source/entries-dir) 更改入口目录 `src/` 为其他目录。 - 以下内容省略文件后缀,默认支持 js、jsx、ts、tsx。 ::: ### 入口特征 入口所在目录必须满足以下三个条件之一: 1. 具有 `App` 文件 2. 具有 `pages` 目录 3. 具有 `index` 文件 三种情况对应的目录结构示例如下: ```bash title="src/App.(j|t)sx?" ├── src │ ├── common │ └── App.(j|t)sx? ``` ```bash title="src/pages" ├── src │ ├── common │ └── pages // pages 目录 ``` ```bash title="src/index.(j|t)sx?" ├── src │ ├── common │ └── index.(j|t)sx? ``` 默认会遍历 `src/` 目录以及 `src/` 目录下的二级目录,如 `src/a/`。 :::tip 提示 Modern.js 内置了 "@" 别名,指向 `src` 目录。 ::: ### 单入口应用`src/` 目录满足**入口特征**时,Modern.js 会认为当前项目为单入口应用。 :::tip 提示 单入口应用默认入口名为 `main`。 ::: ### 多入口应用 当项目不是单入口应用时,Modern.js 会进一步查看 `src` 下的文件夹。 如 `src/a/`,当 `a` 目录符合单入口应用的特征时,会被当做一个入口,对应的入口名为当前目录名。 多个入口之间公用的代码,推荐放到 `src/common/` 目录下,这里就要求 `common` 目录不能满足入口的特征。 例如,目录结构如下时: ```bash ├── src │ ├── common │ │ ├── components │ │ ├── models │ │ └── utils │ ├── landing-page │ │ └── index.js │ ├── page-1 │ │ ├── components │ │ ├── models │ │ └── App.jsx │ └── page-2 │ ├── components │ ├── models │ └── pages // pages 目录 └── package.json ``` 会检测出 3 个入口:`landing-page``page-1``page-2`。 :::tip 提示 * 对于多入口应用,入口名和 `package.json` 文件中的 `name` 属性值相同的入口为主入口,访问时可以直接通过 `/` 访问。 * 如上述目录结构,假如 `package.json` 文件中 `name` 属性值为 `page-2`, 则同名入口就可以通过 `http://localhost:8080` 直接访问。 ::: ## App、Pages、Index 入口的区别 ### App 入口 在 MWA 项目中,推荐入口目录下只放置 `App` 文件,默认导出整个应用的根组件即可,如下: ```js title=src/App.jsx import React from 'react'; const App = () => { return (//...) }; export default App; ``` 当 Modern.js 检测到入口中存在 `App` 文件时,会自动生成真正的 Webpack 入口文件,完成将组件挂载到 DOM 节点等所有工作,大致代码逻辑如下: ```js import { createApp, bootstrap } from '@modern-js/runtime'; import React from 'react'; import App from 'xxxx/src/App'; const IS_BROWSER = typeof window !== 'undefined'; const MOUNT_ID = 'root'; let AppWrapper = null; const renderApp = () => { AppWrapper = createApp({})(App); bootstrap(AppWrapper, MOUNT_ID); }; renderApp(); export default AppWrapper; if (IS_BROWSER && module.hot) { module.hot.accept('xxxxx/src/App', () => { renderApp(); }); } ``` :::tip 提示 * Modern.js 生成的入口会统一放到 `node_modules/.modern-js/` 目录。对应的 Webpack 入口文件为 `node_module/.modern-js/${entryName}/index.js`。 * `createApp``bootstrap``@modern-js/runtime` API,详情请参考:【[Runtime API](/docs/apis/runtime/app/create-app)】。 ::: ### Pages 入口 Modern.js 也同样提供了约定式路由,即**文件系统即路由**。 约定式路由详情请参考: 【[使用约定式路由](/docs/guides/tutorials/c08-client-side-routing/8.2-file-based-routing)】。 ### Index 入口 通常情况下,上面两种路由已经能够满足需求,当开发者需要在 `bootstrap` 执行之前做些操作或者完全接管 Webpack 入口时,可以在入口目录下放置 `index` 文件。下面对两种情况分别介绍: 1. **bootstrap 之前添加自定义行为**。 当 `index` 文件默认导出**函数**时,Modern.js 还是会根据 `runtime.features` 的设置情况生成 `createApp` 包裹后的代码。在渲染过程中,将 `createApp` 包裹后的组件作为参数传递给 `index` 文件导出的函数,这样开发者可以自定义将组件挂载到 DOM 节点上。 例如: ```js title=src/index.jsx import { bootstrap } from '@modern-js/runtime'; export default App => { // do something before bootstrap... bootstrap(App, 'root'); }; ``` Modern.js 生成的文件内容如下: ```js import customRender from 'xxxx/src/index.js'; import { createApp, bootstrap } from '@modern-js/runtime'; import React from 'react'; import App from 'xxxx/src/App'; const IS_BROWSER = typeof window !== 'undefined'; const MOUNT_ID = 'root'; let AppWrapper = null; const renderApp = () => { AppWrapper = createApp({ // runtime 的插件参数... })(App); customRender(AppWrapper); }; renderApp(); export default AppWrapper; if (IS_BROWSER && module.hot) { module.hot.accept('xxx/src/App', () => { renderApp(); }); } ``` 2. **完全接管 Webpack 入口** 当 `index` 文件没有导出函数时,这时候 `index` 文件就是真正的 Webpack 入口文件,这里和 [Create React App](https://github.com/facebook/create-react-app) 类似,需要自己将组件挂载到 DOM 节点、添加热更新代码等。如下: ```js title=src/index.jsx import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; ReactDOM.render(<App />, document.getElementById('root')); ``` Modern.js **不推荐**使用这种方式,这种方式丧失了框架的一些能力,如 **`modern.config.js` 文件中的 `runtime` 配置将不会再生效**。 ## 如何自定义入口 当项目对目录结构有特殊要求时,可以在 `modern.config.js` 文件中添加配置 `source.entries` 自行配置入口。 ### 自定义普通入口 通常情况下,创建的入口需要是 **App 类型**,即指定的入口文件只需要导出应用的根组件。 例如以下目录结构: ```bash . ├── src │ └── entry │ ├── chat.tsx │ └── home.tsx └── package.json ``` 结合上面【默认入口机制】的内容,Modern.js 在分析上述目录结构时,不会得到任何默认入口。 在不想改变目录结构的情况下(如项目迁移),可以通过 `source.entries` 自定义入口: ```js title="modern.config.js" export default defineConfig({ source: { entries: { home: './src/entry/home.tsx', chat: './src/entry/chat.tsx', }, }, }) ``` `src/entry/home.tsx` 中只需要导出根组件,此时该文件就会和 `src/App` 等价,Modern.js 会生成真正的 Webpack 入口文件。 ### disableMount 在迁移旧项目到 Modern.js 时,可能不需要使用默认生成代码的功能,可以设置 `disableMount` 属性,关闭入口代码生成的功能。 针对上面 `chat``home` 两个入口的目录结构,当配置如下时: ```js title="modern.config.js" export default defineConfig({ source: { entries: { home: { entry: './src/entry/home.tsx', disableMount: true, // 禁止生成入口代码 }, chat: './src/entry/chat.tsx', }, }, }) ``` Modern.js 不会为 `src/entry/home.tsx` 生成额外的入口代码,而是直接把它当做普通的 Webpack 入口文件。此时,用户需要自行处理组件挂载、热更新等问题。 ### enableFileSystemRoutes 可以通过该选项指定某个目录使用约定式路由,例如以下目录结构: ```bash . ├── src │ └── routes │ ├── a.jsx │ ├── b.jsx │ └── index.js └── package.json ``` 需要在 `routes` 目录开启约定式路由时,配置如下: ```js title="modern.config.js" export default defineConfig({ source: { entries: { routes: { entry: './src/routes', // 启用约定式路由时,entry 必须为目录 enableFileSystemRoutes: true, }, }, }, }) ``` `dev` 命令运行成功后,访问路由与文件对应如下: * `http://localhost:8080/routes` -> `./src/routes/index.js` * `http://localhost:8080/routes/a` -> `./src/routes/a.jsx` * `http://localhost:8080/routes/b` -> `./src/routes/b.jsx` :::tip 提示 1. 使用 `enableFileSystemRoutes` 时,`entry` 属性必须为目录。 2. `enableFileSystemRoutes` 不能与 `disableMount` 同时开启。 ::: ### 自定义入口和默认入口合并 在指定 `source.entries` 后,Modern.js 会将用户自定义的入口与分析目录结构得到的默认入口合并。合并规则为: 比较自定义入口设置的入口路径和默认入口路径,当入口路径一致时,自定义入口会覆盖默认入口。 例如以下目录结构: ```bash . ├── src │ ├── chat │ │ └── App.jsx │ └── home │ └── index.js └── package.json ``` Modern.js 分析 `src/` 目录,得到默认入口 `chat``home`。当用户在 `modern.config.js` 文件中配置如下时: ```js title="modern.config.js" export default defineConfig({ source: { entries: { index: './src/home/index.js', }, }, }; ``` 可以看到自定义入口 `index` 的路径和默认入口 `home` 的路径一致,在合并的过程中,`index` 会覆盖掉 `home`,最终入口如下: * `chat` -> `./src/chat/App.jsx` * `index` -> `./src/home/index.js` 需要注意的是 `index` 入口由于没有设置 `disableMount`,所以是会默认生成入口代码。 ## 如何修改入口对应的路由 默认情况下,入口名也就是对应的访问路由,当需要修改入口对应的访问路由或者设置多个路由时,可以在 `modern.config.js` 文件中添加 [`server.routes`](/docs/apis/config/server/routes) 配置。