npm init一路enter下去
- {
- "name": "demo",
- "version": "1.0.0",
- "description": "",
- "main": "index.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1",
- "devBuild": "cross-env NODE_ENV=development webpack --config build/webpack.config.js",
- "start:dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.js",
- "start:prod": "cross-env NODE_ENV=production webpack-dev-server --config build/webpack.config.js",
- "build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js"
- },
- "keywords": [],
- "author": "",
- "license": "ISC",
- "devDependencies": {
- "@babel/core": "^7.0.0",
- "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
- "@babel/preset-env": "^7.22.20",
- "@babel/preset-react": "^7.22.15",
- "@babel/preset-typescript": "^7.23.0",
- "@reduxjs/toolkit": "^1.9.7",
- "@types/react": "^18.2.27",
- "@types/react-dom": "^18.2.12",
- "autoprefixer": "^9.7.3",
- "axios": "^1.5.1",
- "babel-core": "^7.0.0-bridge.0",
- "babel-loader": "7",
- "babel-preset-env": "^1.7.0",
- "clean-webpack-plugin": "^3.0.0",
- "cross-env": "^7.0.3",
- "css-loader": "^3.2.1",
- "file-loader": "^5.0.2",
- "happypack": "^5.0.1",
- "html-webpack-plugin": "^3.2.0",
- "less": "^3.10.3",
- "less-loader": "5.0.0",
- "mini-css-extract-plugin": "^0.8.0",
- "optimize-css-assets-webpack-plugin": "^5.0.3",
- "postcss-loader": "^3.0.0",
- "react-redux": "^8.1.3",
- "redux": "^4.2.1",
- "redux-persist": "^6.0.0",
- "style-loader": "^1.0.1",
- "terser-webpack-plugin": "^2.2.2",
- "typescript": "^5.2.2",
- "url-loader": "^3.0.0",
- "webpack": "^4.41.2",
- "webpack-cli": "^3.3.10",
- "webpack-dev-server": "^3.9.0",
- "webpack-merge": "^4.2.2",
- "webpack-parallel-uglify-plugin": "^1.1.2"
- },
- "dependencies": {
- "antd": "^5.10.0",
- "lodash": "^4.17.15",
- "moment": "^2.24.0",
- "react": "^18.2.0",
- "react-dom": "^18.2.0"
- }
- }
另外因为我使用了redux, 复杂的项目可以使用它, 简单的项目context或者recoil就行了, 按需下载, 不需要的可以卸载
- const path = require('path')
- const HtmlWebpackPlugin = require('html-webpack-plugin')
- const { srcPath, distPath } = require('./paths')
-
- module.exports = {
- entry: {
- index: path.join(srcPath, 'index.tsx'),
- other: path.join(srcPath, 'other.tsx')
- },
- resolve: {
- extensions: ['.tsx', '.ts', '.jsx', '.js']
- },
- module: {
- rules: [
- {
- test: /.(jsx?)|(ts?x?)$/,
- loader: ['babel-loader'],
- include: srcPath,
- exclude: /node_modules/
- },
- ]
- },
- plugins: [
- // 多入口 - 生成 index.html
- new HtmlWebpackPlugin({
- template: path.join(srcPath, 'index.html'),
- filename: 'index.html',
- // chunks 表示该页面要引用哪些 chunk (即上面的 index 和 other),默认全部引用
- chunks: ['index', 'vendor', 'common'] // 要考虑代码分割
- }),
- // 多入口 - 生成 other.html
- new HtmlWebpackPlugin({
- template: path.join(srcPath, 'other.html'),
- filename: 'other.html',
- chunks: ['other', 'common'] // 考虑代码分割
- })
- ]
- }
dev环境需要的webpack配置, 注意与webpack.common.js的逻辑进行合并
- const path = require('path')
- const webpack = require('webpack')
- const webpackCommonConf = require('./webpack.common.js')
- const { smart } = require('webpack-merge')
- const { srcPath, distPath } = require('./paths')
-
- module.exports = smart(webpackCommonConf, {
- mode: 'development',
- module: {
- rules: [
- // 直接引入图片 url
- {
- test: /\.(png|jpg|jpeg|gif)$/,
- use: 'file-loader'
- },
- {
- test: /\.css$/,
- // loader 的执行顺序是:从后往前
- loader: ['style-loader', 'css-loader', 'postcss-loader'] // 加了 postcss
- },
- {
- test: /\.less$/,
- // 增加 'less-loader' ,注意顺序
- loader: ['style-loader', 'css-loader', 'less-loader']
- }
- ]
- },
- plugins: [
- new webpack.DefinePlugin({
- // 'development'
- 'process.env': {
- NODE_ENV: JSON.stringify(process.env.NODE_ENV)
- }
- })
- ],
- devServer: {
- port: 8111,
- progress: true, // 显示打包的进度条
- contentBase: distPath, // 根目录
- open: true, // 自动打开浏览器
- compress: true, // 启动 gzip 压缩
- // 设置代理
- proxy: {
- // 将本地 /api/xxx 代理到 localhost:3000/api/xxx
- '/api': 'http://localhost:8000',
-
- // 将本地 /api2/xxx 代理到 localhost:3000/xxx
- '/api2': {
- target: 'http://localhost:8111',
- pathRewrite: {
- '/api2': ''
- }
- }
- }
- }
- })
production环境需要的webpack配置, ,注意与webpack.common.js的逻辑进行合并
- const path = require('path')
- const webpack = require('webpack')
- const { smart } = require('webpack-merge')
- const { CleanWebpackPlugin } = require('clean-webpack-plugin')
- const MiniCssExtractPlugin = require('mini-css-extract-plugin')
- const TerserJSPlugin = require('terser-webpack-plugin')
- const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
- const webpackCommonConf = require('./webpack.common.js')
- const { srcPath, distPath } = require('./paths')
-
- module.exports = smart(webpackCommonConf, {
- mode: 'production',
- output: {
- // filename: 'bundle.[contentHash:8].js', // 打包代码时,加上 hash 戳
- filename: '[name].[contentHash:8].js', // name 即多入口时 entry 的 key
- path: distPath,
- // publicPath: 'http://cdn.abc.com' // 修改所有静态文件 url 的前缀(如 cdn 域名),这里暂时用不到
- },
- module: {
- rules: [
- // 图片 - 考虑 base64 编码的情况
- {
- test: /\.(png|jpg|jpeg|gif)$/,
- use: {
- loader: 'url-loader',
- options: {
- // 小于 5kb 的图片用 base64 格式产出
- // 否则,依然延用 file-loader 的形式,产出 url 格式
- limit: 5 * 1024,
-
- // 打包到 img 目录下
- outputPath: '/img1/',
-
- // 设置图片的 cdn 地址(也可以统一在外面的 output 中设置,那将作用于所有静态资源)
- // publicPath: 'http://cdn.abc.com'
- }
- }
- },
- // 抽离 css
- {
- test: /\.css$/,
- loader: [
- MiniCssExtractPlugin.loader, // 注意,这里不再用 style-loader
- 'css-loader',
- 'postcss-loader'
- ]
- },
- // 抽离 less
- {
- test: /\.less$/,
- loader: [
- MiniCssExtractPlugin.loader, // 注意,这里不再用 style-loader
- 'css-loader',
- 'less-loader',
- 'postcss-loader'
- ]
- }
- ]
- },
- plugins: [
- new CleanWebpackPlugin(), // 会默认清空 output.path 文件夹
- new webpack.DefinePlugin({
- 'process.env': {
- NODE_ENV: JSON.stringify(process.env.NODE_ENV)
- }
- }),
-
- // 抽离 css 文件
- new MiniCssExtractPlugin({
- filename: 'css/main.[contentHash:8].css'
- })
- ],
-
- optimization: {
- // 压缩 css
- minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
-
- // 分割代码块
- splitChunks: {
- chunks: 'all',
- /**
- * initial 入口 chunk,对于异步导入的文件不处理
- async 异步 chunk,只对异步导入的文件处理
- all 全部 chunk
- */
-
- // 缓存分组
- cacheGroups: {
- // 第三方模块
- vendor: {
- name: 'vendor', // chunk 名称
- priority: 1, // 权限更高,优先抽离,重要!!!
- test: /node_modules/,
- minSize: 0, // 大小限制
- minChunks: 1 // 最少复用过几次
- },
-
- // 公共的模块
- common: {
- name: 'common', // chunk 名称
- priority: 0, // 优先级
- minSize: 0, // 公共模块的大小限制
- minChunks: 2 // 公共模块最少复用过几次
- }
- }
- }
- }
- })
根据环境判断是使用webpack.dev.js还是webpack.production.js
- const prodConfig = require('./webpack.prod.js');
-
- const webpackConfig = process.env.NODE_ENV !== 'development' ? prodConfig : () => import('./webpack.dev.js');
- module.exports = webpackConfig;
5) 在package.json添加如下命令:
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1",
- "dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.js",
- "buil:debv": "cross-env NODE_ENV=development webpack --config build/webpack.config.js",
- "build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js"
- },
console.log(process.env.NODE_ENV);
index.tsx文件
- // index.tsx
- import React from 'react'
- import ReactDOM from 'react-dom/client'
- /**
- * 不加后缀.tsx的配置: 在module.exports对象中追加=>
- * resolve: {
- extensions: ['.js', '.jsx', '.tsx', '.ts']
- },
- */
- import App from './App';
- const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
- // v18 的新方法
- root.render(<App />)
index.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
- <title>webpack demo</title>
- </head>
- <body>
- <p>webpack demo</p>
- <div id="root"></div>
- </body>
- </html>
other.tsx和other.html差不多一样按react要求写, 写出差异页面能够看出是不同页面就行
5. 新建.babelrc文件
- {
- "presets": [
- "@babel/preset-react",
- "@babel/preset-typescript",
- ],
- "plugins": []
- }
6. 新建postcss.config.js文件
- module.exports = {
- plugins: [require('autoprefixer')]
- }
7. 新建tsconfig文件
- {
- "compilerOptions": {
- "target": "es5",
- "lib": [
- "dom",
- "dom.iterable",
- "esnext"
- ],
- "allowJs": true,
- "skipLibCheck": true,
- "esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "strict": true,
- "forceConsistentCasingInFileNames": true,
- "module": "esnext",
- "moduleResolution": "node",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "noEmit": true,
- "jsx": "react-jsx",
- "noImplicitAny": false
- },
- "include": [
- "src"
- ]
- }
- import React, { useEffect, useState } from "react";
- import _ from "lodash";
-
- const Test = () => {
- const [data, setData] = useState({ a: "hello world" });
- useEffect(() => {
- setData({ a: "gggg" });
- console.log("process", process.env.NODE_ENV, process.env);
- }, []);
- return <div>{_.get(data, "a")}</div>;
- };
-
- export default Test;
- import React, { Suspense, lazy } from "react";
- import { Spin } from "antd";
- import Test from "./Test";
-
- const App: React.FC = () => {
- return (
- <Suspense fallback={<Spin />}>
- <Test />
- </Suspense>
- );
- };
-
- export default App;
index.html效果图如下:

other.html效果图如下:
