react全家桶环境的搭建

305 阅读4分钟

前言

公司目前正在启动一个新项目,之前用的架构整体来说版本都比较低,出于时间空余,周末来公司加加班,自己整理一套新的东西,目前项目还没启动,有些不足之处还在优化,现在分享出来,大家互相学习,有什么不足之处,望指出! 这次新的项目使用react全家桶,主要是webpack4 react16.8 数据状态管理用的redux-saga 数据交互这块主要用的就是axios

开始表演

打开终端 创建项目目录 mkdir my_app 生成package文件: npm init -y

webpack篇

首先我们需要安装我们的主角webpack相关依赖 && react相关依赖

yarn add react react-dom react-router-dom webpack webpack-cli webpack-dev-server 
or
npm i react react-dom react-router-dom webpack webpack-cli webpack-dev-server --save-dev

配置webpack公共配置文件

在根目录新建webpack.basic.config.js 此文件主要配置 入口文件 entry 输出文件:output 以及我们的loader 和 plugins; 我们开始安装所需要用到的loader包

yarn add babel-core babel-loader babel-plugin-import babel-plugin-transform-runtime babel-preset-latest babel-preset-react babel-preset-stage-0 style-loader url-loader
less less-loader

我们来开始写入口代码

entry: {
   bundle: path.resolve(__dirname, './src/main.js')
 },

出口代码:

output: {
   path: path.resolve(__dirname, './build'),
   filename: '[name].[hash].js',
   chunkFilename: '[name].chunk.[hash:5].js',
   publicPath: '/'
 },

配置loader

module: {
    rules: [
      {
        test: /\.js|jsx$/,
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader'
        }
      },
      {
        test: /\.(less|css)$/,
        include: path.resolve(__dirname, './src/components'),
        use: [
          miniCssExtractPlugin.loader, // webpack4.x
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              modules: true,
              camelCase: true,
              localIdentName: '[path][name]__[local]--[hash:base64:5]'
            }
          }, // react css module
          'resolve-url-loader',
          'px2rem-loader', 'postcss-loader', 'less-loader'
        ]
      },
      {
        test: /\.(less|css)$/,
        include: path.resolve(__dirname, './src/css'),
        use: [miniCssExtractPlugin.loader, 'css-loader', 'px2rem-loader', 'postcss-loader', 'less-loader']
      },
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
              name: 'images/[hash].[ext]' // 所有图片在一个目录
            }
          }
        ]
      }
    ]
  },

抽离公共文件:

optimization: {
    // 抽离webpack runtime到单文件
    runtimeChunk: 'single',
    splitChunks: {
      chunks: 'all',
      // 最大初始请求数量
      maxInitialRequests: Infinity,
      // 抽离体积大于80kb的chunk
      minSize: 80 * 1024,
      // 抽离被多个入口引用次数大于等于1的chunk
      minChunks: 1,
      cacheGroups: {
        // 抽离node_modules下面的第三方库
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          // 从模块的路径地址中获得库的名称
          name (module, chunks, chacheGroupKey) {
            const packageName = module.context.match(
              /[\\/]node_modules[\\/](.*?)([\\/]|$)/
            )[1];
            return `vendor_${packageName.replace('@', '')}`;
          }
        }
      }
    }
},

安装插件yarn add mini-css-extract-plugin clean-webpack-plugin 配置插件

plugins: [
    // eslint-disable-next-line new-cap
    new miniCssExtractPlugin({
      filename: '[name].[contentHash].css',
      chunkFilename: '[id].[contentHash].css'
    }),
    new webpack.DefinePlugin({// 设置成production去除警告
      'process.env': {
        NODE_ENV: JSON.stringify('production')
      }
    }),
    new CleanWebpackPlugin(['dist',
      'build'], {
      root: __dirname,
      verbose: true,
      dry: false,
      exclude: ['jslibs']
    })
  ]

webpack.basic.config.js完整代码

const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const miniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  entry: {
    bundle: path.resolve(__dirname, './src/main.js')
  },
  output: {
    path: path.resolve(__dirname, './build'),
    filename: '[name].[hash].js',
    chunkFilename: '[name].chunk.[hash:5].js',
    publicPath: '/'
  },
  module: {
    rules: [
      {
        test: /\.js|jsx$/,
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader'
        }
      },
      {
        test: /\.(less|css)$/,
        include: path.resolve(__dirname, './src/components'),
        use: [
          miniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
              modules: true,
              // namedExport: true, // this is  invalid Options ,I find it
              camelCase: true,
              localIdentName: '[path][name]__[local]--[hash:base64:5]'
            }
          }, // react css module
          'resolve-url-loader', // may need this (https://www.npmjs.com/package/resolve-url-loader)
          'px2rem-loader', 'postcss-loader', 'less-loader'
        ]
      },
      {
        test: /\.(less|css)$/,
        include: path.resolve(__dirname, './src/css'),
        use: [miniCssExtractPlugin.loader, 'css-loader', 'px2rem-loader', 'postcss-loader', 'less-loader']
      },
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
              name: 'images/[hash].[ext]' // 所有图片在一个目录
            }
          }
        ]
      }
    ]
  },
  performance: {
    hints: false
  },
  optimization: {
    // 抽离webpack runtime到单文件
    runtimeChunk: 'single',
    splitChunks: {
      chunks: 'all',
      // 最大初始请求数量
      maxInitialRequests: Infinity,
      // 抽离体积大于80kb的chunk
      minSize: 80 * 1024,
      // 抽离被多个入口引用次数大于等于1的chunk
      minChunks: 1,
      cacheGroups: {
        // 抽离node_modules下面的第三方库
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          // 从模块的路径地址中获得库的名称
          name (module, chunks, chacheGroupKey) {
            const packageName = module.context.match(
              /[\\/]node_modules[\\/](.*?)([\\/]|$)/
            )[1];
            return `vendor_${packageName.replace('@', '')}`;
          }
        }
      }
    }
  },
  plugins: [
    // eslint-disable-next-line new-cap
    new miniCssExtractPlugin({
      filename: '[name].[contentHash].css',
      chunkFilename: '[id].[contentHash].css'
    }),
    new webpack.DefinePlugin({// 设置成production去除警告
      'process.env': {
        NODE_ENV: JSON.stringify('production')
      }
    }),
    new CleanWebpackPlugin(['dist',
      'build'], {
      root: __dirname,
      verbose: true,
      dry: false,
      exclude: ['jslibs']
    })
  ]
};

以上公共配置文件已经完成,我们后续要对生产环境和开发环境进行配置 首先我们来配置开发环境。通过webpack-merge来合并公共配置

const path = require('path');
const merge = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const baseWebpackConfig = require('./webpack.base.config');
module.exports = merge(baseWebpackConfig, {
  mode: 'development',
  devServer: {
    host: 'localhost',
    port: 3334,
    contentBase: path.resolve(__dirname, './build'),
    historyApiFallback: true,
    compress: true
  },
  devtool: 'inline-source-map',
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: './index.html',
      inject: 'body'
    })
  ]
})

生产环境配置文件

const merge = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// const TerserWebpackPlugin = require("terser-webpack-plugin");
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");
const baseWebpackConfig = require('./webpack.base.config');

module.exports = merge(baseWebpackConfig, {
  mode: 'production',
  // 对线上代码压缩
  optimization: {
    minimizer: [
      new OptimizeCssAssetsWebpackPlugin(),
      // new TerserWebpackPlugin({ sourceMap: true }),
      new HtmlWebpackPlugin({
        filename: 'index.html',
        template: './index.html',
        inject: 'body',
        minify: {
          collapseWhitespace: true,
          removeComments: true,
          removeAttributeQuotes: true
        }
      })
    ]
  }
});

以上我们所有的webpack文件都配置完成了,下面我们开始搭建项目结构,集成redux-saga

搭建项目结构

目录结构

我们需要安装我们react redux所需要的依赖

yarn add react react-css-modules react-dom react-redux react-router-dom redux redux-saga axios

main.js

import React from 'react'
// eslint-disable-next-line semi
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import { Provider } from 'react-redux'
import store from './redux/store/store';
import { app } from './app';
import '../src/css/app.less';

const render = Component => {
  ReactDOM.render(
    <AppContainer>
      <Provider store={store}>
        <Component />
      </Provider>
    </AppContainer>,
    document.getElementById('root')
  )
}

render(app)

// Webpack Hot Module Replacement API
if (module.hot) {
  module.hot.accept('./app', () => {
    render(app)
  })
}

app.js

import React from 'react';
import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom';
import Bundle from './components/bundle';
// 按需加载
const Home = (props) => (
  <Bundle load={() => import('./pages/home')}>
      {(Home) => <Home {...props}/>}
  </Bundle>
);

export const app = () => (

  <Router>
    <Switch>
      <Route exact path="/home" component={Home} ></Route>

      <Redirect to='home'/>
    </Switch>
  </Router>

)

按需加载 bundle.js

import { Component } from 'react';
export default class Bundle extends Component {
  constructor (props) {
    super(props);
    this.state = {
      mod: null
    };
  }

  componentWillMount () {
    this.load(this.props)
  }

  componentWillReceiveProps (nextProps) {
    if (nextProps.load !== this.props.load) {
      this.load(nextProps)
    }
  }

  load (props) {
    this.setState({
      mod: null
    });
    // 注意这里,使用Promise对象; mod.default导出默认
    props.load().then((mod) => {
      this.setState({
        mod: mod.default ? mod.default : mod
      });
    });
  }

  render () {
    return this.state.mod ? this.props.children(this.state.mod) : null;
  }
}

以上代码就不一一列出了,大家可以进入我的仓库直接观看,谢谢! github.com/long-joan/w…