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

前言

在日常开发中,我们通常会选择脚手架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']},
      
    • 使用示例:
    • 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模块热更新则不再过多介绍,或者后续将会继续更新

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值