使用webpack搭建React项目开发环境

ByteRhapsody
• 阅读 3737

前言

在日常开发中,我们通常会选择脚手架vue-clicreate-react-app等来帮助开发。可是了解并掌握webpack来完成项目工程化开发却是一门基本功

本文基于:
webpack: 4.41.2
node: 10.15.1
yarn: 1.16.0

webpack基本配置

1.1 初始化React项目

  • 在新建目录下

    yarn init 
  • 安装React相关包

    yarn add react react-dom axios
  • 安装babel
    在开发中,我们经常使用ES6+的语法,但是并非所有浏览器都有将ES6+语法转换成javascript的能力,所以这就是我们为何使用babel的原因,babel能将ES6+的语法转换为兼容的JS语法

    • 安装babel的核心包和命令行工具

        yarn add @babel/core
        yarn add @babel/cli 
    • 安装babel的预置配置和语法插件

       yarn add @babel/preset-env 支持ES6语法  
       yarn add @babel/preset-react 支持React语法  
       yarn add @babel/plugin-proposal-class-properties  支持类属性  
    • 编辑.babelrc 使安装的插件和配置生效

      {
          "presets": [
             [
                "@babel/preset-env",
                {
                   "useBuiltIns": "entry",
                   "corejs": 3
                }
             ],
              "@babel/preset-react"
           ],
           "plugins": [
              "@babel/plugin-proposal-class-properties"
           ]
      }
    • 安装babel-polyfill
      babel默认只转换语法,但不转换对象。可开发时由于原浏览器没 有例如Promise、Symbol这些对象的语法,我们需要使用时,就得对babel的配置做些调整
      我们可以使用babel-polyfill来解决这一问题。其原理是自己实现了ES6+的所有对象和实例方法,但缺点是污染了全局空间和内置对象,可能会出现同名对象冲突

        yarn add @babel/polyfill

1.2 使用webpack

  • 安装

    yarn add webpack webpack-cli
  • 新建src/index.js 作为全局入口

    import "@babel/polyfill"  // 放在开头
    import React from 'react'
    import ReactDOM from 'react-dom'
    import axios from 'axios'
  • 创建webpack.config.js

    const path = require('path');
    
    module.exports = {
        entry: './src/index.js',  // 入口文件
        output: {  // 出口文件(编译后)
            filename: 'bundle.js',
            path: path.resolve(__dirname, 'dist')
        },
        // babel-loader将根据.babelrc去处理文件
        module: {
            rules: [
                {test: /\.js$/, use: 'babel-loader'}
            ]
        }
    };
  • yarn add babel-loader

1.3 集成webapck-dev-server

  • yarn add webapck-dev-server 快速搭建本地运行环境
  • yarn add html-webpack-plugin在内存中生成html
  • 修改package.json

    "scripts": {
        "dev": "webpack-dev-server --open --port 8081 --contentBase src --hot",
        "build": "webpack --mode production",
        "test": "echo \"Error: no test specified\" && exit 1"
     },
  • 修改webpack.config.js

    const path = require('path');
    const htmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
        entry: path.join(__dirname, './src/index.js'),
        output: {
            filename: 'bundle.js',
            path: path.resolve(__dirname, 'dist')
        },
        plugins: [
            new htmlWebpackPlugin({
                template: path.join(__dirname, './index.html'),
                filename: 'index.html'
            })
        ],
        module: {
            rules: [
                {test: /\.js$/, use: 'babel-loader'}
            ]
        },
        mode: 'development' 
    };
  • 在集成webpack-dev-server、http-webpack-plugin后,访问localhost:8081(在内存中的html页面)对于在html中引入css、图片、less、字体等文件会报错,需要另外处理

    • yarn add less less-loader css-loader style-loader file-loader
    • 修改webpack.config.js

      rules: [
         {test: /\.js$/, use: 'babel-loader'}, 
         {test: /\.css$/, use: ['style-loader', 'css-loader']},
         {test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader']},
         {test: /\.(eot|svg|ttf|woff|woff2|png)\w*/, use: 'file-loader'}
      ]
    • 修改index.html

      <script src="bundle.js"></script>

1.4 配置React路由

  • yarn add react-router react-router-dom
  • 在配置了react-router后,无法通过输入地址或刷新子路由访问页面
    原因: 我们通过react-router配置的只是前端路由,但webpack不会识别前端路由,在服务端匹配不到路径后,会将其自动转到404界面
    解决:在webpack.config.js中配置:

    // webpack在匹配不到对应路径后,会默认返回首页
    // 这时将由前端路由控制
    devServer: {
        historyApiFallback: true,
    }

1.5 区分开发环境和生产环境

  • webpack.config.js中对开发模式和生产模式做区分:

    const path = require('path');
    
    module.exports = function(env, argv){
        // 1. 通过 argv 判断是开发还是生产环境
        const isEnvDevelopment = argv.mode === 'development' || !argv.mode;
        const isEnvProduction = argv.mode === 'production'; 
        
        return{
            // 2. mode和devtool根据环境做不同配置
            mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
            devtool: isEnvProduction ? 'source-map' : isEnvDevelopment && 'cheap-module-source-map',
            entry: './src/index.js',
            output: {
                filename: 'bundle.js',
                path: path.resolve(__dirname, 'dist')
            },
            module: {
                rules: [{
                    test: /\.js$/,
                    exclude: /node_modules/,
                    use: 'babel-loader'
                }]
            }
        }
    }

1.6 开发配置

  • Source Map:将目标代码映射到原始代码
    经过打包器后,浏览器执行的是目标代码。而在开发中,出错时希望报的错能直接定位到原始代码中,而不是显示目标代码,这时我们需要配置 source map

    const path = require('path');
    const htmlWebpackPlugin = require('html-webpack-plugin');
    const webpack = require('webpack');
    
    module.exports = function(env, argv){
        const isEnvDevelopment = argv.mode === 'development' || !argv.mode;
        const isEnvProduction = argv.mode === 'production'; 
        
        return{
            mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development', 
            devtool: isEnvProduction ? 'source-map' : isEnvDevelopment && 'cheap-module-source-map',  // 修改
            entry: path.join(__dirname, './src/index.js'),
            output: {
                filename: 'bundle.js',
                path: path.resolve(__dirname, 'dist'),
            },
            plugins: [
                new htmlWebpackPlugin({
                    template: path.join(__dirname, './public/index.html'),
                    filename: 'index.html'
                })
            ],
            module: {
                rules: [
                   {test: /\.js$/, use: 'babel-loader'}, 
                   {test: /\.css$/, use: ['style-loader', 'css-loader']},
                   {test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader']},
                   {test: /\.(eot|svg|ttf|woff|woff2|png)\w*/, use: 'file-loader'}
                ]
            },
            devServer: {
                historyApiFallback: true,
                contentBase: './dist',
                hot: true
            }
        }
    }
  • Eslint 与 Prettier

    • Eslint的作用是检查代码是否符合规范,并进行提示
    • Prettier一般配合ESlints使用,ESlints能检测出代码是否规范,但不能完全统一代码风格,而Prettier统一了代码风格,但不能检测代码规范
    • yarn add eslint babel-eslint
    • yarn add eslint-config-airbnb
    • yarn add eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react
    • 创建.eslintrc.js

      module.exports = {
          // parser用于指定解析器,eslint本身无法解析es6语法,需要转换成babel-eslint
          "parser": "babel-eslint", 
          // extends表示以airbnb为基础规范 
          "extends": ["airbnb"], 
          // env告诉eslint,如果碰到browser和es6的对象,不用报undefined
          "env": {  
              browser: true, 
              es6: true, 
           }, 
           // rules用于指定扩展规范规则,会覆盖airbnb规则中的配置 
          "rules": {
              'react/jsx-filename-extension': [1, {extensions: ['.js']}],
              'react/prop-types': 0,
              'react/prefer-stateless-function': 0,  
              'react/no-array-index-key': 0, 
              'no-console': 0, 
              'jsx-ally/anchor-is-valid': 0, 
              'react/destructuring-assignment': 0,
              'react/jsx-one-expression-per-line': 0  
          }
      }
    • 手动执行eslint
      yarn eslint src/ 检查src/下的js文件
      npx eslint --fix src/ 自动修复不规范,只列出那些无法自动修复的
  • 配置路径别名
    在开发中,我们经常会配置路径别名,快速定位文件,提高效率和文件引入的准确度,通常是用 @ 来表示 src目录

    resolve: {
       alias: {
         '@': path.resolve('src')
       }
    }
  • 模块化CSS

    • 单纯使用style-loader、css-loader,虽然可以在JS中引入CSS样式文件,但随之出现的问题是:

      • 全局污染:CSS文件中的选择器是全局的,不同文件的同名选择器,根据build后生成文件中的先后顺序,后面的样式会覆盖前面的样式
      • 选择器复杂,命名规则混乱
    • 而使用CSS Module模块化处理后,针对每一个JS文件,其引入的CSS文件都只对该文件生效,这有点类似vue中scoped属性起到的作用
    • 修改webpack.config.js

      {test: /\.css$/, use: ['style-loader', 'css-loader?modules']},
    • 使用示例:
      使用webpack搭建React项目开发环境
    • CSS Module的原理:对每个类名按照一定规则进行转换(加上hash值),保证其唯一性,但是只对className和id能进行转换
  • CSS兼容性处理
    使CSS能针对不同浏览器添加不同的前缀,解决浏览器兼容问题

    • yarn add postcss-loader autoprefixer
    • 配置postcss.config.js

      module.exports = {
          plugins: {
              'autoprefixer': {}
          }
      };
    • 修改webpack.config.js

      // postcss-loader要放在less-loader前,先从less转为css,再用postcss处理
      module: {
          rules: [
              {test: /\.js$/, exclude: /node_modules/, enforce: "pre", use: 'babel-loader'}, 
              {test: /\.css$/, include: [path.resolve(__dirname, 'src/styles'), /node_modules/], use: ['style-loader', 'css-loader', 'postcss-loader']},
              {test: /\.css$/, exclude: [path.resolve(__dirname, 'src/styles'), /node_modules/], use: ['style-loader', 'css-loader?modules', 'postcss-loader']},
              {test: /\.less$/, include: [path.resolve(__dirname, 'src/styles'), /node_modules/], use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader']},
              {test: /\.less$/, exclude: [path.resolve(__dirname, 'src/styles'), /node_modules/], use: ['style-loader', 'css-loader?modules', 'postcss-loader', 'less-loader']},
              {test: /\.(eot|svg|ttf|woff|woff2|png)\w*/, use: 'file-loader'},     
              {
                  test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.svg$/],
                  loader: 'url-loader',
                  options: {
                      limit: 10000
                  }
              }
          ],
      },

1.7 生产环境配置

  • 复制webpack.config.js,重命名为webpack.config.prod.js
  • 编辑package.json

    "scripts": {
        "dev": "webpack-dev-server --port 8081 --mode development",
        "build": "webpack --config webpack.prod.config.js --mode production",
        "watch": "webpack --watch",
        "test": "echo \"Error: no test specified\" && exit 1"
    },
  • 修改webpack.prod.js
    删除devServer配置

结尾

到此,我们可以使用以下命令:
yarn run dev 开发环境下启动项目,在端口8081访问
yarn run build 打包编译项目文件,生成dist目录
至于一些webpack的优化配置,如:代码分离、文件体积压缩、配置缓存等以及开发环境中使用HMR模块热更新则不再过多介绍,或者后续将会继续更新

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
可莉 可莉
3年前
18个常用 webpack插件,总会有适合你的!
!(https://oscimg.oschina.net/oscnet/71317da0c57a8e8cf5011c00e302a914609.jpg)来源| https://github.com/Michaellzg/myarticle/blob/master/webpack/Plugin何为插
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这