Webpack 代码拆分使用中继的应用程序
Webpack code-splitting an app that uses relay
Sample github repo(特定提交)
我正在研究 react/relay 并希望确保我已准备好开始开发与生产。我们有一个大型应用程序,因此必须进行代码拆分。
我更喜欢基于 JSX 的 react-router
路由声明,所以我很高兴 运行 变成 Implicit Code Splitting and Chunk Loading with React Router and Webpack from Evan Henley.
通过对正则表达式和 ES6 进行一些调整,我能够使他的方法起作用。克隆 the repo、npm install && npm start
,您将能够导航到不同的块(待办事项除外)。
^^^所有这些都适用于应用程序的非中继部分 - 好东西。
问题
然后...导航到 Todos(基于中继)。
Uncaught TypeError: _TodoListFooter2.default.getFragment is not a function
viewer
Fragment
(instrumented buildRQL.Fragment)
buildContainerFragment
我们的生产应用将完全依赖中继。
中继取决于对 类 的一些静态调用,例如 getFragment
。在这种情况下,webpack 已将 TodoListFooter
推送到 不同的 共享块中,因此 TodoListFooter2
没有 getFragment
。相比之下,在它中断的同一行上,_AddTodoMutation2
是 在同一个块 中,因此类似的调用 _AddTodoMutation2.default.getFragment('viewer')
工作正常。
可能的解决方案
- 使用命名块? This commit 试图这样做,但它仍然将一些代码推送到共享块中(并因同样的错误而失败)
- 与静态方法中继兼容的不同代码拆分方法?
- 有什么我没有想到的?
我发现虽然 Evan Henley 的方法更好,但我需要稍微修改它以使其与 Relay 兼容(与 ES6+ import
)。
这需要在 ES6 导入中显式 bundle?lazy
才能与中继兼容。 This commit 显示了我发现使其兼容中继的主要差异。
首先,在 Henley 的代码中,它需要配置 webpack 加载器和导入,例如:
import Landing from './components/Landing'
相比之下,我的方法不需要额外的 webpack 加载器配置,在 import
:
中使用显式 bundle-loader 延迟加载
import Landing from 'bundle?lazy!./components/Landing'
Henley 的方法使路由文件导入更清晰。我的方法使其与 Relay 一起工作,并且通过 import
进行更明确的代码拆分,而无需通过正则表达式进行任何额外的 webpack.config.js
加载程序配置。
每个都很好,虽然我的方法是我发现使其中继兼容的唯一方法。
实施:
type GetComponent = (location?: string, cb: GetComponentCB) => void
/**
* Purpose: abbreviated expression so that we can read routes
*
* Background:
* - (failed) luqin/react-router-loader is awesome, but it is a Proxy and doesn't work with relay components.
* @see https://github.com/relay-tools/react-router-relay/issues/161
*
* - (failed) webpack dynamic requires need a hard path root (the rest can be an expression)
* at compile time and a regex is created
* @see https://webpack.github.io/docs/html#dynamic-requires
* @see
*
* - (succeeded ultimately) huge-app-splitting-refactor
* - uses bundle-loader, works with imports! and no path hardcoding!
* - approach doesn't work with Relay (quite as-is)
* - fixed approach with explicit bundle lazy imports!
* @see
* @see http://henleyedition.com/implicit-code-splitting-with-react-router-and-webpack/
* @see https://github.com/echenley/react-router-huge-apps-refactor
*
* @param component
* @returns {function()}
*/
export function lazy (lazyModule: Function): GetComponent {
if (!lazyModule) {
throw new Error('lazy() did not find the given module. Check to be sure your import path is correct, and make sure you are pointing at a file with a ****default export****.')
}
return (location, cb) => {
lazyModule((module) => {
cb(null, module.default)
})
}
}
用法:
/* eslint-disable flowtype/require-valid-file-annotation */
import OrganizationQueries from '../../queries/OrganizationQueries'
import React from 'react'
import {Route} from 'react-router'
import {lazy} from '../../config/routes'
import Organization from 'bundle-loader?lazy!./Organization'
// protected by parent route
export default (
<Route>
<Route path='/organization' getComponent={lazy(Organization)} queries={OrganizationQueries} />
</Route>
)
Sample github repo(特定提交)
我正在研究 react/relay 并希望确保我已准备好开始开发与生产。我们有一个大型应用程序,因此必须进行代码拆分。
我更喜欢基于 JSX 的 react-router
路由声明,所以我很高兴 运行 变成 Implicit Code Splitting and Chunk Loading with React Router and Webpack from Evan Henley.
通过对正则表达式和 ES6 进行一些调整,我能够使他的方法起作用。克隆 the repo、npm install && npm start
,您将能够导航到不同的块(待办事项除外)。
^^^所有这些都适用于应用程序的非中继部分 - 好东西。
问题
然后...导航到 Todos(基于中继)。
Uncaught TypeError: _TodoListFooter2.default.getFragment is not a function
viewer
Fragment
(instrumented buildRQL.Fragment)
buildContainerFragment
我们的生产应用将完全依赖中继。
中继取决于对 类 的一些静态调用,例如 getFragment
。在这种情况下,webpack 已将 TodoListFooter
推送到 不同的 共享块中,因此 TodoListFooter2
没有 getFragment
。相比之下,在它中断的同一行上,_AddTodoMutation2
是 在同一个块 中,因此类似的调用 _AddTodoMutation2.default.getFragment('viewer')
工作正常。
可能的解决方案
- 使用命名块? This commit 试图这样做,但它仍然将一些代码推送到共享块中(并因同样的错误而失败)
- 与静态方法中继兼容的不同代码拆分方法?
- 有什么我没有想到的?
我发现虽然 Evan Henley 的方法更好,但我需要稍微修改它以使其与 Relay 兼容(与 ES6+ import
)。
这需要在 ES6 导入中显式 bundle?lazy
才能与中继兼容。 This commit 显示了我发现使其兼容中继的主要差异。
首先,在 Henley 的代码中,它需要配置 webpack 加载器和导入,例如:
import Landing from './components/Landing'
相比之下,我的方法不需要额外的 webpack 加载器配置,在 import
:
import Landing from 'bundle?lazy!./components/Landing'
Henley 的方法使路由文件导入更清晰。我的方法使其与 Relay 一起工作,并且通过 import
进行更明确的代码拆分,而无需通过正则表达式进行任何额外的 webpack.config.js
加载程序配置。
每个都很好,虽然我的方法是我发现使其中继兼容的唯一方法。
实施:
type GetComponent = (location?: string, cb: GetComponentCB) => void
/**
* Purpose: abbreviated expression so that we can read routes
*
* Background:
* - (failed) luqin/react-router-loader is awesome, but it is a Proxy and doesn't work with relay components.
* @see https://github.com/relay-tools/react-router-relay/issues/161
*
* - (failed) webpack dynamic requires need a hard path root (the rest can be an expression)
* at compile time and a regex is created
* @see https://webpack.github.io/docs/html#dynamic-requires
* @see
*
* - (succeeded ultimately) huge-app-splitting-refactor
* - uses bundle-loader, works with imports! and no path hardcoding!
* - approach doesn't work with Relay (quite as-is)
* - fixed approach with explicit bundle lazy imports!
* @see
* @see http://henleyedition.com/implicit-code-splitting-with-react-router-and-webpack/
* @see https://github.com/echenley/react-router-huge-apps-refactor
*
* @param component
* @returns {function()}
*/
export function lazy (lazyModule: Function): GetComponent {
if (!lazyModule) {
throw new Error('lazy() did not find the given module. Check to be sure your import path is correct, and make sure you are pointing at a file with a ****default export****.')
}
return (location, cb) => {
lazyModule((module) => {
cb(null, module.default)
})
}
}
用法:
/* eslint-disable flowtype/require-valid-file-annotation */
import OrganizationQueries from '../../queries/OrganizationQueries'
import React from 'react'
import {Route} from 'react-router'
import {lazy} from '../../config/routes'
import Organization from 'bundle-loader?lazy!./Organization'
// protected by parent route
export default (
<Route>
<Route path='/organization' getComponent={lazy(Organization)} queries={OrganizationQueries} />
</Route>
)