手动搭webpack + React Hooks + TypeScript + Antd(一)
本章主要讲react react-hot-loader react-router loadable typescript的引入
新建App.jsx
将src/index.js中的App函数移出至src/App.js
src/App.js
import React from 'react';
function App() {
return (
<div>==start??、==</div>
)
}
export default App;
src/index.js
import App from './App';
run的时候会报错
import App from './App.jsx';
// ...
module: { ... },
+ resolve: {
+ extensions: ['.jsx', '.js'],
+ },
plugins: [ ... ]
安装react-hot-loader
yarn add react-hot-loader -D
babel.config.js
const plugins = [
+ ['react-hot-loader/babel'],
];
webpack.dev.js 配置hot: true
devServer: {
contentBase: './dist',
host: '0.0.0.0',
public: `${process.env.DEV_HOST || localhost}:8080`,
+ hot: true,
},
src/App.js 新增
import { hot } from 'react-hot-loader/root';
export default hot(App);
开始安装配置typescript
yarn add typescript -D
yarn add @babel/preset-typescript -D
tsconfig.json
{
"compilerOptions": {
"jsx": "react",
"outDir": "./dist/",
"isolatedModules": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"allowJs": true,
"baseUrl": "./",
"target": "es5",
"module": "ESNext",
"noImplicitAny": true
},
"include": ["src/**/*", "types/*"]
}
将入口文件后缀改成tsx
build/webpack.commom.js修改
entry: './src/index.tsx',
module: {
rules: [
{
test: /\.(jsx?|tsx?)$/,
exclude: /node_modules/,
use: 'babel-loader',
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
-
根据报错提示尝试安装该库的TypeScript版本 (该库的 ts 声明文件),也就是在该库的名称前加上 @types/
-
建立xxx.d.t申明文件
此处先选方法二
types/index.d.ts
declare module 'react';
declare module 'react-dom';
但是使用Hooks需要确定组件返回
安装@types/react @types/react-dom
yarn add @types/react @types/react-dom -D
然后所有报错已经解决,至此typescript已经配置好
安装 react-router-dom
yarn add react-router-dom
在src下新建pages和routes两个文件夹,pages下新建home和about文件夹,分别新建index.tsx文件 加入Home和About
src/pages/home/index.tsx
import React from 'react';
const Home: React.FC = () => {
return (
<div>Home</div>
)
}
export default Home;
src/pages/about/index.tsx
import React from 'react';
const About: React.FC = () => {
return (
<div>About</div>
)
}
export default About;
webpack.common.js中配置别名alias
build/webpack.common.js
resolve: {
extensions: ['.tsx', '.ts', '.js'],
+ alias: {
+ '@pages': resolve('../src/pages'),
+ },
},
routes 文件夹下新建config.ts 和 index.tsx, 在config.ts中引入Home 和 About 但是报错
src/routes/config.ts
ts会导致报错找不到模块“@pages/home” 需要在tsconfig中配置相同路径,在tsconfig.ts中的compilerOptions中配置
tsconfig.ts
{
"compilerOptions": {
...
+ "paths": {
+ "@pages/*": ["./src/pages/*"],
+ }
},
"include": ["src/**/*", "types/*"]
}
如果以后引入其他alias 也需要配置tsconfig文件,错误已经解决
src/routes/config.ts
import Home from '@pages/home';
// ...其他Compont
export interface IRouteItem {
name: string;
path: string;
exact: boolean;
component: React.FC;
title?: string;
meta?: any;
needAuth?: boolean; // 是否需要登录
};
export const routes: IRouteItem[] = [
{
name: 'Index',
path: '/',
exact: true,
component: Home,
title: '主页',
meta: {},
},
{...},
];
src/routes/index.tsx
import React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { routes, IRouteItem } from './config';
export default function Routes() {
return (
<BrowserRouter>
<Switch>
{routes.map(route => {
const { name, path, exact, component: Component, ...rest } = route;
return (
<Route
key={name}
path={path}
exact={exact}
render={routeProps => {
return (
<Component {...routeProps} />
);
}}
></Route>
);
})}
</Switch>
</BrowserRouter>
);
}
在页面中url后加上/home 发现页面404了
react-router设置path无效,错误信息Cannot GET /xxx
build/webpack.dev.js
devServer: {
contentBase: './dist',
host: '0.0.0.0',
public: `${process.env.DEV_HOST || localhost}:8080`,
hot: true,
+ historyApiFallback: true,
},
但是在VScode中,Component会报错
这是因为在Home和About里面的组件是继承React.FC
const About: React.FC = () => {
routeProps是react router返回的router
可以从react-router中引入RouteComponentProps或者RouteProps
src/routes/config.ts
+ import { RouteComponentProps, RouteProps } from "react-router";
所以IRouteItem中将component:React.FC改写成写入React.ComponentType<RouteProps>;
或者component: React.ComponentType<RouteComponentProps>;
参考 TypeScript + React最佳实践-第一节:Component类型化
src/routes/config.ts IRouteItem 修改
export interface IRouteItem {
name: string;
path: string;
exact: boolean;
- component: React.FC;
+ component: React.ComponentType<RouteProps>;
title?: string;
meta?: any;
needAuth?: boolean; // 是否需要登录
};
但是这样直接引入所有的Component会导致打包过大,所以需要引入react-loadable做按需加载
安装react-loadable
yarn add react-loadable -D
src/routes/config.ts
+ import Loadable from 'react-loadable';
安装loadable ts声明文件
yarn add @types/react-loadable -D
src/routes/config.ts 中引入loadable
import Loadable, { LoadingComponentProps } from 'react-loadable';
function LoadPage() {
return (
<div>loading...</div>
);
}
src/routes/config.ts 新增
type ComponentType = React.ComponentType<RouteComponentProps> |
React.ComponentType<any>;
interface ILoadable<Props> {
loader(): Promise<React.ComponentType<Props> |
{ default: React.ComponentType<Props> }>;
loading?: React.ComponentType<LoadingComponentProps>;
}
function loadable<Props>(props: ILoadable<Props>) {
return Loadable({
loading: LoadingPage,
delay: 300,
...props,
});
}
src/routes/config.ts
- import Home from '@pages/home';
- import About from '@pages/about';
const Home: ComponentType = loadable({
loader: () => import('../pages/home'),
});
const About: ComponentType = loadable({
loader: () => import('../pages/about'),
});
src/pages/home/index.tsx
+ import { Link } from "react-router-dom";const Home: React.FC = () => {
return (
<>
<div>Home</div>
+ <Link to="/about">to about</Link>
</>
)
}
export default Home;
浏览器中发现Home和About已经被分别打包成了
通常在页面中会把页面中的search当做query直接传入
将url的参数改成object形式
src/routes/index.tsx
const query = Object.fromEntries(new URLSearchParams(location.search.substr(1)));
因为在tsconfig.ts中为了考虑兼容性问题就把target指定为较低版本"target": "es5",如果使用了较高的es版本则ts会有报错提示。可以改成ESNext,具体哪些配置参见ts编译选项 修改后ts不会再报错,但是query依然报错
那是因为Route的Component的Prop上并没有query,可以查看RouteComponentProps源码👇
所以在config.ts中可以重写一个interface继承RouteComponentProps
src/routes/config.ts
interface IComponentProps extends RouteComponentProps {
query?: {
[key: string]: any;
};
}
type ComponentType = React.ComponentType<IComponentProps> |
React.ComponentType<any>;
页面中也能通过props直接获取query,而不需要每次都解析url参数一遍, Home或者About的props可打印出
至此react react-hot-loader react-router loadable typescript已经配置完成,接下来继续配置css less css-modules