使用webpack搭建react项目

1,639 阅读6分钟

在之前有一篇使用react搭建vue项目的文章,现在来说一下用webpack搭建react项目,按照我个人来说,搭建react项目比vue项目坑多一点。下面的文章会一一说明,废话不多说,直接开干

react官方有脚手架,为什么还要用webpack搭建一个?这不是找麻烦嘛,自己搭建一个对于提高自己的技术,了解一些底层的技术也很有帮助,没准以后面试的时候正好问到了呢!,刚开始搭建的时候也踩坑不少

第一步老样子还是要在文件夹里面npm init -y初始化项目

一般工具安装都是-D;-S是我们从开发到上线一直都要用到的

1、工程目录

我的工程目录结构是这样的,按照个人开发习惯
下面的例子用到的都是main.js,我个人真正开发用到的是app.js,哈哈哈不要纠结那么多

在这里插入图片描述

dist:打包输出文件夹
pubilc中的index.html:入口html文件
assets:存放图片和icon的
components:存放封装的公共组件
utils:存放全局封装的js文件
views:页面相关文件
app.js:全局js文件,配置相关

2、新建工程目录和相关文件

  • 根目录下新建src文件夹用来存放项目文件;新建public文件夹并在里面新建index.html作为入口html文件
  • 根目录下新建main.js作为webpack打包的入口文件

在这里插入图片描述

3、安装webpack相关

  • 安装webpack:npm i webpack -D
  • 安装webpack-cli:npm i webpack-cli -D
  • 根目录下新建webpack.config.js,然后的简单的配置一下

​ 如果我们不配置webpack.config.js直接在终端输入webpack,这时候会报错的,如下图所示

在这里插入图片描述

然后我们来简单的配置一下。这里我们看见用的是module.exports来向外暴漏一个对象。那么用export default可以吗,当然是不可以的,因为webpack是基于node开发的,第二种是ES6的语法,node识别不了,那么webpack也识别不了

在这里插入图片描述

注意:在webpack4.x以后,用pink老师的话说就是:约定大于配置。目的是为了减少配置文件的体积

这时候我们在终端输入webpack会看见就不报错了,dist文件夹里面也有一个main.js

在这里插入图片描述

在这里提醒一下大家,不建议安装webpack5.x版本的,原因是:配置的时候及容易出现不兼容报错的问题,坑太多;而且现在4.x稳的一批

  • 安装webpack-dev-serve:npm install webpack-dev-server --save-dev 

现在安装下来的是最新的5.x的版本,在run跑起来的时候会报错,是因为版本不兼容的导致的,把webpack的版本安装为4.x的即可,非常不建议安装到5.x的版本。更换为4.x的版本后,重新run一下脚本,哇靠,运行成功了,仔细一看怎么和我想象中的不一样,而且我在main.js中打印的数据也不一样

在这里插入图片描述

在这里插入图片描述

右键查看源代码的时候,看到main.js是在根目录下的一个dist文件夹里面的,这是为啥?因为webpack-dev-serve打包好的main.js是托管在内存中的,所以在根目录中看不到

在index.html中我们引用的main.js路径是写死的,所以一直都是以前的文件,改变一下路径即可,或者直接不用引用,交给一个plugin插件处理下面会说 到,看一下修改后的

在这里插入图片描述

在这里插入图片描述

  • 配置运行后自动打开浏览器,一般我都是不配置,因为太懒了!!!只在后面加上运行脚本只需要在 package.json的script部分的运行脚本中加入如下代码即可

--open:是自动打开浏览器,后面加上相应的浏览器名字就能打开不同的浏览器,比如:--open firefox

--port 3000:代表运行在3000的端口

--hot:实现热重载

--progress:代表进度

--compress:代表走网络的时候压缩(数据)

--host:指定端口号,一般默认的都是localhost的端口,–host+空格+后面加上端口号就可以改变端口号:--host 129.0.0.1

4、安装html-webpack--plugin

现在运行成功后浏览器上显示的不是我们想要的内容,这时候需要借助一个插件:html-webpack-plugin,首先用npm安装一下,安装的时候注意思 webpack4.x和5.x安装的命令是不一样,可以去npm网站看一下,我的webpack是4.x的,所以用:npm i --save-dev html-webpack-plugin,他能帮助我们把HTML页面生成到内存中

在这里插入图片描述

使用方法也很简单,可以去看webpack的官方文档,首先在webpack配置文件创建一个plugins节点,在节点中new一下那个插件,可以传递两个参数:template、filename

webpack官方文档---tml-webpack-pluginwebpack.js.org/plugins/htm…

npm官方文档---tml-webpack-pluginwww.npmjs.com/package/htm…

  • template:是根据谁生成模板,源文件
  • ​filename:生成的目标文件的名称

在这里插入图片描述

重新跑一下项目看看效果,ok完美

加粗样式

这时候我们右键查看源代码看到多了一个main.js的引用,这个是插件帮我们生成的,所以在上面index.html的引用去掉就可以了,毕竟有时候我自己写着写着难免不出现问题

在这里插入图片描述

5、安装react、react-dom

1、安装命令:npm i react react-dom -S

2、main.js导入

在这里插入图片描述

先来渲染一个最基本的div看看

在这里插入图片描述

在这里插入图片描述

6、安装babel相关

使用过react的都知道平时项目中可不是这样一个个创建的,那这样就能累死人啊,假设我们现在用class的方式创建一个组件会怎么样呢??

在这里插入图片描述

他告诉我们应该有一个loader来处理这种type,就要用到babel来处理这种语法,webpack只能打包处理js文件

下面就来安装一下:npm install babel-loader@8 @babel/core @babel/preset-env @babel/runtime @babel/plugin-transform-runtime @babel/plugin-proposal-class-properties @babel/preset-react -D,这里具体的就不解释了,可以去百度,反正就是转换成webpack可以识别的,看个人情况也不用全部都安装

配置babel

在webpack.config.js中添加module节点并进行配置,也可以新建.babelrc文件来配置,我更喜欢第一种,因为就不用新建文件了(懒!!!)(文章底部有完整配置)

//模块配置规则
    module: {
        //第三方匹配规则(多个loader的话要写成数组)
        rules: [
            { test: /\.js|jsx$/, exclude: /node_modules/, use: 'babel-loader' }//在配置babel-loader的时候一定要加上exclude,否则项目会报错跑不起来
        ]
    }

根目录新建.babelrc文件(考虑到更多人,这里还是说一下新建文件配置的方式)

在这里插入图片描述

这时候重新run一下看看,ok完美

在这里插入图片描述

7、配置路径、别名

作为一个懒人,这是我必须配置,就能少些一些代码了

在webpack.config.js中新增resolve节点,写入如下代码

resolve: {
        //省略后缀名
        extensions: ['*', '.js', '.jsx', '.json'],
        // 配置路径别名
        alias: {
            '@assets': path.resolve('./src/assets'),
            '@views': path.resolve('./src/views'),
            '@utils': path.resolve('./src/utils'),
            '@components': path.resolve('./src/components')
        },
    }

现在我们引入文件只需要这样写就行了

在这里插入图片描述

8、react中使用css、scss;css模块化

如果你样式写的都是内联的话,这一步可以不看,应该很少有人吧~~~~~~~~~~~

完整的css配置及模块化请看最底部的附录,那里的很完整,这部分介绍如何使用和部分的配置

sass的配置下面会讲到

webpack css处理流程: ​ 

  1. 当webpack打包到某个文件的时候看到有后缀名为.css的文件,这时候webapck识别不了他会拿着该文件到rules里面找第三方的loader ​ 

  2. 当找到有这样的loader的时候会从最后一个loader开始处理,也就是css-loader,当css-loader处理完后会看前面还有没有其他的loader ​ 这时候前面还有一个style-loader,会给他做第二次处理这时候再去前面找有没有其他的loader,有的话再交给前面的处理 ​ 

  3. 当最后一个loader处理完的时候就会交给webpack做打包合并

安装相关loader

npm i style-loader css-loader -D

配置css-loader

在这里插入图片描述

css模块化

我们也可以不使用sass,直接将css模块化,万一到时候用的第三方的UI框架怎么办,直接在rules里面加一个exclude就可以了 ​ 

在这里使用css模块化的时候,首先安装一个插件:mini-css-extract-plugin:npm install --save-dev mini-css-extract-plugin这个插件主要是其他样式格式转成css ,抽离css用的 ​ 然后我们实例化一下这个插件;filename是我们要输出的css文件地址

const MiniCssPlugin = new MiniCssExtractPlugin({
        filename: './css/common.css'
})

在webpack.config.js文件的plugins节点导入

plugins: [
        HtmlPlugin, MiniCssPlugin
],

使用的时候看下图 ,将module设置为true就可以开启模块化

**注意:这个插件不能和style-loader一起使用、不能为sass和less文件使用,主要就是给css文件使用的;最好放在首位**

在这里插入图片描述

 rules: [
            { test: /\.js|jsx$/, exclude: /node_modules/, use: 'babel-loader' },//在配置babel-loader的时候一定要加上exclude,否则项目会报错跑不起来
            { test: /\.css$/, exclude: /node_modules/, use: [MiniCssExtractPlugin.loader, {loader: 'css-loader',
                options: {
                    modules: true,
                    importLoaders: 1
                }
            }]},

            //打包处理字体、图片文件的loader
            { test: /\.ttf|woff|woff2|eot|svg|png|jpg|gif$/, use: 'url-loader' },
        ]

9、安装url-loader、file-loader

安装这两个loader可以帮助我们处理字体文件和图片文件

安装: npm i url-loader file-loader -D

在moudle------>rules节点里面进行配置

//打包处理字体、图片文件的loader
 { test: /\.ttf|woff|woff2|eot|svg|png|jpg|gif$/, use: 'url-loader' }

10、安装、配置sass

sass-loader内部是依赖node-sass的,安装命令建议去使用npm官网的 先安装node-sass:npm install node-sass; 然后安装sass-loader:npm install sass-loader sass webpack --save-dev ​现在运行一下:npm run start,这时候发现终端报错了(这里选用的是less报错,scss报错的图片找不到了,不过都一样

在这里插入图片描述

报错信息告诉我们getOptions这么个东西不是一个方法,出现这个的原因就是由于sass-loader版本过高导致的,换一下版本号,建议安装8.x以下的版本,还有node-sass也是,因为默认安装最新版本的,刚开始默认安装了一个5.x,终端也是报错并提示最好安装4.x的

在这里插入图片描述

在这里插入图片描述

下面的配置我没有对css进行模块化,因为以后项目使用UI框架地时候引入的都是css文件,为了以后不出什么问题我还是搞一下sass吧,我们自己的文件就是.scss结尾的,UI框架的就是.css 下方postcss-loader去webpack官方看一下文档吧,文档写的很详细,这里就不说了

webpack文档:webpack.js.org/loaders/pos…

npm文档:www.npmjs.com/package/pos…

rules: [
	{
        test: /\.scss$/,
        use: ['style-loader', {
          loader: 'css-loader',
          options: {
            modules: true,
            importLoaders: 1,
          }
        }, 'sass-loader']
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              importLoaders: 1,
            }
          },
          {
            loader: 'postcss-loader',
            options: {
            //配置选项和插件
              postcssOptions: {
                plugins: [
                  autoprefixer(["last 2 version", "> 1%", "not ie < 11"]),
                  pxtorem({
                    rootValue: 16,
                    unitPrecision: 5,
                    propList: ["font", "font-size", "line-height", "letter-spacing", "width", "height", "margin", "padding"],
                    selectorBlackList: [],
                    replace: true,
                    mediaQuery: false,
                    minPixelValue: 0,
                    exclude: /node_modules/i
                  })
                ]
              }
            }
          }
        ]
      }
    ]

10、打包测试

现在我们需要的都已经配置完了,打个包试一下,走起~~~

我项目中已经有一个dist文件夹,虽然我不知道是干啥的,没事,将最后的打包文件换一个

在这里插入图片描述

package.json中写上打包的命令

"build": "webpack --progress --config webpack.config.js"

在这里插入图片描述

给我飞起来!!!!!~~~~~~

在这里插入图片描述

一般情况下我们的打包文件都是dist文件夹,可以看一下本篇最后面的目录,里面有非常完整的配置,我已经将outPutDist改为dist文件夹了,打包出来也是一样,里面也都是一些我们打包后的html、js、css文件文件

在这里插入图片描述

在这里插入图片描述

static文件夹里面放的是我们的css和一些图片文件,我们借助了mini-css-extract-plugin这个插件将css合成了一个

至此,打包也已经完成了

11、附录(一定要看!!!)

  1. 有时候安装的依赖太多了,一个个安装也是费死劲,将全部用到的依赖和部分的webpack配置放在这里;到时候直接复制过去,然后直接npm install就可以,方便大家使用 
  2. 里面安装了ts相关的loader,不用ts的可以不用安装,直接用js就可以 
  3. @babel/plugin-proposal-class-properties"和@babel/plugin-transform-runtime不用安装也可以 
  4. 配置babel的时候我是没有使用创建.babelrc文件来配置的,太麻烦了直接在webpack配置文件里面就可以

package.json完整代码

{
  "name": "react-cli",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --colors --progress --profile",
    "start": "webpack-dev-server"
  },
  "devDependencies": {
    "@babel/core": "^7.12.13",
    "@babel/plugin-proposal-class-properties": "^7.12.13",
    "@babel/plugin-transform-runtime": "^7.12.15",
    "@babel/preset-env": "^7.12.13",
    "@babel/preset-react": "^7.12.13",
    "@babel/runtime": "^7.12.13",
    "autoprefixer": "^10.0.2",
    "babel-loader": "^8.2.2",
    "terser-webpack-plugin": "^2.2.1",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^5.0.1",
    "file-loader": "^6.2.0",
    "html-webpack-plugin": "^4.5.0",
    "mini-css-extract-plugin": "^1.3.1",
    "node-sass": "^4.14.1",
    "postcss": "^8.1.9",
    "postcss-loader": "^4.1.0",
    "postcss-pxtorem": "^5.1.1",
    "sass": "^1.32.8",
    "ts-loader": "^8.0.12",
    "typescript": "^4.1.3",
    "sass-loader": "^7.3.1",
    "style-loader": "^2.0.0",
    "url-loader": "^4.1.1",
    "webpack": "^4.46.0",
    "webpack-cli": "^3.2.1",
    "webpack-dev-server": "^3.1.14"
  },
  "dependencies": {
    "antd": "^4.12.3",
    "axios": "^0.21.0",
    "core-js": "^3.7.0",
    "qs": "^6.9.4",
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "react-router-dom": "^5.2.0"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

                

webpack.config.js完整代码

clean-webpack-plugin:是用来清空dist文件的

postcss-pxtorem:是px自动转rem的一个插件,推荐和postcss-loader一起使用

 autoprefixer:一个自动管理浏览器前缀的插件 

terser-webpack-plugin:打包自动去除console.log、打包优化

const path = require('path')
const HtmlWebpackPlugin = require("html-webpack-plugin")
const autoprefixer = require("autoprefixer")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const { CleanWebpackPlugin} = require("clean-webpack-plugin")
const pxtorem = require("postcss-pxtorem")
const TerserPlugin = require('terser-webpack-plugin')

module.exports = {
  mode: 'production',
  entry: path.join(__dirname, './app.js'),
  output: {
    path: path.join(__dirname, 'dist'),
    filename: "static/js/bundle.js",
  },
  //loader配置相关
  module: {
    rules: [{
        // js文件、babel配置
        test:  /\.js|jsx$/,
        exclude: [
          path.resolve(__dirname, 'node_modules')
        ],
        use: [{
          loader: 'babel-loader',
          options: {
            babelrc: false,
            presets: [
              require.resolve('@babel/preset-react'),
              [require.resolve('@babel/preset-env'), {
                "useBuiltIns": "usage",
                "corejs": 3,
                modules: false
              }]
            ],
            cacheDirectory: true
          }
        }]
      },
      //css、scss和模块化相关配置
      {
        test: /\.scss$/,
        use: ['style-loader', {
          loader: 'css-loader',
          options: {
            modules: true,
            importLoaders: 1,
          }
        }, 'sass-loader']
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              importLoaders: 1
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  autoprefixer(["last 2 version", "> 1%", "not ie < 11"]),
                  pxtorem({
                    rootValue: 16,
                    unitPrecision: 5,
                    propList: ["font", "font-size", "line-height", "letter-spacing", "width", "height", "margin", "padding"],
                    selectorBlackList: [],
                    replace: true,
                    mediaQuery: false,
                    minPixelValue: 0,
                    exclude: /node_modules/i
                  })
                ]
              }
            }
          }
        ]
      },
      // 文件配置
      {
        test: /\.(png|svg|jpg|gif|ttf|woff|woff2|eot)$/,
        use: {
          loader: "url-loader",
          options: {
            esModule: false,
            name: "static/images/[hash:8].[name].[ext]",
            limit: 3 * 1024
          }
        }
      }
    ]
  },
  // 路径别名配置
  resolve: {
    extensions: ['.json', '.ts', '.tsx','.js', '.jsx'],
    alias: {
      "@images": path.resolve("./src/assets"),
      "@css": path.resolve("./src/css"),
      "@views": path.resolve("./src/views"),
      "@components": path.resolve("./src/components"),
      "@utils": path.resolve("./src/utils")
    }
  },
  //插件配置
  plugins: [
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({
      filename: "static/css/[name].css",
      chunkFilename: "static/css/[id].css"
    }),
    new HtmlWebpackPlugin({
      template: "./public/index.html",
      filename: "index.html"
    })
  ],
  //打包优化配置
  optimization: {
    minimizer: [
        new TerserPlugin({
            cache: true,
            sourceMap: true,
            terserOptions: {
                comments: false,
                compress: {
                    unused: true,
                    drop_console: true,
                    drop_debugger: true,
                    dead_code: true
                }
            },
            parallel: true,
        })
    ]
  }
  //服务器代理配置
  // devServer: {
  //   host: "192.168.15.28",
  //   port: "3001",
  //   historyApiFallback: true,
 	   //proxy: { //代理
          //'http://106.53.151.120:8888/user-info/
          //'/user-info' : {
            //target: 'http://132.232.223.165:8888/',
            // changeOrigin:true,
            //secure:false,
            //pathRewrite: { '^/user-info': '' }
          //}
        //}
  // }
}

App.js(也就是main.js)代码

这里代码可以很简洁也可以直接写antd的组件,我就不新建文件然后引入了,直接在这里面写入

import React from 'react'
import ReactDOM from 'react-dom'
import { HashRouter, Route, Link } from 'react-router-dom'
import { Layout, Menu } from 'antd'

//导入组件
import Home from './src/views/Home'
import About from './src/views/About'
import 'antd/dist/antd.css'


const { Header, Content, Footer } = Layout


ReactDOM.render(
    <HashRouter>
        <Layout style={{width:'100vw',height:'100%',flex:1}}>
          <Header className="header" style={{display:'flex',alignItems:'center'}}>
            <Menu style={{flex:1}} theme="dark" mode="horizontal">
              <Menu.Item key="Home">
                <Link to="/Home">首页</Link>
              </Menu.Item>
              <Menu.Item key="About">
                <Link to="/About">关于</Link>
              </Menu.Item>
            </Menu>
          </Header>
          <Layout>
            <Layout style={{ padding: '0 24px 24px' }}>
              <Content className="site-layout-background" style={{  margin: 0, minHeight: 280, }}>
                <Route path="/Home" component={Home}></Route>
                <Route path="/About" component={About}></Route>
              </Content>
              <Footer style={{ textAlign: 'center' }}>webpack—react-cli</Footer>
            </Layout>
          </Layout>
        </Layout>
      </HashRouter>,
    document.getElementById('app')
)