《抛开脚手架,徒手搭建 react 项目(一)》]我们用webpack搭建了一个react项目,引入了typescript,用Babel编译,还用webpack-merge拆分了我们的配置文件。
《抛开脚手架,徒手搭建 react 项目(二)》我们在项目里面引入了eslint,还介绍了打开服务的五种方法。尤其重要的是 nginx,要你在本地体验一把线上奔跑的感觉。
《抛开脚手架,徒手搭建 react 项目(三)》我们在项目里面引入了 stylelint、还有husky相关工具,轻松实现git commit的时候,对代码格式和提交信息做具体的检测。
《抛开脚手架,徒手搭建 react 项目,webpack打包优化篇(四)》亲手测试并整理了六点减少打包时间的手段。
抛开脚手架,徒手搭建 react 项目,webpack打包优化篇(五) 亲手测试并整理了六点能够缩减打包体积的手段
本篇文章主要讲解如何从一个空目录开始,建立起一个基于webpack + react + typescript的标准化前端应用。
- 技术栈: webpack5 + React18 + TS
- 工程化: eslint + prettier + husky + git hooks
- 支持图片、less、sass、fonts、数据资源(JSON、csv、tsv等)、Antd按需加载以及主题
- 支持热更新、资源压缩、代码分离(动态导入、懒加载等)、缓存、devServer
初始化项目
创建一个项目,然后用npm初始化一下
mkdir demo
cd demo
git init
npm init -y
他会生成package.json
加入webpack
npm i webpack webpack-cli webpack-dev-server -D
配置文件webpack.config.js:
import path from 'path';
import { fileURLToPath } from "node:url";
import { dirname } from "node:path";
const __dirname = dirname(fileURLToPath(import.meta.url));
export default {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js'
},
};
在package.json里面加入命令
"dev": "webpack --mode development ",
"build": "webpack --mode production "
创建文件src/index.js,然后测试,看看有没有生成dist
加入热启动
在webpack.config.js里面先加入插件,再在webpack-dev-server上标注
引入react 和 babel
npm react react-dom -S
npm i babel-loader @babel/core @babel/preset-env @babel/preset-react -D
配置webpack.config.js
import path from 'path';
import { fileURLToPath } from "node:url";
import { dirname } from "node:path";
import HtmlWebpackPlugin from 'html-webpack-plugin'
const __dirname = dirname(fileURLToPath(import.meta.url));
export default {
entry: './src/index.jsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
include: path.resolve(__dirname, 'src'),
exclude: path.resolve(__dirname, 'node_modules'),
loader:'babel-loader',
options:{
presets: [
"@babel/preset-env",
"@babel/preset-react"
]
}
}
]
},
resolve: {
extensions: [ '.jsx', '.js'],
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html', // 指定一个HTML模板文件
filename: './index.html', // 输出的HTML文件名,默认是index.html
inject: true, // 允许插件修改哪些内容,true将脚本添加到body元素的末尾
})
],
mode: "development", // 开发模式
};
上面babel的配置,你可以单独放在babel.config.js里面,也可以像我一样配置。
添加react组件,先写 index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';;
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<App />
);
再写App.jsx
import React from 'react'
function App() {
return (
<div className="App">
hello,React!
</div>
);
}
export default App;
然后写index.html容器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script defer src="index.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
配置webpack.config.js
import path from 'path';
import { fileURLToPath } from "node:url";
import { dirname } from "node:path";
import HtmlWebpackPlugin from 'html-webpack-plugin'
const __dirname = dirname(fileURLToPath(import.meta.url));
export default {
entry: './src/index.jsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
include: path.resolve(__dirname, 'src'),
exclude: path.resolve(__dirname, 'node_modules'),
loader:'babel-loader',
options:{
presets: [
"@babel/preset-env",
"@babel/preset-react"
]
}
}
]
},
resolve: {
extensions: [ '.jsx', '.js'],
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html', // 指定一个HTML模板文件
filename: './index.html', // 输出的HTML文件名,默认是index.html
inject: true, // 允许插件修改哪些内容,true将脚本添加到body元素的末尾
})
],
mode: "development", // 开发模式
};
配置打包命令
"dev": "webpack --mode development ",
"build": "webpack --mode production "
加入ts
说明
@babel/preset-typescript 是一个 Babel 插件的集合,它可以将 TypeScript 代码转换为 JavaScript。如果你想要使用 Babel 来转换 TypeScript 代码,而不需要使用 TypeScript 编译器本身,那么你可以使用 @babel/preset-typescript。
所以说什么typescript,什么ts-loader都不需要了,就@babel/preset-typescript即可。
如果你想手动编译ts文件用命令
npx babel your-typescript-file.ts --out-file compiled-js-file.js
npx babel --watch src --out-dir dist --presets @babel/preset-env,@babel/preset-typescript
安装
npm i @types/react @types/react-dom @babel/preset-typescript -D
tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": false,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react" // react18这里也可以改成react-jsx
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
修改webpack.config.js
修改文件
测试
用webpack-merge拆分webpack.config.js
说明
-
development(开发环境) 和production(生产环境) 这两个环境下的构建目标存在着巨大差异所以webpack的配置写的差距会非常的大 -
在开发环境中,我们需要:强大的 source map 和一个有着 live reloading(实时重新加载) 或 hot module replacement(热模块替换) 能力的
localhost server。 -
而生产环境目标则转移至其他方面,关注点在于压缩 bundle、更轻量的 source map、资源优化等,通过这些优化方式改善加载时间。由于要遵循逻辑分离,我们通常建议为
每个环境编写彼此独立的 webpack 配置。 -
虽然,以上我们将 生产环境 和 开发环境 做了细微区分,但是,请注意,我们还是会遵循不重复写配置的原则,保留一个
"common( 公共 )" 配置。为了将这些配置合并在一起,我们将使用一个名为webpack-merge的工具。此工具会引用 “common” 配置,因此我们不必再在环境特定env的配置中编写重复代码。
安装
npm i webpack-merge -D
引入merge
import {merge} from "webpack-merge";
拆分webpack.config.js
代码webpack.common.js
import path from 'path';
import { fileURLToPath } from "node:url";
import { dirname } from "node:path";
import HtmlWebpackPlugin from 'html-webpack-plugin'
const __dirname = dirname(fileURLToPath(import.meta.url));
export default {
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
clean: true, // webpack4需要配置clean-webpack-plugin来删除dist文件,webpack5内置了
publicPath: '/' // 打包后文件的公共前缀路径
},
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
include: path.resolve(__dirname, 'src'),
exclude: path.resolve(__dirname, 'node_modules'),//不解析的模块
loader:'babel-loader',
options:{
presets: [//他的主席那个顺序时自下往上的。
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript"
]
}
}
]
},
resolve: {
extensions: ['.js','.jsx', '.tsx', '.ts'],//当文件没有扩展名的时候,默认就找这些扩展名的文件,比如import A from ‘./A’
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html', // 打包的时候需要的模板,你不设置也行,一般都会在public文件夹里面设置下,我们图方便就不分public了
filename: './index.html', // 打包后的文件名
inject: true, // 注入静态资源,就是把js打包好以后,把script标签加进去,不然运行起来怎么找到js文件。
})
],
mode: "development", // 开发模式
};
代码webpack.dev.js
import path from 'path';
import { fileURLToPath } from "node:url";
import { dirname } from "node:path";
import {merge} from "webpack-merge";
import baseConfig from "./webpack.common.js";
const __dirname = dirname(fileURLToPath(import.meta.url));
//开发
export default merge(baseConfig, {
mode: "development", // 开发模式,打包更加快速,省了代码优化步骤
devtool: "inline-source-map", // 源码调试模式,后面会讲
devServer: {
host: "localhost", // 启动服务器域名
port: "3000", // 启动服务器端口号
open: true, // 是否自动打开浏览器
compress: false, // gzip压缩,开发环境不开启,提升热更新速度
hot: true, // 开启热更新,后面会讲react模块热替换具体配置
historyApiFallback: true, // 解决history路由404问题
static: {
directory: path.join(__dirname, "../public"), //托管静态资源public文件夹
},
},
});
代码webpack.prod.js
import {merge} from "webpack-merge";
import baseConfig from "./webpack.common.js";
export default merge(baseConfig, {
mode: "production",
});
命令配置:
"scripts": {
"dev": "webpack-dev-server --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
},
添加样式编译
由于webpack默认只加载js文件,所以前端的html和css文件都需要特定的工具来处理才能正确的加载并打包。所以对于css文件而言,我们用的是css-loader
加载css
npm i style-loader css-loader -D
配置webpack.config.js
测试看看已经生效
加载less
npm i less-loader -D
配置
把之前的css文件改成less,并且将样式改成包含结构的,试试看
启动项目
加载sass
npm i sass-loader sass -D
配置webpack
添加文件
测试
一般情况下,项目要么使用less,要么使用sass,不会一起使用的,所以咱们就选择一个就好了,我喜欢使用less,所以就把sass删掉了,如果你选择了sass,请把后缀名写成.scss,不是.sass,'.sass'是老版本。
优化配置文件
别名设置
配置
测试
webpack.config.js
resolve: {
extensions: ['.js','.jsx', '.tsx', '.ts'],//当文件没有扩展名的时候,默认就找这些扩展名的文件,比如import A from ‘./A’
alias: {
'@': path.resolve(__dirname, 'src'),
}
},
tsconfig.js
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
打包图片
webpack5已经支持了对图片的加载,所以不再额外下载loader了,只需要简单的配置就好。
配置
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 1 * 1024, // 小于1kb转base64,减少请求次数
},
},
generator: {
filename: "public/[hash:10][ext][query]", // 指定打包路径和文件名
},
},
测试
加入字体图标
npm i antd -S
npm install @ant-design/icons --save
一般我们用的都是组件库里面的图标,我发现不管配置,还是不配置,webpack都能处理,为了保险期间还是配上吧。
配置
{
test: /\.(ttf|woff|woff2?)$/i,
type: "asset/resource",
generator: {
filename: "public/iconfont/[hash:10][ext][query]",
},
},
加入视频音频
接下来看看视频
添加webpack处理
{
test: /\.(map3|map4|avi)$/i,
type: "asset/resource",
generator: {
filename: "public/media/[hash:10][ext][query]",
},
},