记录一次搭建 react 项目遇到的坑
背景
电商项目需要搭建一个 h5 项目。这里简单记录一下搭建过程遇到的坑,作为总结。
ui 框架:为了统一 ui 框架,因此选择使用 antd-mobile。
打包:parceljs 在此前的项目中已经使用过很多次了,打包速度较 cra 快,且配置简单,因此拟用 parcel。
ts:多人项目还是上 ts,没什么好说的。
很快就搭建好 demo,接着就开始遇到一些问题。
antd-mobile 按需加载
antd-mobile 按需加载推荐使用 babel-plugin-import
// .babelrc or babel-loader option
{
"plugins": [
["import", { "libraryName": "antd-mobile", "style": "css" }] // `style: true` 会加载 less 文件
]
}
此前 parcel 实际上内置支持了 ts,直接转成 es5 了,因此此前没有额外再配置 babel。
🤔 babel-plugin-import 只支持 es6 module 写法的代码,看来需要先把 ts 转成 es6 然后在通过 babel 转成 es5, 没问题,安排~ 于是:
新增 .babelrc
修改 tsconfig 的 module 为
es6{ "compilerOptions": { "module": "ES6" } }项目启动,没问题。但是 vscode 开始报错:
import React from 'react'
// Module '"/node_modules/@types/react/index"' can only be default-imported using the 'allowSyntheticDefaultImports' flag
根本原因是由于 react 导出如下:
module.exports = require('react.development.js')
而非
module.exports.default = require('react.development.js')
Babel 会在 commonjs 格式添加 default 导出,那我们要做的就是让 ts 不要提示这种类型的报错:
{
"compilerOptions": {
"allowSyntheticDefaultImports": true
// ...
}
}
🤔 不过为什么一开始 parcel 不会报错呢?
接下来路由动态引入组件开始报错:
import React, { Suspense, lazy } from 'react'
const List = lazy(() => import('./modules/List'))
// Dynamic imports are only supported when the '--module' flag is set to 'es2020', 'esnext', 'commonjs', 'amd', 'system', or 'umd'.
看来 es6 是不支持 import() 语法, 那好办,将 module 改成 esnext:
{
"compilerOptions": {
"module": "ESNext",
}
}
接下来引入 anti-mobile 组件
import { List } from 'antd-mobile'
// Cannot find module 'antd-mobile' or its corresponding type declarations.
此时提示找不到对应的模块声明, 此时我的内心是崩溃的,看起来应该是模块解析路径不对,因此在 tsconfig 中显式配置 moduleResolution:
{
"compilerOptions": {
"moduleResolution": "Node"
// ...
}
}
不再报错了,看看 ts 文档的解释吧,ts 共有两种可用的模块解析策略:Node和Classic
module === "AMD" or "System" or "ES6" ? "Classic" : "Node"
🤔 按理说 module 为 esnext 的话岂不是应该按 node 策略解析?但是目前看来似乎不是这样的?
配置 antd-mobile 主题
接下来需要配置主题,anti-mobile 推荐使用 modifyVars 来配置主题。
将 .babelrc 改成做一下调整:
{
...
"plugins": [
["import", {"libraryName": "antd-mobile", "style": true}],
...
]
}
按照 webpack 的经验,此时需要开启 javascriptEnabled: true问题来了 parcel 内置支持 less ,那我们要在哪里去配置 less 呢?根据 issue 提到可以用 .lessrc 文件来配置,但是我经过尝试发现其实是无效的。。
至此基本放弃 parcel。parcel 在快速搭建实验性项目时确实有一定的优势,不过生态确实不如 webpack,由此转向使用 create-react-app
create-react-app
使用 cra 很快也建好 demo,解决如何让 cra 创建的项目支持 less ,这个简单:
通过 customize-cra 提供的 addLessLoader 方法即可,同时也提供了 fixBabelImports 可以很方便的添加 babel-plugin-import 配置:
const { override, fixBabelImports, addLessLoader } = require('customize-cra')
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd-mobile',
style: true,
}),
addLessLoader({
lessOptions: {
javascriptEnabled: true,
modifyVars: {
'@brand-primary': '#ff74b9',
'brand-primary-tap': '#ff9cca',
},
},
})
)
别名
接着是配置路径别名遇到的问题,通过 customize-cra 提供的 addWebpackAlias 可以配置别名,但是 typescript 一直无法解析对应别名,那么应该只需要配置好 ts 的 paths 就行了吧:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
// ...
}
}
vscode 不再报错了,接下来就是见证奇迹的时刻了npm run start:
控制台显示 - compilerOptions.paths must not be set (aliased imports are not supported) 并且 cra 会自动把 tscongfig 中的 paths 移除掉 😂
根据 issue,使用将 baseUrl 和 paths 的配置移动到单独的文件后,在 tsconfig 里通过 extends 的方式继承,虽然控制台依然提示错误,但是别名可以生效。
mini-css-extract-plugin
在 build 时,mini-css-extract-plugin 提示 Conflicting order, 错误位置在 antd-mobile 的样式文件。根据 issue 以及 issue 看来这个问题主要是由于懒加载加上 antd 组件导入顺序不一致导致的,目前看来无需解决。