• 从零开始使用webpack搭建一个react项目


    先做一个正常编译es6语法的webpack demo

    1. 初始化package.json文件

    npm init一路enter下去

    2. 添加插件

    1. {
    2. "name": "demo",
    3. "version": "1.0.0",
    4. "description": "",
    5. "main": "index.js",
    6. "scripts": {
    7. "test": "echo \"Error: no test specified\" && exit 1",
    8. "devBuild": "cross-env NODE_ENV=development webpack --config build/webpack.config.js",
    9. "start:dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.js",
    10. "start:prod": "cross-env NODE_ENV=production webpack-dev-server --config build/webpack.config.js",
    11. "build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js"
    12. },
    13. "keywords": [],
    14. "author": "",
    15. "license": "ISC",
    16. "devDependencies": {
    17. "@babel/core": "^7.0.0",
    18. "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
    19. "@babel/preset-env": "^7.22.20",
    20. "@babel/preset-react": "^7.22.15",
    21. "@babel/preset-typescript": "^7.23.0",
    22. "@reduxjs/toolkit": "^1.9.7",
    23. "@types/react": "^18.2.27",
    24. "@types/react-dom": "^18.2.12",
    25. "autoprefixer": "^9.7.3",
    26. "axios": "^1.5.1",
    27. "babel-core": "^7.0.0-bridge.0",
    28. "babel-loader": "7",
    29. "babel-preset-env": "^1.7.0",
    30. "clean-webpack-plugin": "^3.0.0",
    31. "cross-env": "^7.0.3",
    32. "css-loader": "^3.2.1",
    33. "file-loader": "^5.0.2",
    34. "happypack": "^5.0.1",
    35. "html-webpack-plugin": "^3.2.0",
    36. "less": "^3.10.3",
    37. "less-loader": "5.0.0",
    38. "mini-css-extract-plugin": "^0.8.0",
    39. "optimize-css-assets-webpack-plugin": "^5.0.3",
    40. "postcss-loader": "^3.0.0",
    41. "react-redux": "^8.1.3",
    42. "redux": "^4.2.1",
    43. "redux-persist": "^6.0.0",
    44. "style-loader": "^1.0.1",
    45. "terser-webpack-plugin": "^2.2.2",
    46. "typescript": "^5.2.2",
    47. "url-loader": "^3.0.0",
    48. "webpack": "^4.41.2",
    49. "webpack-cli": "^3.3.10",
    50. "webpack-dev-server": "^3.9.0",
    51. "webpack-merge": "^4.2.2",
    52. "webpack-parallel-uglify-plugin": "^1.1.2"
    53. },
    54. "dependencies": {
    55. "antd": "^5.10.0",
    56. "lodash": "^4.17.15",
    57. "moment": "^2.24.0",
    58. "react": "^18.2.0",
    59. "react-dom": "^18.2.0"
    60. }
    61. }

    注意: babel相关插件最好是同一个数字版本开头的, 比如7.xx.xx, 因为页面报babel-loader编译问题, 提示是版本不一致

    另外因为我使用了redux, 复杂的项目可以使用它, 简单的项目context或者recoil就行了, 按需下载, 不需要的可以卸载

    3. react多页配置

    1) webpack.common.js拆分公共部分

    1. const path = require('path')
    2. const HtmlWebpackPlugin = require('html-webpack-plugin')
    3. const { srcPath, distPath } = require('./paths')
    4. module.exports = {
    5. entry: {
    6. index: path.join(srcPath, 'index.tsx'),
    7. other: path.join(srcPath, 'other.tsx')
    8. },
    9. resolve: {
    10. extensions: ['.tsx', '.ts', '.jsx', '.js']
    11. },
    12. module: {
    13. rules: [
    14. {
    15. test: /.(jsx?)|(ts?x?)$/,
    16. loader: ['babel-loader'],
    17. include: srcPath,
    18. exclude: /node_modules/
    19. },
    20. ]
    21. },
    22. plugins: [
    23. // 多入口 - 生成 index.html
    24. new HtmlWebpackPlugin({
    25. template: path.join(srcPath, 'index.html'),
    26. filename: 'index.html',
    27. // chunks 表示该页面要引用哪些 chunk (即上面的 indexother),默认全部引用
    28. chunks: ['index', 'vendor', 'common'] // 要考虑代码分割
    29. }),
    30. // 多入口 - 生成 other.html
    31. new HtmlWebpackPlugin({
    32. template: path.join(srcPath, 'other.html'),
    33. filename: 'other.html',
    34. chunks: ['other', 'common'] // 考虑代码分割
    35. })
    36. ]
    37. }

    2) webpack.dev.js:

    dev环境需要的webpack配置, 注意与webpack.common.js的逻辑进行合并

    1. const path = require('path')
    2. const webpack = require('webpack')
    3. const webpackCommonConf = require('./webpack.common.js')
    4. const { smart } = require('webpack-merge')
    5. const { srcPath, distPath } = require('./paths')
    6. module.exports = smart(webpackCommonConf, {
    7. mode: 'development',
    8. module: {
    9. rules: [
    10. // 直接引入图片 url
    11. {
    12. test: /\.(png|jpg|jpeg|gif)$/,
    13. use: 'file-loader'
    14. },
    15. {
    16. test: /\.css$/,
    17. // loader 的执行顺序是:从后往前
    18. loader: ['style-loader', 'css-loader', 'postcss-loader'] // 加了 postcss
    19. },
    20. {
    21. test: /\.less$/,
    22. // 增加 'less-loader' ,注意顺序
    23. loader: ['style-loader', 'css-loader', 'less-loader']
    24. }
    25. ]
    26. },
    27. plugins: [
    28. new webpack.DefinePlugin({
    29. // 'development'
    30. 'process.env': {
    31. NODE_ENV: JSON.stringify(process.env.NODE_ENV)
    32. }
    33. })
    34. ],
    35. devServer: {
    36. port: 8111,
    37. progress: true, // 显示打包的进度条
    38. contentBase: distPath, // 根目录
    39. open: true, // 自动打开浏览器
    40. compress: true, // 启动 gzip 压缩
    41. // 设置代理
    42. proxy: {
    43. // 将本地 /api/xxx 代理到 localhost:3000/api/xxx
    44. '/api': 'http://localhost:8000',
    45. // 将本地 /api2/xxx 代理到 localhost:3000/xxx
    46. '/api2': {
    47. target: 'http://localhost:8111',
    48. pathRewrite: {
    49. '/api2': ''
    50. }
    51. }
    52. }
    53. }
    54. })

    3) webpack.production.js:

    production环境需要的webpack配置, ,注意与webpack.common.js的逻辑进行合并

    1. const path = require('path')
    2. const webpack = require('webpack')
    3. const { smart } = require('webpack-merge')
    4. const { CleanWebpackPlugin } = require('clean-webpack-plugin')
    5. const MiniCssExtractPlugin = require('mini-css-extract-plugin')
    6. const TerserJSPlugin = require('terser-webpack-plugin')
    7. const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
    8. const webpackCommonConf = require('./webpack.common.js')
    9. const { srcPath, distPath } = require('./paths')
    10. module.exports = smart(webpackCommonConf, {
    11. mode: 'production',
    12. output: {
    13. // filename: 'bundle.[contentHash:8].js', // 打包代码时,加上 hash 戳
    14. filename: '[name].[contentHash:8].js', // name 即多入口时 entry 的 key
    15. path: distPath,
    16. // publicPath: 'http://cdn.abc.com' // 修改所有静态文件 url 的前缀(如 cdn 域名),这里暂时用不到
    17. },
    18. module: {
    19. rules: [
    20. // 图片 - 考虑 base64 编码的情况
    21. {
    22. test: /\.(png|jpg|jpeg|gif)$/,
    23. use: {
    24. loader: 'url-loader',
    25. options: {
    26. // 小于 5kb 的图片用 base64 格式产出
    27. // 否则,依然延用 file-loader 的形式,产出 url 格式
    28. limit: 5 * 1024,
    29. // 打包到 img 目录下
    30. outputPath: '/img1/',
    31. // 设置图片的 cdn 地址(也可以统一在外面的 output 中设置,那将作用于所有静态资源)
    32. // publicPath: 'http://cdn.abc.com'
    33. }
    34. }
    35. },
    36. // 抽离 css
    37. {
    38. test: /\.css$/,
    39. loader: [
    40. MiniCssExtractPlugin.loader, // 注意,这里不再用 style-loader
    41. 'css-loader',
    42. 'postcss-loader'
    43. ]
    44. },
    45. // 抽离 less
    46. {
    47. test: /\.less$/,
    48. loader: [
    49. MiniCssExtractPlugin.loader, // 注意,这里不再用 style-loader
    50. 'css-loader',
    51. 'less-loader',
    52. 'postcss-loader'
    53. ]
    54. }
    55. ]
    56. },
    57. plugins: [
    58. new CleanWebpackPlugin(), // 会默认清空 output.path 文件夹
    59. new webpack.DefinePlugin({
    60. 'process.env': {
    61. NODE_ENV: JSON.stringify(process.env.NODE_ENV)
    62. }
    63. }),
    64. // 抽离 css 文件
    65. new MiniCssExtractPlugin({
    66. filename: 'css/main.[contentHash:8].css'
    67. })
    68. ],
    69. optimization: {
    70. // 压缩 css
    71. minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
    72. // 分割代码块
    73. splitChunks: {
    74. chunks: 'all',
    75. /**
    76. * initial 入口 chunk,对于异步导入的文件不处理
    77. async 异步 chunk,只对异步导入的文件处理
    78. all 全部 chunk
    79. */
    80. // 缓存分组
    81. cacheGroups: {
    82. // 第三方模块
    83. vendor: {
    84. name: 'vendor', // chunk 名称
    85. priority: 1, // 权限更高,优先抽离,重要!!!
    86. test: /node_modules/,
    87. minSize: 0, // 大小限制
    88. minChunks: 1 // 最少复用过几次
    89. },
    90. // 公共的模块
    91. common: {
    92. name: 'common', // chunk 名称
    93. priority: 0, // 优先级
    94. minSize: 0, // 公共模块的大小限制
    95. minChunks: 2 // 公共模块最少复用过几次
    96. }
    97. }
    98. }
    99. }
    100. })

    4) 可以再加一个webpack.config.js文件:

    根据环境判断是使用webpack.dev.js还是webpack.production.js

    1. const prodConfig = require('./webpack.prod.js');
    2. const webpackConfig = process.env.NODE_ENV !== 'development' ? prodConfig : () => import('./webpack.dev.js');
    3. module.exports = webpackConfig;

    5) 在package.json添加如下命令:

    1. "scripts": {
    2. "test": "echo \"Error: no test specified\" && exit 1",
    3. "dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.js",
    4. "buil:debv": "cross-env NODE_ENV=development webpack --config build/webpack.config.js",
    5. "build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js"
    6. },
    cross-env改变process.env.NODE_ENV的值, 在控制台可以打印出来

    console.log(process.env.NODE_ENV);

    4. 新建index.html, index.tsx, other.html, other.tsx文件

    index.tsx文件

    1. // index.tsx
    2. import React from 'react'
    3. import ReactDOM from 'react-dom/client'
    4. /**
    5. * 不加后缀.tsx的配置: 在module.exports对象中追加=>
    6. * resolve: {
    7. extensions: ['.js', '.jsx', '.tsx', '.ts']
    8. },
    9. */
    10. import App from './App';
    11. const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
    12. // v18 的新方法
    13. root.render(<App />)

    index.html

    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
    7. <title>webpack demo</title>
    8. </head>
    9. <body>
    10. <p>webpack demo</p>
    11. <div id="root"></div>
    12. </body>
    13. </html>

    other.tsx和other.html差不多一样按react要求写, 写出差异页面能够看出是不同页面就行

    5. 新建.babelrc文件

    1. {
    2. "presets": [
    3. "@babel/preset-react",
    4. "@babel/preset-typescript",
    5. ],
    6. "plugins": []
    7. }

    6. 新建postcss.config.js文件

    1. module.exports = {
    2. plugins: [require('autoprefixer')]
    3. }

    7. 新建tsconfig文件

    1. {
    2. "compilerOptions": {
    3. "target": "es5",
    4. "lib": [
    5. "dom",
    6. "dom.iterable",
    7. "esnext"
    8. ],
    9. "allowJs": true,
    10. "skipLibCheck": true,
    11. "esModuleInterop": true,
    12. "allowSyntheticDefaultImports": true,
    13. "strict": true,
    14. "forceConsistentCasingInFileNames": true,
    15. "module": "esnext",
    16. "moduleResolution": "node",
    17. "resolveJsonModule": true,
    18. "isolatedModules": true,
    19. "noEmit": true,
    20. "jsx": "react-jsx",
    21. "noImplicitAny": false
    22. },
    23. "include": [
    24. "src"
    25. ]
    26. }

    5. Test组件

    1. import React, { useEffect, useState } from "react";
    2. import _ from "lodash";
    3. const Test = () => {
    4. const [data, setData] = useState({ a: "hello world" });
    5. useEffect(() => {
    6. setData({ a: "gggg" });
    7. console.log("process", process.env.NODE_ENV, process.env);
    8. }, []);
    9. return <div>{_.get(data, "a")}</div>;
    10. };
    11. export default Test;

    6. 在App.tsx组件中引入Test组件测试效果

    1. import React, { Suspense, lazy } from "react";
    2. import { Spin } from "antd";
    3. import Test from "./Test";
    4. const App: React.FC = () => {
    5. return (
    6. <Suspense fallback={<Spin />}>
    7. <Test />
    8. </Suspense>
    9. );
    10. };
    11. export default App;

    index.html效果图如下:

    other.html效果图如下:

  • 相关阅读:
    【SQL刷题】DAY16----SQL高级联结专项练习
    资本+商业模式+中国制造的出海跨境电商,走向世界!(Starday)
    设计模式-原型模式
    我的NVIDIA开发者之旅——CUDA编程基础——并行矩阵乘法
    七天接手react项目 系列 —— state&事件处理&ref
    Java项目防止SQL注入的几种方案
    1.1 异步相关概念:初步了解
    Golang goroutine MPG模式浅析
    面试现场!月薪3w+的这些数据挖掘SQL面试题你都掌握了吗? ⛵
    Hive学习(待续)
  • 原文地址:https://blog.csdn.net/qq_42750608/article/details/133774421