若是你還不知道什麼是React
,請點擊這裏
github源碼javascript
若是你還不知道什麼是ECMAScript
,請點擊這裏css
若是你還不知道什麼是Node.js
,請點擊這裏html
下載Node.js並安裝;接着打開windows
命令行窗口分別輸入node -v
及npm -v
以下圖所示,node
和npm
均顯示出版本號則表示安裝成功!
java
若是你還不知道什麼是npm
,請點擊這裏node
在你想要放置該項目代碼的任何地方新建一個文件夾,並命名爲react-template
,接着打開該文件夾路徑下windows
命令行窗口輸入npm init
,接着根據提示依次輸入:react
package name
:項目名稱(默認是文件夾名稱)version
:版本號(默認是1.0.0)description
:項目描述entry point
:項目入口文件(默認是index.js)test command
:測試命令git repository
:git
遠程倉庫地址keywords
:項目關鍵詞author
:做者license
:開源許可聲明
初始化完成後,項目根目錄會自動生成package.json
文件,這就是項目的npm
配置文件了。webpack
若是你還不知道什麼是webpack
,請點擊這裏,而且本人強烈建議把該頁內容耐心讀完。git
webpack
是一個靜態資源模塊打包器,而且webpack
支持多種不一樣的模塊系統,咱們主要用到如下三個:es6
首先,給package.json
文件寫入兩個屬性:github
這兩個屬性都是用來維護項目的本地依賴包列表的,可是devDependencies
比較特殊,它只是開發環境的依賴,當構建生產環境代碼時,這些包的代碼會被捨去。
接着,給devDependencies
寫入webpack
的依賴:
鍵值對:key
爲包名,value
爲版本號
{ ... "devDependencies": { "webpack": "^4.12.0", "webpack-cli": "^3.0.8" ... } }
命令行npm install
或npm i
,這個命令會根據dependencies
和devDependencies
的配置去檢查是否全部的依賴包都在本地安裝了,若沒有則會安裝對應的包到本地。
若是你對npm
命令行不瞭解,能夠看這裏
若是你對npm
的「全局」和「本地」的概念不是很清楚,例如:上文提到的本地依賴包,能夠看這裏
安裝成功後,項目根目錄下會生成一個node_modules
文件夾,它就是本地依賴包的倉庫,你能夠在它的裏面找到包webpack
和webpack-cli
。
特別地,還須要全局安裝webpack
,不然命令行窗口認不到 webpack 的命令。
$ npm i -g webpack webpack-cli
新建react-template/src
文件夾,並在裏面新建文件index.js
新建react-template/build
文件夾,接着在build
文件夾裏再新建兩個文件:
webpack.base.js
:基礎配置文件(開發和生產共用)webpack.dev.js
:自動化開發環境的配置文件webpack.pro.js
:構建生產環境代碼配置文件關於webpack
配置的詳細內容,請看這裏
配置文件一般是一個CommonJS
規範的模塊,輸出一個JavaScript Object
// __dirname表示當前目錄,path.resolve()能夠防止不一樣操做系統之間的文件路徑問題,而且可使相對路徑按照預期工做 module.exports = { /** * 項目入口文件 */ entry: path.resolve(__dirname, '../src/index.js') // ...省略其它選項 }
module.exports = { //... /** * 指定打包後的 bundle 如何輸出 * 特別說明: * 1. bundle是指多個模塊打包在一塊兒,產生的新文件。bundle 通常由html文件經過 script 標籤加載 */ output: { // 打包後的 bundle 的生成位置(E:/react-template/dist/) path: path.resolve(__dirname, "../dist/"), // 主 bundle(E:/react-template/dist/js/main.js) filename: "js/main.js", // chunk: 單獨拆分出來的 bundle,name即爲chunk的名稱 chunkFilename: "js/[name].js", // publicPath + chunkFilename 爲打包後生成的html文件請求 chunkFile 的路徑 // publicPath + 圖片的URL 爲打包後生成的html文件請求圖片的路徑,其餘靜態資源文件同理 publicPath: "/" } //... }
module.exports = { //... /** * 如何解析模塊路徑 */ resolve: { // 指定要解析的文件擴展名 extensions: [".web.js", ".jsx", ".js", ".json"], // 模塊路徑別名 alias: {} }, //... }
module.exports = { //... /** * 指定如何處理(編譯)各類類型的模塊 * 特別說明: * 1. webpack提供了豐富的針對不一樣類型模塊的loader,你可使用loader對模塊進行預處理或者對模塊的 * 源代碼進行轉換(編譯) * 2. 常見的模塊類型:js, jsx, css, scss, less, json, png, git, jpg */ module: { /** * 各類類型模塊的處理規則 * 特別說明: * 1. use屬性表示模塊使用什麼loader * 2. 模塊可使用多個loader,處理順序爲use屬性的數組的第一個到最後一個 */ rules: [ // 圖片文件小於8192byte時,轉換爲base64字符串 { test: /\.(gif|png|jpg|jpeg|woff|woff2|eot|ttf|svg)(\?t=\d+)?$/, exclude: /node_modules/, use: ["url-loader?limit=8192"] }, /** * 將js和jsx模塊的源代碼編譯成瀏覽器能正常執行的代碼 * 特別說明: * 1. eslint是一個代碼檢查工具,中文官網:https://cn.eslint.org/ * 通常咱們會在項目根目錄下爲eslint建立一個配置文件 .eslintrc.json ,關於eslint * 的配置,祥見:附錄/eslint-loader配置文件 * 2. babel是一個JavaScript編譯器,它可以將瀏覽器還沒有實現的新一代的ES語法轉換成瀏覽器 * 已實現的語法,好比咱們如今普遍使用的es6和部分es7語法和新的內置對象,其實瀏覽器並沒 * 有徹底實現,可是有了babel,咱們徹底能夠放心使用它們。 * 3. 通常咱們會在項目根目錄下爲babel建立一個配置文件 .babelrc ,關於babel的配置,詳 * 見:附錄/babel-loader配置文件 */ { enforce: "pre", test: /\.(js|jsx)?$/, exclude: /node_modules/, use: [{ loader: 'eslint-loader', options: { emitError: true, emitWarning: true, failOnError: true } }] }, { test: /\.(js|jsx)?$/, exclude: /node_modules/, use: ["babel-loader"] }, /** * 處理css模塊 * loader說明: * 1. style-loader 將css文件以 * <link rel="stylesheet" href="path/to/file.css"> * 的形式插入到html文件 * 2. css-loader 處理css的 @import語句 與 url() ,同時壓縮代碼 * 3. postcss-loader 對css作一些加工處理,具體的配置放在postcss.config.js,好比給 * css自動添加瀏覽器廠商前綴。若是不知道css瀏覽器廠商前綴的,請自行百度。 */ { test: /\.(css)?$/, use: [ "style-loader/url", { loader: "css-loader", options: { minimize: { safe: true, discardComments: { removeAll: true } } } }, "postcss-loader" ] }, /** * 處理less模塊 * 特別說明: * 1. Less 是一門 CSS 預處理語言,它擴展了 CSS 語言,增長了變量、Mixin、函數等特性, * 使 CSS 更易維護和擴展。 * 2. Less中文網:http://lesscss.cn/ */ { test: /\.less$/, use: ["style-loader", "css-loader", "less-loader"] }, /** * 處理scss模塊 * 特別說明: * 1. sass與less相似,也是一門css預處理語言。 */ { test: /\.scss$/, exclude: /node_modules/, use: [ 'style-loader', 'css-loader', 'postcss-loader', 'sass-loader' ] } ] } //... }
module.exports = { //... /** * 外部擴展 * 有時候你可能不想把某個第三方 library 打包進你的 package 裏,而是但願 library 作爲外部依賴; * 好比經過 script 標籤來加載此 library , externals 這個選項能夠幫到你。 */ externals: { "react": { commonjs: 'React', commonjs2: 'React', amd: 'React', root: 'React' }, "react-dom": { commonjs: 'ReactDOM', commonjs2: 'ReactDOM', amd: 'ReactDOM', root: 'ReactDOM' } } //... }
module.exports = { //... /** * 優化 */ optimization: { /** * 代碼拆分 * 從入口文件開始,webpack 遞歸地構建了整個應用的模塊依賴圖表(dependency graph),而後通 * 常會將全部的模塊打包成一個 bundle。可是有兩種狀況須要把一些模塊拆分紅單獨的 bundle: * 1. 經過 import() 函數導入的模塊,這些模塊不會被打包進主 bundle 裏,而是拆分爲單獨的 * bundle,等待 import() 函數執行,再去異步加載。 * import() 函數介紹:https://juejin.im/entry/58ba3308a22b9d005ede7565 * 2. 有的模塊因爲被多個不一樣的 bundle 依賴,因此這幾個 bundle 裏都會有該模塊的代碼;這時就 * 須要將這種模塊也單獨拆分出來,避免重複加載相同的模塊。 */ splitChunks: { chunks: "all", minSize: 30000, minChunks: 2, maxAsyncRequests: 5, maxInitialRequests: 3, automaticNameDelimiter: "~", name: true, cacheGroups: { default: false, vendor: { name: "vendor", priority: 99 } } } } }
以上就是webpack
的基礎配置了。咱們還須要把配置中用到的loader
及相關的模塊下載到本地。
其中,因爲:
babel-loader
依賴如下列出的package
:
eslint-loader
依賴如下列出的package
:
less-loader
依賴 less
postcss-loader
依賴 postcss
把上面列出的package
寫入package.json
的dependencies
和devDependencies
{ "dependencies": { "babel-runtime": "^6.26.0" }, "devDependencies": { "autoprefixer": "^9.1.5", "babel-core": "^6.26.3", "babel-eslint": "^9.0.0", "babel-loader": "^7.1.5", "babel-plugin-external-helpers": "^6.22.0", "babel-plugin-import": "^1.9.1", "babel-plugin-syntax-dynamic-import": "^6.18.0", "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-decorators-legacy": "^1.3.5", "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-env": "^1.7.0", "babel-preset-react": "^6.24.1", "css-loader": "^1.0.0", "eslint": "^5.6.0", "eslint-loader": "^2.1.1", "eslint-plugin-react": "^7.11.1", "file-loader": "^2.0.0", "less": "^3.8.1", "less-loader": "^4.1.0", "node-sass": "^4.9.0", "path": "^0.12.7", "postcss": "^6.0.22", "postcss-loader": "^3.0.0", "react-hot-loader": "^4.3.8", "sass-loader": "^7.0.3", "style-loader": "^0.23.0", "url-loader": "^1.1.1", "webpack": "^4.12.0", "webpack-cli": "^3.1.0", "webpack-merge": "^4.1.4" } }
接着
$ npm install
上一步的基礎配置都完成以後,實際上webpack
已經能夠正常工做了,你能夠寫一些測試模塊,而後在src/index.js
引入,用命令行運行webpack
$ webpack --config ./build/webpack.base.js --mode=development
可是這個過程是純手動的,咱們可使用一些webpack plugin
來讓某些動做自動化,以此來提升工做效率。
下面是webpack
開發模式的配置文件:
var base = require("./webpack.base.js"), // merge() 合併配置選項 merge = require('webpack-merge'), HtmlWebpackPlugin = require("html-webpack-plugin"), copyWebpackPlugin = require("copy-webpack-plugin"); module.exports = merge(base, { // 開發模式 mode: "development", devtool: "#cheap-module-eval-source-map", // webpack plugin -> https://webpack.docschina.org/plugins/ plugins: [ // 複製無需參與構建的文件到輸出位置 new copyWebpackPlugin([ { from: "src/js_modules/react/dev/react.js", to: "js/" }, { from: "src/js_modules/react-dom/dev/react-dom.js", to: "js/" }, { from: "img/**/*.*", to: "" } ]), // 自動在輸出位置建立html文件,並在html文件自動注入加載bundle的script標籤或link標籤 new HtmlWebpackPlugin({ filename: "index.html", template: "index.html", chunks: ["main", "vendor"], inject: true, chunksSortMode: "auto" }) ] });
光是能複製或自動建立文件還不夠,咱們但願在開發過程當中,當咱們修改代碼以後,webpack可以監聽變動的文件自動增量編譯,也但願瀏覽器能實時響應咱們的代碼(文件)變動,並自動變化或重載。要實現這個需求,咱們須要使用express
,並結合webpack-dev-middleware
和webpack-hot-middleware
來搭建一個開發服務器。順便提一句我我的比較推薦使用gulp來做爲項目工程化的流程管理工具,因此啓動開發服務器我是做爲一個 gulp任務 來編寫的,首先在根目錄建立一個gulpfile.js
,寫入下面的代碼:
var gulp = require("gulp"), gutil = require("gulp-util"), express = require("express"), webpack = require("webpack"), webpackDevMiddleware = require("webpack-dev-middleware"), webpackHotMiddleware = require("webpack-hot-middleware"), history = require("connect-history-api-fallback"), opn = require("opn"); // 開發服務器 gulp.task("dev", function() { var webpackDevConfig = require("./build/webpack.dev.js"); webpackDevConfig.entry = [ "webpack-hot-middleware/client?noInfo=true" ].concat([webpackDevConfig.entry]); webpackDevConfig.plugins = webpackDevConfig.plugins.concat([ new webpack.HotModuleReplacementPlugin() ]); var devCompiler = webpack(webpackDevConfig); var devMiddleware = webpackDevMiddleware(devCompiler, { publicPath: webpackDevConfig.output.publicPath, stats: { chunks: false, colors: true, timings: true, source: true, cachedAssets: false }, watchOptions: { ignored: /node_modules/, aggregateTimeout: 300, poll: true } }); var hotMiddleware = webpackHotMiddleware(devCompiler, { log: false }); var server = express(); server.use(history()); server.use(devMiddleware); server.use(hotMiddleware); server.listen(3008, function(err) { if (err) throw new gutil.PluginError("webpack-dev-server", err); opn("http://localhost:3008") }); });
把以上相關的package
寫入package.json
並npm install
{ "dependencies": { "babel-runtime": "^6.26.0", "react": "^16.4.1" }, "devDependencies": { "autoprefixer": "^9.1.5", "babel-core": "^6.26.3", "babel-eslint": "^9.0.0", "babel-loader": "^7.1.5", "babel-plugin-external-helpers": "^6.22.0", "babel-plugin-import": "^1.9.1", "babel-plugin-syntax-dynamic-import": "^6.18.0", "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-decorators-legacy": "^1.3.5", "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-env": "^1.7.0", "babel-preset-react": "^6.24.1", "connect-history-api-fallback": "^1.5.0", "copy-webpack-plugin": "^4.5.2", "css-loader": "^1.0.0", "eslint": "^5.6.0", "eslint-loader": "^2.1.1", "eslint-plugin-react": "^7.11.1", "express": "^4.16.3", "file-loader": "^2.0.0", "gulp": "^3.9.1", "gulp-sequence": "^1.0.0", "gulp-util": "^3.0.8", "html-webpack-plugin": "^3.2.0", "http-proxy-middleware": "^0.18.0", "less": "^3.8.1", "less-loader": "^4.1.0", "node-sass": "^4.9.0", "opn": "^5.3.0", "path": "^0.12.7", "postcss": "^6.0.22", "postcss-loader": "^3.0.0", "react-hot-loader": "^4.3.4", "sass-loader": "^7.0.3", "style-loader": "^0.23.0", "url-loader": "^1.1.1", "webpack": "^4.12.0", "webpack-cli": "^3.1.0", "webpack-dev-middleware": "^3.1.3", "webpack-hot-middleware": "^2.22.3", "webpack-merge": "^4.1.4" } }
而後在根目錄建立index.html
,內容以下:
<!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> </head> <body> <div id="app"></div> <script src="./js/react.js"></script> <script src="./js/react-dom.js"></script> </body> </html>
接着,編寫一個react
組件
// 文件路徑:react-template/src/components/myFirstComponent/myFirstComponent.jsx import { hot } from "react-hot-loader"; // @hot 能夠是react組件熱重載 @hot(module) class MyFirstComponent extends React.Component { state = { text: "Hello React" }; /** 組件生命週期鉤子函數:在組件掛載完成後當即被調用 */ componentDidMount() { console.log("組件掛載完成!"); } render() { return ( <div>{this.state.text}, I am {this.props.author}!</div> ) } } export default MyFirstComponent;
// 文件路徑:react-template/src/index.js import MyFirstComponent from "./components/myFirstComponent/myFirstComponent" ReactDOM.render(<MyFirstComponent author="Shaye"></MyFirstComponent>, document.getElementById("app"));
最後,打開CMD
:
$ gulp dev
廢話少說,直接上圖:
瀏覽器會自動打開:
你還能夠試着修改組件的內容,你會發現每當你按下保存鍵,瀏覽器會跟着實時變化
好了,前面折騰了這麼久,如今你能夠去開發你的react應用了。只不過嘛,如今你能自由地開發調試了,可是還不能構建產品包,因此接下來咱們還須要再配置一下 如何構建生產環境的代碼。
webpack.pro.js
配置文件
var base = require('./webpack.base.js'), merge = require('webpack-merge'), HtmlWebpackPlugin = require('html-webpack-plugin'), BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin, WebpackMd5Hash = require('webpack-md5-hash'); module.exports = merge(base, { mode: 'production', plugins: [ new WebpackMd5Hash(), new BundleAnalyzerPlugin(), new HtmlWebpackPlugin({ filename: 'index.html', template: 'index.html', chunks: ['main', 'vendor'], inject: true, chunksSortMode: 'auto', minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: true } }) ] });
編寫gulp
構建生產環境代碼任務:
var gulp = require("gulp"), gulpSequence = require("gulp-sequence"), gutil = require("gulp-util"), del = require("del"), uglify = require("gulp-uglify"), imagemin = require("gulp-imagemin"), express = require("express"), webpack = require("webpack"), webpackDevMiddleware = require("webpack-dev-middleware"), webpackHotMiddleware = require("webpack-hot-middleware"), history = require("connect-history-api-fallback"), opn = require("opn"); //...省略其餘任務 //清除 gulp.task("clean", function(cb) { del.sync("dist"); cb(); }); //圖片壓縮 gulp.task("copyImg", function() { return gulp .src("img/**/*.*") .pipe(imagemin()) .pipe(gulp.dest("dist/img/")); }); //複製無需編譯的js文件 gulp.task("copyJs", function() { return gulp .src([ "src/js_modules/react/pro/react.js", "src/js_modules/react-dom/pro/react-dom.js" ]) .pipe(uglify()) .pipe(gulp.dest("dist/js/")); }); //webpack production gulp.task("webpackPro", function(cb) { var webpackProConfig = require("./build/webpack.pro.js"); webpack(webpackProConfig, function(err, stats) { if (err) throw new gutil.PluginError("webpack:production", err); gutil.log( "[webpack:production]", stats.toString({ chunks: false, colors: true, timings: true, source: true, cachedAssets: false }) ); cb(); }); }); gulp.task("buildSuccess", function(cb) { gutil.log("[webpack:production]", "build success!"); cb(); }); gulp.task( "build", gulpSequence("clean", "copyImg", "copyJs", "webpackPro", "buildSuccess") );
最後,老規矩把相關的package
寫入package.json
並npm install
"dependencies": { "babel-runtime": "^6.26.0", "react": "^16.4.1" }, "devDependencies": { "autoprefixer": "^9.1.5", "babel-core": "^6.26.3", "babel-eslint": "^9.0.0", "babel-loader": "^7.1.5", "babel-plugin-external-helpers": "^6.22.0", "babel-plugin-import": "^1.9.1", "babel-plugin-syntax-dynamic-import": "^6.18.0", "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-decorators-legacy": "^1.3.5", "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-env": "^1.7.0", "babel-preset-react": "^6.24.1", "connect-history-api-fallback": "^1.5.0", "copy-webpack-plugin": "^4.5.2", "css-loader": "^1.0.0", "eslint": "^5.6.0", "eslint-loader": "^2.1.1", "eslint-plugin-react": "^7.11.1", "express": "^4.16.3", "file-loader": "^2.0.0", "gulp": "^3.9.1", "gulp-sequence": "^1.0.0", "gulp-util": "^3.0.8", "html-webpack-plugin": "^3.2.0", "http-proxy-middleware": "^0.18.0", "less": "^3.8.1", "less-loader": "^4.1.0", "node-sass": "^4.9.0", "opn": "^5.3.0", "path": "^0.12.7", "postcss": "^6.0.22", "postcss-loader": "^3.0.0", "react-hot-loader": "^4.3.4", "sass-loader": "^7.0.3", "style-loader": "^0.23.0", "url-loader": "^1.1.1", "webpack": "^4.12.0", "webpack-cli": "^3.1.0", "webpack-dev-middleware": "^3.1.3", "webpack-hot-middleware": "^2.22.3", "webpack-merge": "^4.1.4" }
最後的最後,命令行輸入gulp build
試試效果吧!
{ "presets": [ [ "env", { "modules": false, "targets": { "browsers": ["last 5 versions", "IE 9"] } } ], "react" ], "plugins": [ [ "transform-runtime", { "polyfill": false, "regenerator": true } ], "external-helpers", "syntax-dynamic-import", "transform-class-properties", "transform-decorators-legacy", [ "import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" } ], "react-hot-loader/babel" ], "env": { "test": { "presets": [ [ "env", { "modules": false, "targets": { "browsers": [ "last 5 versions", "IE 9" ] } } ], "react" ], "plugins": [ [ "transform-runtime", { "polyfill": false, "regenerator": true } ], "external-helpers", "syntax-dynamic-import", "transform-es2015-modules-commonjs", "transform-class-properties", "transform-decorators-legacy", [ "import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" } ] ] } } }
{ "root": true, "env": { "es6": true, "browser": true, "node": true }, "parser": "babel-eslint", "parserOptions": { "sourceType": "module", "ecmaFeatures": { "jsx": true, "experimentalObjectRestSpread": true } }, "globals": { "ReactDOM": false, "React": false, "jest": false, "test": false, "expect": false, "describe": false, "it": false }, "plugins": ["react"], "extends": ["eslint:recommended", "plugin:react/recommended"], "settings": { "react": { "version": "16.4.1" } }, "rules": { "no-extra-semi": 0, "no-console": 0, "react/prop-types": 0, "no-extra-boolean-cast": 0, "no-else-return": [1, { "allowElseIf": false }], "no-loop-func": 1, "arrow-spacing": 1, "eqeqeq": 2, "no-restricted-properties": [ 2, { "object": "disallowedObjectName", "property": "disallowedPropertyName" }, { "object": "disallowedObjectName", "property": "anotherDisallowedPropertyName", "message": "Please use allowedObjectName.allowedPropertyName." } ], "no-return-assign": [2, "always"], "no-sequences": 2, "no-throw-literal": 2, "no-unmodified-loop-condition": 2, "no-useless-return": 2, "prefer-promise-reject-errors": [2, {"allowEmptyReject": true}], "require-await": 2, "jsx-quotes": [2, "prefer-double"], "prefer-const": 2 } }
module.exports = { plugins:[ require('autoprefixer')({ browsers:[ "last 3 versions","iOS 7","not ie <= 9", "Android >= 4.0", "last 3 and_chr versions", "last 3 and_ff versions", "last 3 op_mob versions", "last 3 op_mob versions", "last 3 op_mini versions" ], //是否美化屬性值 cascade:true, //是否去掉沒必要要的前綴 remove:true }) ] };