使用Webpack,Express和Babel來配置你的React/ES6開發環境

轉載自blog.hellojs.org/settin

在2016年,你需要嘗試學習一下react, 而首先,你需要將React和React DOM這兩個庫加入到你的頁面中

加入你已經在頁面中加入了這兩個庫,接下來我該怎麼使用React呢?

多說幾句,你得在你的項目中加入Babel你才可以使用React

當下學習javascript是什麼感受

在我第一次接觸React來練習在頁面上創建並渲染組件。雖然我非常喜歡react動態的頁面和Redux的單狀態項目,可我使用了太多的時間來配置配置文件以及安裝開發依賴,浪費了大量的編碼時間

React項目初始化搭建非常痛苦是因為React不是一個面向一整個項目的庫。根據Facebook的文檔,`it』s a view library for building user interfaces`這是一個用於建設用戶界面的庫,可隨著React環境的慢慢成熟,當使用React開發的時候大量的開發者會普遍使用一些類似於Webpack或者Babel的工具來處理這個問題,可是這些工具經常變更,所以,每當你第一次初始化React開發環境的時候,會令人非常煩惱

為了幫你解決這個困擾,我已經把把自己的項目初始化放到了這個gist上,其中包含了,我的webpack在開發環境和生產環境的配置和express伺服器的代碼。我會向你演示如何熱更新模塊,這個功能可以在你的瀏覽器在你修改代碼後自動更新界面,不需要自己手動刷新這個界面,這一整個項目也在Github上面可以找到

你可以直接使用我的配置然後開始使用React,可是我還是想嘮叨一遍這些文件都在做一些什麼事情,這樣的話當出現了一些bug後,你不會在要解決一些問題的時候手足無措。當我剛開始搭建一個React項目的時候,有大量的關於使用es5語法和一些廢棄的模塊搭建的Webpack/React項目的教程。如果你是一個非常酷的選手,正在使用ES6編寫React的話,我希望這篇文章能夠幫到你

/* 注意:在我寫了這篇博客之後,我開發了一個Create-React-App的項目,這個項目是一個我自己做的用以快速創建一個React的Helloworld項目的工具,它分裝了webpack和Babel複雜的使用。

但是配置Webpack和Babel是你開發React必須要掌握的技能,這個教程會很好的幫助你理解他們 */

1、開始一個React項目需要準備些什麼

技術上,你只需要兩個庫:React和React-DOM(這兩個庫讓你能夠把React組件渲染到瀏覽器上)

但是如果你要馬上使用React,你必須要學會如何編寫JSX

什麼是JSX?JSX是一個讓你用HTML寫法寫組建的Javascript語法,使用了JSX,你的React代碼就會變得非常美觀,讓其他的開發者能清楚的之後你的代碼想要實現什麼,比如這就是JSX:

const Component = () => (<div><h1>I am a component></h1></div>);

可惜的是,現在的瀏覽器的Javascript解釋器並不能理解JSX,他遇到JSX會拋出異常,所以你需要自己轉換成通用JS語法:

var Component = function Component() { return React.createElement( "div", null, React.createElement( "h1", null, "I am a component>" ) );};

更多

沒有人願意像這樣寫代碼,這麼寫會顯得代碼非常冗餘,而且這有點讓人弄不懂這個代碼到底在做些什麼,可是瀏覽器只能解釋通用版本的JS代碼,所以我們在開發時候應該怎麼辦

進入Babel。Babel是一個Javascript的編譯器,無論你使用什麼你喜歡的(JSX還是ES6)語法,他都會幫你轉換成瀏覽器可以理解的語言

我使用JSX和ES6來編寫javascript並使用Babel來將他們轉換成使用ES5規範的另一個文件中,這樣瀏覽器就能夠理解這些代碼了

好,那麼現在我們有React,React-DOM還有Babel,我們還需要什麼呢

當我們編寫React的時候,我們需要在把大量不同的組件寫到不同的文件中,這些文件都會導入各自的依賴。如果我們將這些文件都通過sprite標籤各自導入的話,會消耗大量時間。

這就是我們為什麼使用Webpack的原因,這是一個可以將我們需要的所有文件和庫依賴整合成一個文件能夠直接加入到頁面中的打包工具

Webpack還有一個非常方便的插件叫做webpack-dev-server,這個插件通過一個輕量級的Express伺服器使得你可以很方便的看到你修改的地方在瀏覽器上的改變,很吊!

所以在我們開始編寫代碼之前,我們需要:

  1. React

  2. React-DOM

  3. Babel

  4. Webpack

2、安裝依賴

那麼,我們要怎麼準備這些東西呢

當然是npm install

在你的項目的根目錄,運行下面的命令來安裝React和React-DOM

npm install --save reactnpm install --save react-dom

運行下面的命令來安裝開發依賴,這些依賴對你最後的應用是沒有用的,但是在你的開發編譯過程中有用

npm install --save-dev babel-corenpm install --save-dev babel-clinpm install --save-dev babel-loadernpm install --save-dev babel-preset-es2015npm install --save-dev babel-preset-reactnpm install --save-dev react-hot-loadernpm install --save-dev webpacknpm install --save-dev webpack-dev-middlewarenpm install --save-dev webpack-hot-middleware

我們使用的Babel被分為核心庫(babel-core),命令行工具(babel-cli)和一個能夠讓babel和webpack協調使用的插件babel-loader。除此之外,我們還安裝了babel-preset-react和babel-preset-es2015插件,這兩個插件涵蓋了把ES6和React代碼編譯成通用javascript的規則

我現在不想深入react-hot-loader和webpack middleware模塊,但我們會在將來的熱更新模塊章節中提及到他

當你完成這些操作後,你的package.json應該像這樣:

{ "name": "how_far_hr", "version": "1.1.0", "engines": { "node": "5.12.0" }, "description": "A small toy app showing HR progress with React and ES6", "author": "Jon Deng <jondeng.com>", "license": "MIT", "private": false, "scripts": { "start": "npm build && node dist/app.js", "dev-start": "node dist/app.js", "build": "webpack --config ./webpack.deployment.config.js --progress --colors" }, "dependencies": { "bootstrap": "^4.0.0-alpha.2", "express": "^4.14.0", "react": "^15.3.2", "react-dom": "^15.3.2", "redux": "^3.6.0", "underscore": "^1.8.3" }, "devDependencies": { "babel-cli": "^6.6.5", "babel-core": "^6.17.0", "babel-loader": "^6.2.5", "babel-preset-es2015": "^6.16.0", "babel-preset-react": "^6.16.0", "react-hot-loader": "^3.0.0-beta.5", "webpack": "^1.13.2", "webpack-dev-middleware": "^1.8.3", "webpack-hot-middleware": "^2.12.2" }}

如果已經做好了的話,這是我的項目目錄,我把我所有的源文件都放在了app下的目錄,當這些文件被編譯打包的時候,webpack會把這些文件都輸出到dist下,在根目錄下含有一個package.json文件和生產環境還有開發環境的webpack配置文件

3、為開發環境配置Webpack

為了使得webpack在開發環境上運作,我們需要對React進行配置,這可以在我們每次保存我的時候轉義並打包並可以利用`webpack-dev-server`直接在瀏覽器上顯示修改。

以下是我的開發環境的webpack.config.js文件

var path = require(path);var webpack = require(webpack);module.exports = { entry: ./app/index.js, output: { path: __dirname, filename: bundle.js, publicPath: /app/assets/ }, module: { loaders: [ { test: /.jsx?$/, loader: babel-loader, include: path.join(__dirname, app), exclude: /node_modules/, query: { presets: [es2015, react] } } ] },};

第一行和第二行,我使用了require語法導入了path和webpack模塊

在第五行,我指明了這次打包的文件的入口,最簡單的找到入口文件的方法就是去找那些調用React-DOM來渲染你的React組建到頁面DOM的文件

在第6行到第9行,我申明了我會把我所有的腳本文件打包到一個叫做`bundle.js`的文件,並申明了公開文件目錄為/app/assets

最後,我在12~22行,我在每次webpack打包文件的時候都讓webpack運行babel-loader。babel-loader會將用ES6和JSX編寫的文件轉移成瀏覽器可以識別的通用javascript文件,在第14行到16行,我申明了一個正則表達式來讓webpack轉義那些在app/目錄下後綴名為.jsx的文件並屏蔽了node_modules下的文件,在第16行,我設置了Babel使用RS6和React的規則去轉義文件

現在,你想要啟動你的應用並動態編輯的話,你只要在命令行執行以下的操作:

webpack-dev-server --progress --colors

我喜歡使用progress和colors命令來讓命令行的輸出可讀性更高

也不是很難嘛,對不對~

4、為生產環境配置Webpack

const path = require(path);const webpack = require(webpack);module.exports = { devtool: source-map, entry: [ ./app/index.js ], output: { path: path.join(__dirname, dist), filename: bundle.js, publicPath: /dist/ }, plugins: [ new webpack.optimize.UglifyJsPlugin({ minimize: true, compress: { warnings: false } }) ], module: { loaders: [{ test: /.jsx?$/, loader: babel-loader, include: path.join(__dirname, app), exclude: /node_modules/, query: { presets: [es2015, react] } }] },};

你會發現生產環境的webpack配置回合開發環境的非常像

主要的區別在於我們添加了一個叫做UglifyJSPlugin的插件,這個插件可以混淆你的代碼

你也可以根據你自己的需要添加更多的webpack插件

5、創建一個Express伺服器

當然,webpack-dev-server在開發的時候非常方便,但是你最好別把它用在開發環境上,如果你使用Express伺服器的話你可以使用更多你想要的功能,如果你想要熱更新功能,我們會在下個章節中提及

在下面的文件中,我編寫了一個工廠方法來新建一個express伺服器對象,我們會把這個方法在應用入口文件中導入並創建一個express伺服器

const path = require(path)const express = require(express)module.exports = { app: function () { const app = express(); const indexPath = path.join(__dirname, indexDep.html); const publicPath = express.static(path.join(__dirname, ../dist)); app.use(/dist, publicPath); app.get(/, function (_, res) { res.sendFile(indexPath) }); return app; }}

在第10行,我為express對象指出`/dist`目錄來滿足靜態文件比如css,圖片,javascript文件的直接獲取的需求。因為我們webpack配置文件中,我們的打包的文件會導出到`/dist`目錄下

在第11行,我告訴express當訪問根目錄的時候返回indexDep.html頁面,這個文件中含有一個腳本標籤來導入我們在dist下打包後的文件,使得React可以渲染到頁面DOM上

在下一個章節,我們會使用這個工廠方法,來創建一個express伺服器對象

6、添加動態模塊動態模塊更新

開發React最炫酷的事情就是有很多開發功能上的優點,比如動態模塊更新,這個功能可以讓我們當組件代碼被修改的時候讓這些組件自動在頁面更新,而不是重新渲染一邊這個頁面。使用動態模塊更新,你會有一個非常好的動態響應編輯的體驗。因為在你編輯這個組件的時候他們會持續重新渲染,所以你會馬上看到自己的修改

我發現使用動態模塊更新最簡單的方法就是在express伺服器上面使用一個webpack中間件

在繼續下去之前,我想要花一點時間來了解一下中間件,在express中,中間件是像管道一樣的方法,接收數據流,輸出處理過的數據流

因為響應數據是數據流,我們可以在這些數據流入客戶端之前利用這些中間件方法來修改這些數據

所以我們使用webpack-dev-middleware來將react代碼轉義成瀏覽器可讀的通用js,並將這些文件打包到一個可以被客戶端讀取的js文件中

另外我們還將使用webpack-hot-middleware,這個中間件可以檢測到文件的修改並通知客戶端重新渲染組件

根據下面的文檔,可以看出webpack-hot-middleware是怎麼工作的

The middleware installs itself as a webpack plugin, and listens for compiler events.

每個連接的客戶端會建立一個伺服器可推送的連接,伺服器可以給連接的客戶端推送重新編譯事件的消息

當客戶端獲取到這個消息的時候,他會檢查代碼是否更新了,如果本地的代碼不是最新,就會觸發重新讀取代碼的事件

下面是app.js文件,這個文件被用做我們伺服器的入口。

這個入口是伺服器啟動所調用的第一個文件,也是連接所有應用文件的一個文件,這個入口文件在node應用中經常被叫做app.js或者index.js,為了更好地理解,這個解答會更有幫助

常規的,我們的入口文件會創建一個新的express伺服器對象來伺服器我們的webpack,並監聽一個指定的埠,這個伺服器會提供一個index.html文件,這個html文件會通過一個腳本標籤引入react組件,另外我們的入口文件會在開發環境的時候使用中間件來允許我們使用動態模塊更新

const Server = require(./server.js)const port = (process.env.PORT || 8080)const app = Server.app()if (process.env.NODE_ENV !== production) { const webpack = require(webpack) const webpackDevMiddleware = require(webpack-dev-middleware) const webpackHotMiddleware = require(webpack-hot-middleware) const config = require(../webpack.deployment.config.js) const compiler = webpack(config) app.use(webpackHotMiddleware(compiler)) app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPathdist }))}app.listen(port)console.log(`Listening at http://localhost:${port}`)

第一行,我們導入了工廠方法並創建了express伺服器對象

第五行,我檢查是否是在開發環境

第12~16行,我們在express伺服器中添加了webpack中間件

現在我們有了一個開發伺服器,我們現在只需要在命令行打入下面命令就能本地啟動應用了

node dist/app.js

與其每次都打著重複的命令,讓我們把我們的運行腳本寫入到package.json文件中吧,這可以幫助我們在像heroku這樣的伺服器上開發,這讓我們可以使用start腳本來部署應用

{ "name": "how_far_hr", "version": "1.1.0", "engines": { "node": "5.12.0" }, "description": "A small toy app showing HR progress with React and ES6", "author": "Jon Deng <jondeng.com>", "license": "MIT", "private": false, "scripts": { "start": "npm build && node dist/app.js", "dev-start": "node dist/app.js", "build": "webpack --config ./webpack.deployment.config.js --progress --colors" }, "dependencies": { "bootstrap": "^4.0.0-alpha.2", "express": "^4.14.0", "react": "^15.3.2", "react-dom": "^15.3.2", "redux": "^3.6.0", "underscore": "^1.8.3" }, "devDependencies": { "babel-cli": "^6.6.5", "babel-core": "^6.17.0", "babel-loader": "^6.2.5", "babel-preset-es2015": "^6.16.0", "babel-preset-react": "^6.16.0", "react-hot-loader": "^3.0.0-beta.5", "webpack": "^1.13.2", "webpack-dev-middleware": "^1.8.3", "webpack-hot-middleware": "^2.12.2" }}

後面沒啥了,就醬

推薦閱讀:

如何開始一張設計圖?
看別人吵架對你來說應該是好事兒
用Lerna管理多包JS項目
運算符
什麼是json jsonp 與ajax關係

TAG:前端開發 | React | webpack |