手摸手教你搭建一個(gè)react腳手架

初始化項(xiàng)目

$ npm init

webpack配置

  • 創(chuàng)建基于webpack4.42.1版本
$ yarn add webapck webapck-cli webpack-dev-server -D

通過yarn add <package...> [--dev/-D] 會(huì)安裝到devDependencies對(duì)象下瞻想。

通過yarn add <package...>會(huì)安裝到dependencies對(duì)象下膏蚓。

在這里解釋下devDependenciesdependencies

devDependencies對(duì)象下通常是構(gòu)建工具需要的包霜威,比如:loader,plugin等呛牲。

dependencies是需要打包到靜態(tài)文件的包,比如:react,lodash等狼速。

新建webpack基礎(chǔ)配置文件

  • 在根目錄創(chuàng)建 build 文件夾,webpack.base.conf.js 文件
  • 在根目錄創(chuàng)建 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'),
            }
    },
};

不同環(huán)境的配置

這里我們需要 webpack-merge包來(lái)合并不同環(huán)境的配置

$ yarn add webpack-merge -D 

在根目錄創(chuàng)建build文件夾向胡,添加生產(chǎn)環(huán)境 webpack.prod.conf.js 文件與開發(fā)環(huán)境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
  • 根目錄創(chuàng)建.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已經(jīng)支持HMR了惊完,所以不再需要style-loader做開發(fā)模式兼容;

  • 根目錄下創(chuàng)建 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]'
                            }
                        }
                    ]
                },
            ]
        }  
    },
};

靜態(tài)文件

$ 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
  • 根目錄下創(chuàng)建public文件夾僵芹,創(chuàng)建一個(gè) 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({
      // 生成對(duì)應(yīng)的css文件
      filename: "[name].css",
      chunkFilename: "[id].css",
      ignoreOrder: true
    }),
    // 文件更改后重新頁(yè)面
    new webpack.HotModuleReplacementPlugin(),
  ]
});

以上就完成了開發(fā)環(huán)境的配置

配置prod文件

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

生產(chǎn)模式下通常都是配置些壓縮文件的插件,有興趣的童鞋可以到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
            },
        }),
        // 該插件會(huì)根據(jù)模塊的相對(duì)路徑生成一個(gè)四位數(shù)的hash作為模塊id
        new webpack.HashedModuleIdsPlugin(),
        // 這個(gè)插件會(huì)在 webpack 中實(shí)現(xiàn)以上的預(yù)編譯功能拇派。
        new webpack.optimize.ModuleConcatenationPlugin(),
        new MiniCssExtractPlugin({
            filename: "css/[name].[hash:7].css",
            chunkFilename: "css/[id].[hash:7].css",
            ignoreOrder:true
        }),
        // 刪除舊dist目錄
        new CleanWebpackPlugin(),
    ]
});

以上就完成了生產(chǎn)環(huán)境的配置

配置 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",
},

運(yùn)行 yarn dev 本地調(diào)試 react代碼,運(yùn)行 yarn build 可對(duì)項(xiàng)目進(jìn)行打包了凿跳;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末件豌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子拄显,更是在濱河造成了極大的恐慌苟径,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件躬审,死亡現(xiàn)場(chǎng)離奇詭異棘街,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)承边,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門遭殉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人博助,你說我怎么就攤上這事险污。” “怎么了富岳?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵蛔糯,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我窖式,道長(zhǎng)蚁飒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任萝喘,我火速辦了婚禮淮逻,結(jié)果婚禮上琼懊,老公的妹妹穿的比我還像新娘。我一直安慰自己爬早,他們只是感情好哼丈,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著筛严,像睡著了一般醉旦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上脑漫,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天髓抑,我揣著相機(jī)與錄音,去河邊找鬼优幸。 笑死吨拍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的网杆。 我是一名探鬼主播羹饰,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼碳却!你這毒婦竟也來(lái)了队秩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤昼浦,失蹤者是張志新(化名)和其女友劉穎馍资,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體关噪,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鸟蟹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了使兔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片建钥。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖虐沥,靈堂內(nèi)的尸體忽然破棺而出熊经,到底是詐尸還是另有隱情,我是刑警寧澤欲险,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布镐依,位于F島的核電站,受9級(jí)特大地震影響天试,放射性物質(zhì)發(fā)生泄漏槐壳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一秋秤、第九天 我趴在偏房一處隱蔽的房頂上張望宏粤。 院中可真熱鬧,春花似錦灼卢、人聲如沸绍哎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)崇堰。三九已至,卻和暖如春涩咖,著一層夾襖步出監(jiān)牢的瞬間海诲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工檩互, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留特幔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓闸昨,卻偏偏與公主長(zhǎng)得像蚯斯,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子饵较,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容