返回
0 到 1 搭建 React+TypeScript+webpack 项目
前端
2023-10-01 23:18:31
搭建项目结构
我们的项目将采用如下结构:
.
├── assets
│ ├── css
│ │ ├── main.css
│ │ └── normalize.css
│ └── images
│ ├── logo.png
│ └── favicon.ico
├── components
│ ├── Button
│ │ ├── Button.tsx
│ │ └── Button.scss
│ ├── Card
│ │ ├── Card.tsx
│ │ └── Card.scss
│ ├── Footer
│ │ ├── Footer.tsx
│ │ └── Footer.scss
│ ├── Header
│ │ ├── Header.tsx
│ │ └── Header.scss
│ ├── Layout
│ │ ├── Layout.tsx
│ │ └── Layout.scss
│ ├── Page
│ │ ├── Page.tsx
│ │ └── Page.scss
└── src
├── index.tsx
├── main.tsx
├── App.tsx
├── store.ts
├── reducers
│ ├── counterReducer.ts
│ └── todoReducer.ts
└── actions
├── counterActions.ts
└── todoActions.ts
安装依赖库
我们使用 npm 安装所需的依赖库:
npm install react react-dom typescript webpack webpack-cli webpack-dev-server @types/react @types/react-dom @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript babel-loader ts-loader css-loader style-loader sass-loader postcss-loader autoprefixer
配置项目
1. webpack.config.js
在项目根目录创建 webpack 配置文件 webpack.config.js
:
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.tsx',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'sass-loader',
],
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|jpg|gif|svg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/images',
},
},
],
},
],
},
devServer: {
contentBase: './dist',
port: 3000,
},
};
2. tsconfig.json
在项目根目录创建 TypeScript 配置文件 tsconfig.json
:
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"jsx": "react-jsx"
},
"include": [
"src"
],
"exclude": [
"node_modules"
]
}
3. .babelrc
在项目根目录创建 Babel 配置文件 .babelrc
:
{
"presets": [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript"
]
}
开发项目
现在,我们可以使用以下命令启动开发服务器:
npm start
编写组件
为了编写组件,我们将在 components
目录中创建单独的文件,每个组件一个文件。例如,创建一个名为 Button
的组件,并在 Button.tsx
文件中编写代码:
import React from 'react';
import styles from './Button.scss';
interface ButtonProps {
label: string;
onClick: () => void;
}
const Button: React.FC<ButtonProps> = ({ label, onClick }: ButtonProps) => {
return (
<button className={styles.button} onClick={onClick}>
{label}
</button>
);
};
export default Button;
编写样式
组件的样式放在与其同名的 .scss
文件中,例如 Button.scss
:
.button {
padding: 10px 20px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #fff;
color: #333;
font-size: 16px;
cursor: pointer;
&:hover {
background-color: #eee;
}
&:active {
background-color: #ddd;
}
}
编写页面
页面放在 src
目录中,例如创建一个名为 Home
的页面,在 Home.tsx
文件中编写代码:
import React from 'react';
import Button from '../components/Button';
const Home: React.FC = () => {
return (
<div>
<h1>Home</h1>
<Button label="Click Me" onClick={() => { alert('Hello World!'); }} />
</div>
);
};
export default Home;
编写路由
在 src
目录中创建 index.tsx
文件,作为应用程序的入口:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Home from './pages/Home';
const App: React.FC = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
</Routes>
</BrowserRouter>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
优化项目
1. 代码分割
我们可以使用代码分割来优化项目的加载速度,在 webpack.config.js
中添加如下配置:
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
}
2. 图片压缩
我们可以使用 image-webpack-loader
来压缩图片,在 webpack.config.js
中添加如下配置:
module: {
rules: [
// ...其他规则
{
test: /\.(png|jpg|gif|svg)$/,
use: [
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
quality: 80,
},
pngquant: {
quality: [0.65, 0.90],
speed: 4,
},
gifsicle: {
optimizationLevel: 7,
},
svgo: {
plugins: [
{
removeViewBox: false,
},
],
},
},
},
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/images',
},
},
],
},
],
}
3. CSS 提取
我们可以使用 `mini