前言
webpack4正式版釋出也有一段時間了, 為了從實際中感受變化, 於是以typescript, react, 結合之前翻譯的一篇文章webpack-4.0更新日誌(翻譯), 搭建一個可供專案使用的框架.
專案狀況
github
專案已使用
- webpack-4
- typescript
- react
- react-router-4
- component hot reload
- async component
- import .(s)css auto generate .(s)css.d.ts
- svg icon
- Mobx
- Antd
主要庫及版本
- webpack ——— 4.1.1
- typescript ——— 2.9.2
- react ———— 16.3.2
- react-router-dom — 4.2.2
- react-hot-loader — 4.2.0
- ……
- node ————- 8.11.2
原配置:
// tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "es2015",
"moduleResolution": "node",
....
}
}
// webpack.config.js
{
test: /.(ts(x?)|js(x?))$/,
exclude: /node_modules/,
rules: [
{
loader: `react-hot-loader/webpack`,
},
{
loader: `ts-loader`,
options: {
transpileOnly: true
}
}
}
Module not found: Error: Can`t resolve `react-hot-loader/webpack` in `/Users/jackple/Documents/react/ts-react-webpack4`
果然跑不通, 符合心理預期!????
檢視node_modules裡面的react-hot-loader, 對比原專案node_modules的react-hot-loader, 檔案結構改了不少, 看4.0.0的原始碼, 也不再有webpack目錄, 而且程式碼中有這麼一段:
throw new Error(`React Hot Loader: You are erroneously trying to use a Babel plugin ` + `as a Webpack loader. We recommend that you use Babel, ` + `remove "react-hot-loader/babel" from the "loaders" section ` + `of your Webpack configuration, and instead add ` + `"react-hot-loader/babel" to the "plugins" section of your .babelrc file. ` + `If you prefer not to use Babel, replace "react-hot-loader/babel" with ` + `"react-hot-loader/webpack" in the "loaders" section of your Webpack configuration. `);
提示資訊與實際不符, 估計提示沒改過來, 還是用babel吧(其實它的README也是建議用babel), 免得掙扎!
按照react-hot-loader官方推薦做法以及demo
When using TypeScript, Babel is not required, but React Hot Loader will not work without it. Just add babel-loader into your Webpack configuration, with React Hot Loader plugin.
{
test: /.tsx?$/,
use: [
{
loader: `babel-loader`,
options: {
babelrc: true,
plugins: [`react-hot-loader/babel`],
},
},
`ts-loader`, // (or awesome-typescript-loader)
],
}
You also have to modify your tsconfig.json:
// tsconfig.json
{
"module": "commonjs",
"target": "es6"
}
// xxx.tsx
import { hot } from `react-hot-loader`
...
export default hot(module)(Component)
- 首先是react-hot-loader/babel, 無論是按官方推薦配置, 還是寫在.babelrc, 以上兩者都加上環境判斷, 開發環境沒問題, 可是打包出來的生產環境程式碼看起來怪怪的:
...
default = i, u = r(4).
default, s = r(4).leaveModule, u && (u.register(l, "Error", "/Users/jackple/Documents/react/ts-react-webpack4/src/components/Error/index.tsx"), u.register(i, "default", "/Users/jackple/Documents/react/ts-react-webpack4/src/components/Error/index.tsx"), s(e))
}).call(this, r(12)(e))
...
怎麼看都不合理呀! 為什麼會出來我的本地原始檔路徑! 本地開nginx除錯生產環境程式碼, 雖然能跑, 不過, 這不是我想要的!
最後, 這部分的處理結果是把react-hot-loader/babel刪除之! 再將tsx接受hot reload還原為舊式寫法:
import { AppContainer } from `react-hot-loader`
import AppRouter from `./router`
const render = Component => {
ReactDOM.render(
<AppContainer>
<Component />
</AppContainer>,
document.getElementById(`app`) as HTMLElement
)
}
render(AppRouter)
// Hot Module Replacement API
if (module.hot) {
module.hot.accept([`router`], () => {
import(`./router`).then(mod => render(mod.default))
})
}
- 再者是為配合webpack4的程式碼分割(dynamic import), 需要把tsconfig.json中的module置為esnext(主要是為了讓typescript不編譯import), 不知道是不是我配置失誤, 顯示以下錯誤:
ERROR in ./src/index.tsx
Module build failed: SyntaxError: `import` and `export` may only appear at the top level (14:8)
12 | if (module.hot) {
13 | module.hot.accept([`router`], () => {
> 14 | import(`./router`).then(mod => render(mod.default));
| ^
15 | });
16 | }
不是說好的支援import語法了咩? 最後還是要加上syntax-dynamic-import的babel外掛
bootstrap:74 Uncaught (in promise) TypeError: Cannot read property `call` of undefined
at i (bootstrap:74)
at Object.31 (index.css:1)
at i (bootstrap:74)
at Object.32 (index.css?f62f:2)
at i (bootstrap:74)
at Object.52 (index.tsx:3)
at i (bootstrap:74)
at index.tsx:9
at <anonymous>
過程曲折, 還有一些問題沒寫上, 具體請參考專案…
如果大家有更好的解決方案, 也歡迎評論或提issue