手摸手教你搭建一个react脚手架

409 阅读2分钟

初始化项目

$ npm init

webpack配置

  • 创建基于webpack4.42.1版本
$ yarn add webapck webapck-cli webpack-dev-server -D

通过yarn add <package...> [--dev/-D] 会安装到devDependencies对象下。

通过yarn add <package...>会安装到dependencies对象下。

在这里解释下devDependenciesdependencies

devDependencies对象下通常是构建工具需要的包,比如:loader,plugin等。

dependencies是需要打包到静态文件的包,比如:react,lodash等。

新建webpack基础配置文件

  • 在根目录创建 build 文件夹,webpack.base.conf.js 文件
  • 在根目录创建 src 文件夹,添加 main.js 文件
// webpack.base.conf.js 文件
const path = require('path');
// 处理路径
function resolve(dir) {
    return path.join(__dirname, '..', dir)
}
module.exports = {
        // 入口
        entry: {
        	app: "./src/main.js",
    	},
    	// 出口
        output: {
            filename: "[name].js",
            path: "dist"
        },
    	resolve: {
    		// https://webpack.js.org/configuration/resolve/#resolveextensions
            extensions: ['.js', '.jsx','.tsx','.json'],
            // https://webpack.js.org/configuration/resolve/#resolvealias
            alias: {
                '@': resolve('src'),
                'public': resolve('public'),
            }
    },
};

不同环境的配置

这里我们需要 webpack-merge包来合并不同环境的配置

$ yarn add webpack-merge -D 

在根目录创建build文件夹,添加生产环境 webpack.prod.conf.js 文件与开发环境webpack.dev.conf.js 文件

// webpack.prod.conf.js 文件
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf');
module.exports = merge(baseWebpackConfig, {
    mode: 'production'
});
// webpack.dev.conf.js 文件
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf');
module.exports = merge(baseWebpackConfig, {
    mode: 'development'
});

安装React

$ yarn add react react-dom
  • 在src目录下的main.js添加代码如下
import React from "react";
import ReactDom from "react-dom";

ReactDom.render(
    <h1>hello, world!</h1>,
    document.getElementById("root")
);

添加loader与plugin

babel

$ yarn add @babel/core @babel/preset-env @babel/preset-react babel-loader -D
  • 根目录创建.babelrc文件,配置presets
{
    "presets": [
        [
            "@babel/preset-env",
            {
                "targets": {
                    "browsers": [
                        "> 1%",
                        "last 5 versions",
                        "ie >= 8"
                    ]
                }
            }
        ],
        [
            "@babel/preset-react"

        ]
    ],
}
  • 修改webpack.base.conf.js文件
// webpack.base.conf.js 文件
const path = require('path');
// 处理路径
function resolve(dir) {
    return path.join(__dirname, '..', dir)
}
module.exports = {
        // 入口
        entry: {
        	app: "./src/main.js",
    	},
    	// 出口
        output: {
            filename: "[name].js",
            path: "dist"
        },
    	resolve: {
    		// https://webpack.js.org/configuration/resolve/#resolveextensions
            extensions: ['.js', '.jsx','.tsx','.json'],
            // https://webpack.js.org/configuration/resolve/#resolvealias
            alias: {
                '@': resolve('src'),
                'public': resolve('public'),
            },
        module: {
            rules: [
                {
                    test: /\.js?$/,
                    use: "babel-loader",
                    include: resolve('src')
                }
               ]
    	}  
    },
};

css

$ yarn add less less-loader css-loader mini-css-extract-plugin postcss-loader -D

mini-css-extract-plugin已经支持HMR了,所以不再需要style-loader做开发模式兼容;

  • 根目录下创建 postcss.config.js
// postcss.config.js
module.exports = {
    plugins: {
        'autoprefixer': {
            overrideBrowserslist: [
                "Android 4.1",
                "iOS 7.1",
                "Chrome > 31",
                "ff > 31",
                "ie >= 8"
            ]
        }
    }
}
// webpack.base.conf.js 文件
const path = require('path');
// 处理路径
function resolve(dir) {
    return path.join(__dirname, '..', dir)
}
module.exports = {
        // 入口
        entry: {
        	app: "./src/main.js",
    	},
    	// 出口
        output: {
            filename: "[name].js",
            path: "dist"
        },
    	resolve: {
    		// https://webpack.js.org/configuration/resolve/#resolveextensions
            extensions: ['.js', '.jsx','.tsx','.json'],
            // https://webpack.js.org/configuration/resolve/#resolvealias
            alias: {
                '@': resolve('src'),
                'public': resolve('public'),
            },
        module: {
            rules: [
                {
                    test: /\.js?$/,
                    use: "babel-loader",
                    include: resolve('src')
                },
                {
                    test: /\.css$/,
                    use: [
                      {
                        loader: MiniCssExtractPlugin.loader,
                        options: {
                          // only enable hot in development
                          hmr: true,
                          // if hmr does not work, this is a forceful method.
                          reloadAll: true
                        }
                      },
                      "css-loader",
                      "postcss-loader"
                    ]
              	},
               {
                    test: /\.less$/,
                    include: [resolve("src"), resolve("node_modules/antd")],
                    use: [
                      {
                        loader: MiniCssExtractPlugin.loader,
                        options: {
                          // only enable hot in development
                          hmr: true,
                          // if hmr does not work, this is a forceful method.
                          reloadAll: true
                        }
                      },
                      {
                        loader: "css-loader" // translates CSS into CommonJS
                      },
                      {
                        loader: "less-loader", // compiles Less to CSS      
                      }
                    ]
              	},
                {
                    test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
                    use: [
                        {
                            loader: 'url-loader',
                            options: {
                                limit: 4096,
                                fallback: {
                                    loader: 'file-loader',
                                    options: {
                                        name: 'media/[name].[hash:8].[ext]'
                                    }
                                }
                            }
                        }
                    ]
                },
                {
                    test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
                    use: [
                        {
                            loader: 'url-loader',
                            options: {
                                limit: 4096,
                                fallback: {
                                    loader: 'file-loader',
                                    options: {
                                        name: 'fonts/[name].[hash:8].[ext]'
                                    }
                                }
                            }
                        }
                    ]
                },
                {
                    test: /\.(png|jpe?g|gif|webp)(\?.*)?$/,
                    use: [
                        {
                            loader: 'url-loader',
                            options: {
                                limit: 4096,
                                fallback: {
                                    loader: 'file-loader',
                                    options: {
                                        name: 'images/[name].[hash:8].[ext]'
                                    }
                                }
                            }
                        }
                    ]
                },
                {
                    test: /\.(svg)(\?.*)?$/,
                    use: [
                        {
                            loader: 'file-loader',
                            options: {
                                name: 'img/[name].[hash:8].[ext]'
                            }
                        }
                    ]
                },
          	]
    	}  
    },
};

静态文件

$ yarn add url-loader file-loader -D
// webpack.base.conf.js 文件
const path = require('path');
// 处理路径
function resolve(dir) {
    return path.join(__dirname, '..', dir)
}
module.exports = {
        // 入口
        entry: {
        	app: "./src/main.js",
    	},
    	// 出口
        output: {
            filename: "[name].js",
            path: "dist"
        },
    	resolve: {
    		// https://webpack.js.org/configuration/resolve/#resolveextensions
            extensions: ['.js', '.jsx','.tsx','.json'],
            // https://webpack.js.org/configuration/resolve/#resolvealias
            alias: {
                '@': resolve('src'),
                'public': resolve('public'),
            },
        module: {
            rules: [
                {
                    test: /\.js?$/,
                    use: "babel-loader",
                    include: resolve('src')
                },
                {
                    test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
                    use: [
                        {
                            loader: 'url-loader',
                            options: {
                                limit: 4096,
                                fallback: {
                                    loader: 'file-loader',
                                    options: {
                                        name: 'media/[name].[hash:8].[ext]'
                                    }
                                }
                            }
                        }
                    ]
                },
                {
                    test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
                    use: [
                        {
                            loader: 'url-loader',
                            options: {
                                limit: 4096,
                                fallback: {
                                    loader: 'file-loader',
                                    options: {
                                        name: 'fonts/[name].[hash:8].[ext]'
                                    }
                                }
                            }
                        }
                    ]
                },
                {
                    test: /\.(png|jpe?g|gif|webp)(\?.*)?$/,
                    use: [
                        {
                            loader: 'url-loader',
                            options: {
                                limit: 4096,
                                fallback: {
                                    loader: 'file-loader',
                                    options: {
                                        name: 'images/[name].[hash:8].[ext]'
                                    }
                                }
                            }
                        }
                    ]
                },
                {
                    test: /\.(svg)(\?.*)?$/,
                    use: [
                        {
                            loader: 'file-loader',
                            options: {
                                name: 'img/[name].[hash:8].[ext]'
                            }
                        }
                    ]
                },
          	]
    	}  
};

以上就是就是base文件所有配置;

配置dev文件

$ yarn add html-webpack-plugin -D
  • 根目录下创建public文件夹,创建一个 index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>从零开始搭建react脚手架</title>
</head>
<body>
    <div id="root"></div>
</body>
</html>
  • 开启 webpack-dev-server
const path = require("path");
const merge = require("webpack-merge");
const baseWebpackConfig = require("./webpack.base.conf.js");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require("webpack");
const config = require("../config");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");


const devWebpackConfig = merge(baseWebpackConfig, {
  mode: "development",
  output: {
    filename: "js/[name].[hash:16].js"
  },
  devtool: "source-map",
  devServer: {
        port: '8080',
        contentBase: path.join(__dirname, '../public'),
        compress: true,
        historyApiFallback: true,
        hot: true,
        https: false,
        noInfo: true,
        open: true,
        proxy: {}
   },
  plugins: [
    new HtmlWebpackPlugin({
      inject: true,
      template: "public/index.html",
      inject: "body",
      minify: {
        html5: true
      }
    }),
    new MiniCssExtractPlugin({
      // 生成对应的css文件
      filename: "[name].css",
      chunkFilename: "[id].css",
      ignoreOrder: true
    }),
    // 文件更改后重新页面
    new webpack.HotModuleReplacementPlugin(),
  ]
});

以上就完成了开发环境的配置

配置prod文件

$ yarn add optimize-css-assets-webpack-plugin clean-webpack-plugin terser-webpack-plugin uglifyjs-webpack-plugin -D

生产模式下通常都是配置些压缩文件的插件,有兴趣的童鞋可以到npm查查这些包的使用

const config = require('../config')
const webpack = require('webpack');
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf.js');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

module.exports = merge(baseWebpackConfig, {
    mode: 'production',
    output: {
        filename: "js/[name].[chunkhash:16].js",
        chunkFilename: "js/[id].[chunkhash].js",
        path: "dist",
    },
    optimization: {
        // 压缩文件
        minimizer: [new TerserJSPlugin({}), new OptimizeCSSPlugin({})],
    },
    plugins: [
        // 压缩JS 
        new UglifyJsPlugin({
            uglifyOptions: {
                warnings: false
            },
            parallel: true
        }),
        new HtmlWebpackPlugin({
            template: 'public/index.html',
            inject: 'body',
            minify: {
                removeComments: true,
                collapseWhitespace: true,
                removeAttributeQuotes: true
            },
        }),
        // 该插件会根据模块的相对路径生成一个四位数的hash作为模块id
        new webpack.HashedModuleIdsPlugin(),
        // 这个插件会在 webpack 中实现以上的预编译功能。
        new webpack.optimize.ModuleConcatenationPlugin(),
        new MiniCssExtractPlugin({
            filename: "css/[name].[hash:7].css",
            chunkFilename: "css/[id].[hash:7].css",
            ignoreOrder:true
        }),
        // 删除旧dist目录
        new CleanWebpackPlugin(),
    ]
});

以上就完成了生产环境的配置

配置 package.json

// package.json
"scripts": {
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
    "build": "webpack --config build/webpack.prod.conf.js",
},

运行 yarn dev 本地调试 react代码,运行 yarn build 可对项目进行打包了;