React無疑是今年最火的前端框架,github上的star直逼30,000,基於React的React Native的star也直逼20,000。有了React,組件化似乎不再步履蹣跚,有了React Native,前端的邊界似乎廣闊無邊。而Webpack憑藉它非同步載入和可分離打包等優秀的特性,走...
React無疑是今年最火的前端框架,github上的star直逼30,000,基於React的React Native的star也直逼20,000。有了React,組件化似乎不再步履蹣跚,有了React Native,前端的邊界似乎廣闊無邊。而Webpack憑藉它非同步載入和可分離打包等優秀的特性,走在取代Grunt和Gulp的路上。而面向未來的ES6,模塊化的支持似乎已成定局。
我們現在就可以打造自己的Webpack+React+ES6環境並且開始探索起來。
這篇文章就給還沒走在這條路上的前端一個入門的指南。
文章導讀:
一、先把例子跑起來(setup your background)
可以fork我在github上的[react-webpack]demo項目,也可以從頭開始:
1.1 我們在名為react-webpack的新項目中要做的第一件事,就是新建一個package.json文件,它看起來應該是這個樣子的:
1 { 2 "name": "react-webpack", 3 "version": "1.0.0", 4 "description": "webpack demo", 5 "main": "index.js", 6 "scripts": { 7 "start": "webpack-dev-server --hot --progress --colors", 8 "build": "webpack --progress --colors" 9 }, 10 "repository": { 11 "type": "git", 12 "url": ".." 13 }, 14 "keywords": [ 15 "react", 16 "webpack" 17 ], 18 "author": "yixuan", 19 "devDependencies": { 20 "babel-core": "^5.8.25", 21 "babel-loader": "^5.3.2", 22 "css-loader": "^0.12.1", 23 "react": "^0.13.3", 24 "react-hot-loader": "^1.3.0", 25 "react-router": "^0.13.3", 26 "webpack": "^1.12.2", 27 "webpack-dev-server": "^1.11.0" 28 }, 29 "dependencies": { 30 "lodash": "~3.10.1", 31 "react-kendo": "~0.13.11" 32 } 33 }View Code
1.2 第二步就是創建我們webpack的配置文件webpack.config.js:
1 var webpack = require('webpack'); 2 module.exports = { 3 entry: [ 4 'webpack/hot/only-dev-server', 5 "./js/app.js" 6 ], 7 output: { 8 path: './build', 9 filename: "bundle.js" 10 }, 11 module: { 12 loaders: [ 13 { test: /\.js?$/, loaders: ['react-hot', 'babel'], exclude: /node_modules/ }, 14 { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader'}, 15 { test: /\.css$/, loader: "style!css" } 16 ] 17 }, 18 resolve:{ 19 extensions:['','.js','.json'] 20 }, 21 plugins: [ 22 new webpack.NoErrorsPlugin() 23 ] 24 };View Code
1.3 同時我們還需要在入口文件index.html中引入程式跑起來的一些必要的東西,雖然他的作用看起來沒有那麼明顯:
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="utf-8"> 5 <title>New React App</title> 6 <link rel="stylesheet" type="text/css" href="node_modules/bootstrap/dist/css/bootstrap.css"> 7 <link rel="stylesheet" type="text/css" href="css/main.css"> 8 </head> 9 <body> 10 <section id="react"></section> 11 <script src="bundle.js"></script> 12 </body> 13 </html>View Code
註意,這裡面引用的bundle.js文件非常重要,他是我們打包後的入口文件,不引入它程式是跑不起來的。
app.js文件是程式的入口文件,我們在這裡處理簡單的類似路由的邏輯:
1 import React from 'react'; 2 import Router from 'react-router'; 3 import { DefaultRoute, Link, Route, RouteHandler } from 'react-router'; 4 5 import HelloHandler from './hello.js'; 6 7 let App = React.createClass({ 8 render() { 9 return ( 10 <div className="nav"> 11 <Link to="app" className="homelink">Home </Link> 12 <Link to="hello" className="hellolink"> Say Hello</Link> 13 {/* this is the importTant part */} 14 <RouteHandler/> 15 </div> 16 ); 17 } 18 }); 19 20 let routes = ( 21 <Route name="app" path="/" handler={App}> 22 <Route name="hello" path="/hello" handler={HelloHandler}/> 23 </Route> 24 ); 25 26 Router.run(routes, function (Handler) { 27 React.render(<Handler />, document.body); 28 });View Code
相信你已經看到了ES6的身影,沒錯,在這裡,我們寫ES6的代碼,只要新建js文件,我們就在裡面寫ES6的代碼。
我們在入口文件上放上了一個連接,分離我們最熟悉的helloworld代碼。也是為了方便以後我們寫其他的例子可以有單獨的文件,易於管理和查看,就像下麵看到的項目目錄一樣,一個js文件就是一個小例子的代碼。
hello.js:
1 import React from 'react'; 2 3 let Hello = React.createClass({ 4 5 render() { 6 return(<div>Hello World!</div>); 7 } 8 }); 9 10 export default Hello;View Code
1.4 執行:
1 npm install
再加上一些必要的文件入口,我們的項目現在看起來應該是這樣的:
從我的github上面fork下來的目錄應該是這樣的:
裡面包含了一些我已經寫好的小例子。當然,我們按照上面的目錄來創建就沒錯啦。
1.5 程式跑起來,執行:
1 npm start
然後訪問[http://localhost:8080/webpack-dev-server/]我們就可以看到項目跑起來了。
二、理解React
隨著Facebook開源了React Native for Android,React的前景似乎更加光明。它從最早的UI框架慢慢演變成了一套web應用的解決方案,並且它衍生出來的React Native更是承載著巨集偉的目標:learn once, write anywhere。這對前端來說似乎是種不可抗拒的吸引力。
React有三個關鍵詞:
- Just the ui
- virtual dom
- data flow
要理解React,我從它的這三個關鍵詞入手。
2.1 Just the ui
React 負責UI層面的展現,儘管很多人用React作[MVC]架構中的View層,但這並不是React的本意。
一般的情況下,我們在開發過程中,通常會使用模板或者直接使用HTML來構建UI,而HTML是靜態的,使用模板在大多數情況下是可以滿足需求的,但是在複雜的邏輯情境中就顯得有些吃力了,過多的if else或者邏輯控制在模板里,都會讓代碼變得難以維護,當然這還是說整個項目中使用統一的模板的情況下。
React換了一種思路解決問題,它把UI拆分成組件,而不是通過模板引擎和展示邏輯,使得它可以易於拓展和維護。因此它引入了JSX這種語法規則,可以讓我們使用類似HTML的語法去寫js的函數調用。
2.2 virtual dom
瀏覽器渲染頁面的一般過程通常是這樣的:
1 載入html->生成DOM樹->解析css生成Render樹->生成頁面
那麼React的virtual dom是怎麼做的呢?
1 生成virtual dom->diff->必要的DOM更新
在這裡面大部分的操作放在js中去完成,因為我們都知道dom操作是很昂貴的。所以在一般的情況下React的性能還是很不錯的。
2.3 data flow
React中的數據流是沿著組件樹從上到下單向流動的。
這裡的data flow指的是一種應用架構的實現方式,比如說,數據存放在哪裡,在哪裡觸發事件,如何響應用戶操作。它不是React提供的什麼新功能,應該是React構建應用的實踐。我們理解了之後的Flux或許就更加容易理解data flow這個概念了。
2.4 Flux overview
Flux是facebook配套React強推的一種應用架構思想。它利用數據的單向流動為React的可復用的視圖組件提供了補充。
與React的數據流動方式相同,在Flux架構中,數據也是單向流動的:
簡單的說,所有的數據流動都會經過Dispatcher。Action可以通過action creator產生並被提供給dispatcher,但多數情況下action是通過用戶與views的交互產生。
在View層捕獲用戶的交互,產生一個Action,通過觸發註冊在Dispatcher上面的事件回調,使得相關的Store響應Action,然後會觸發到Store上面的onChange事件,進一步的更新View。
數據流動也始終是如上圖所示的單向流動的。
實踐了這個例子:[thinking in react]會對理解React的基本工作原理有很好地幫助。
三、Webpack入門
3.1 什麼是Webpack?
說Webpack是類似Grunt和Gulp一樣的打包工具並不合適,說Webpack是類似Browserify的模塊管理工具也還有點類似。然而Webpack做的事情遠不止這些。
Webpack出自facebook的Instagram團隊,在網上找到了比較好的對它主要特性的歸納,如下:
1 同時支持CommonJS和AMD模塊(對於新項目,推薦直接使用CommonJS); 2 串聯式模塊載入器以及插件機制,讓其具有更好的靈活性和擴展性,例如提供對CoffeeScript、ES6的支持; 3 可以基於配置或者智能分析打包成多個文件,實現公共模塊或者按需載入; 4 支持對CSS,圖片等資源進行打包,從而無需藉助Grunt或Gulp; 5 開發時在記憶體中完成打包,性能更快,完全可以支持開發過程的實時打包需求; 6 對sourcemap有很好的支持,易於調試。
Webpack將項目中的一切資源都看做是模塊,模塊之間可以互相依賴,Webpack對他們做統一的管理,打包和發佈,用於我們的工作流中。
3.2 webpack.config.js文件
在前面我們跑起來了的例子中,我們可以看到webpack.config.js文件,所有跟webpack有關的工作,都在這個文件中進行。
它看起來,目前是這樣的:
1 var webpack = require('webpack'); 2 module.exports = { 3 entry: [ 4 'webpack/hot/only-dev-server', 5 "./js/app.js" 6 ], 7 output: { 8 path: './build', 9 filename: "bundle.js" 10 }, 11 module: { 12 loaders: [ 13 { test: /\.js?$/, loaders: ['react-hot', 'babel'], exclude: /node_modules/ }, 14 { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader'}, 15 { test: /\.css$/, loader: "style!css" }, 16 {test: /\.less/,loader: 'style-loader!css-loader!less-loader'} 17 ] 18 }, 19 resolve:{ 20 extensions:['','.js','.json'] 21 }, 22 plugins: [ 23 new webpack.NoErrorsPlugin() 24 ] 25 };View Code
它通常放在根目錄下,他自己也是一個標準的Commonjs模塊,我們可能已經看到了require,module.exports這樣的標誌性關鍵詞。
它的主要的配置參數有:
- entry:
1 entry: [ 2 'webpack/hot/only-dev-server', 3 "./js/app.js" 4 ],
它定義了打包的入口文件,數組中的文件會按順序進行,並且它會自行解決依賴問題。
在這裡其實我們還定義了Webpack開發伺服器,webpack-dev-server,我們可以在package.json文件中看到:
1 "scripts": { 2 "start": "webpack-dev-server --hot --progress --colors", 3 "build": "webpack --progress --colors" 4 },
這也是我們npm start啟動了伺服器之後訪問http://localhost:8080/webpack-dev-server/可以訪問到頁面的原因。
- output:
1 output: { 2 path: './build', 3 filename: "bundle.js" 4 },
它定義了輸出文件的的位置,包括路徑,文件名,還可能有運行時的訪問路徑(publicPath)參數。
- module:
webpack將所有的資源都看做是模塊,而模塊就需要載入器:
1 module: { 2 loaders: [ 3 { test: /\.js?$/, loaders: ['react-hot', 'babel'], exclude: /node_modules/ }, 4 { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader'}, 5 { test: /\.css$/, loader: "style!css" }, 6 {test: /\.less/,loader: 'style-loader!css-loader!less-loader'} 7 ] 8 },
對於不同的文件,我們可以自行配置使用不同的載入器。也可以自行實現合心意的載入器。
在這裡我們配置了babel-loader,可以讓我們在js文件中隨心所欲的開始寫ES6規範的代碼。
Webpack的載入器之間可以進行串聯,一個載入器的輸出可以成為另一個載入器的輸入。比如LESS文件先通過less-load處理成css,然後再通過css-loader載入成css模塊,最後由style-loader載入器對其做最後的處理,從而運行時可以通過style標簽將其應用到最終的瀏覽器環境。
- resolve:
1 resolve:{ 2 extensions:['','.js','.json'] 3 },
Webpack 是使用類似 Browserify 的方式在本地按目錄對依賴進行查找。
resolve屬性中的extensions數組中用於配置程式可以自行補全哪些尾碼。比如說我們要require一個common.js文件,添加了這個配置我們只要寫:require('common');就可以了。
- plugin:
1 plugins: [ 2 new webpack.NoErrorsPlugin() 3 ]
我們可以在plugin參數中配置我們需要用到的各種各樣的插件。比如我們想將多個文件分開打包,可能會用到:
1 { 2 entry: { a: "./a", b: "./b" }, 3 output: { filename: "[name].js" }, 4 plugins: [ new webpack.CommonsChunkPlugin("init.js") ] 5 }
四、現在開始探索&&擴展閱讀
面向未來的ES6,模塊化的支持似乎已成定局。我們通過從webpack中的配置即可馬上開始編寫ES6的代碼。
通過打造這樣的工作環境,我們可以同時使用多種面向未來的新技術開始新的探索。
現在就開始探索吧!不要等到未來到來,卻發現自己還沒做好準備。
擴展閱讀:
原文鏈接:http://www.cnblogs.com/skylar/p/React-Webpack-ES6.html