React自發布以來吸引了越來越多的開發者,React開發和模塊管理的主流工具webpack也被大家所熟知。那么webpack有哪些優勢,可以成為最主流的React開發工具呢?
webpack是什么
CommonJS和AMD是用於JavaScript模塊管理的兩大規范,前者定義的是模塊的同步加載,主要用於NodeJS;而后者則是異步加載,通過requirejs等工具適用於前端。隨着npm成為主流的JavaScript組件發布平台,越來越多的前端項目也依賴於npm上的項目,或者自身就會發布到npm平台。因此,讓前端項目更方便的使用npm上的資源成為一大需求。
web開發中常用到的靜態資源主要有JavaScript、CSS、圖片、Jade等文件,webpack中將靜態資源文件稱之為模塊。webpack是一個module bundler(模塊打包工具),其可以兼容多種js書寫規范,且可以處理模塊間的依賴關系,具有更強大的js模塊化的功能。Webpack對它們進行統一的管理以及打包發布,其官方主頁用下面這張圖來說明Webpack的作用:
webpack特性
webpack具有requireJs和browserify的功能,但仍有很多自己的新特性:
1. 對 CommonJS 、 AMD 、ES6的語法做了兼容 2. 對js、css、圖片等資源文件都支持打包 3. 串聯式模塊加載器以及插件機制,讓其具有更好的靈活性和擴展性,例如提供對CoffeeScript、ES6的支持 4. 有獨立的配置文件webpack.config.js 5. 可以將代碼切割成不同的chunk,實現按需加載,降低了初始化時間 6. 支持 SourceUrls 和 SourceMaps,易於調試 7. 具有強大的Plugin接口,大多是內部插件,使用起來比較靈活 8.webpack 使用異步 IO 並具有多級緩存。這使得 webpack 很快且在增量編譯上更加快
React-sample
以 react-sample 為例,簡單說明 webpack 如何打包一個 React 組件,其目錄結構如下:
其中Hello.js定義了一個簡單的React組件,使用ES6語法:
/** * Created by pomy on 15/11/4. */ import React, {Component} from 'react'; class Hello extends Component { render(){ return ( <div>Hello, {this.props.name}!</div> ); } }
entry.js是入口文件,將一個Hello組件輸出到界面:
/**
* Created by pomy on 15/11/4. */ import React from 'react'; import Hello from './hello'; React.render(<Hello name="Nate" />, document.body);
index.html的內容如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>React Sample</title> </head> <body> <script src="./assets/bundle.js"></script> </body> </html>
webpack.config.js文件通常放在項目的根目錄中,它本身也是一個標准的Commonjs規范的模塊:
/** * Created by pomy on 15/11/4. */ var path = require('path'); module.exports = { entry: path.resolve(__dirname, './src/entry.js'), output: { path: path.resolve(__dirname, './assets'), filename: 'bundle.js' }, module: { loaders: [ { test: /\.js?$/, loaders: ['babel'], exclude: /node_modules/ }, { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/} ] }, resolve:{ extensions:['','.js','.json'] }, };
整個代碼在 這里,clone之后,切換到react-sample
目錄下,在終端運行 npm i && npm run build
進行打包,打包后的文件名是 bundle.js
, 所在相對目錄是 /assets
。至此,webpack打包過程就Ok了。
Webpack安裝和配置
安裝
webpack 可以作為全局的npm模塊安裝,也可以在當前項目中安裝。
npm install -g webpack npm install --save-dev webpack
對於全局安裝的webpack,直接執行此命令會默認使用當前目錄的webpack.config.js作為配置文件。如果要指定另外的配置文件,可以執行:
webpack —config webpack.custom.config.js
配置
每個項目下都必須配置有一個 webpack.config.js ,它的作用如同常規的 gulpfile.js/Gruntfile.js ,就是一個配置項,告訴 webpack 它需要做什么。
前文說了,webpack.config.js文件通常放在項目的根目錄中,它本身也是一個標准的Commonjs規范的模塊。在導出的配置對象中有幾個關鍵的參數:
entry
entry參數定義了打包后的入口文件,可以是個字符串或數組或者是對象;如果是數組,數組中的所有文件會打包生成一個filename
文件;如果是對象,可以將不同的文件構建成不同的文件:
{ entry: { page1: "./page1", //支持數組形式,將加載數組中的所有模塊,但以最后一個模塊作為輸出 page2: ["./entry1", "./entry2"] }, output: { path: "dist/js/page", publicPath: "/output/", filename: "[name].bundle.js" } }
該段代碼最終會生成一個 page1.bundle.js 和 page2.bundle.js,並存放到 ./dist/js/page 文件夾下
output
output
參數是個對象,定義了輸出文件的位置及名字:
output: { path: "dist/js/page", publicPath: "/output/", filename: "[name].bundle.js" }
path: 打包文件存放的絕對路徑
publicPath: 網站運行時的訪問路徑
filename:打包后的文件名
當我們在entry
中定義構建多個文件時,filename
可以對應的更改為[name].js
用於定義不同文件構建后的名字。
module
在webpack中JavaScript,CSS,LESS,TypeScript,JSX,CoffeeScript,圖片等靜態文件都是模塊,不同模塊的加載是通過模塊加載器(webpack-loader)來統一管理的。loaders之間是可以串聯的,一個加載器的輸出可以作為下一個加載器的輸入,最終返回到JavaScript上:
module: { //加載器配置 loaders: [ //.css 文件使用 style-loader 和 css-loader 來處理 { test: /\.css$/, loader: 'style-loader!css-loader' }, //.js 文件使用 jsx-loader 來編譯處理 { test: /\.js$/, loader: 'jsx-loader?harmony' }, //.scss 文件使用 style-loader、css-loader 和 sass-loader 來編譯處理 { test: /\.scss$/, loader: 'style!css!sass?sourceMap'}, //圖片文件使用 url-loader 來處理,小於8kb的直接轉為base64 { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'} ] }
test
項表示匹配的資源類型,loader
或loaders
項表示用來加載這種類型的資源的loader,loader的使用可以參考 using loaders,更多的loader可以參考 list of loaders。
!
用來定義loader的串聯關系,"-loader"是可以省略不寫的,多個loader之間用“!”連接起來,但所有的加載器都需要通過 npm
來加載。
此外,還可以添加用來定義png、jpg這樣的圖片資源在小於10k時自動處理為base64圖片的加載器:
{ test: /\.(png|jpg)$/,loader: 'url-loader?limit=10000'}
給css和less還有圖片添加了loader之后,我們不僅可以像在node中那樣require js文件了,我們還可以require css、less甚至圖片文件:
require('./bootstrap.css'); require('./myapp.less'); var img = document.createElement('img'); img.src = require('./glyph.png');
注意,require()
還支持在資源path前面指定loader,即require(![loaders list]![source path])
形式:
require("!style!css!less!bootstrap/less/bootstrap.less"); // “bootstrap.less”這個資源會先被"less-loader"處理, // 其結果又會被"css-loader"處理,接着是"style-loader" // 可類比pipe操作
require()
時指定的loader會覆蓋配置文件里對應的loader配置項。
resolve
webpack在構建包的時候會按目錄的進行文件的查找,resolve
屬性中的extensions數組中用於配置程序可以自行補全哪些文件后綴:
resolve: {
//查找module的話從這里開始查找 root: '/pomy/github/flux-example/src', //絕對路徑 //自動擴展文件后綴名,意味着我們require模塊可以省略不寫后綴名 extensions: ['', '.js', '.json', '.scss'], //模塊別名定義,方便后續直接引用別名,無須多寫長長的地址 alias: { AppStore : 'js/stores/AppStores.js',//后續直接 require('AppStore') 即可 ActionType : 'js/actions/ActionType.js', AppAction : 'js/actions/AppAction.js' } }
然后我們想要加載一個js文件時,只要require('common')就可以加載common.js文件了。
注意一下, extensions
第一個是空字符串! 對應不需要后綴的情況.
plugin
webpack提供了[豐富的組件]用來滿足不同的需求,當然我們也可以自行實現一個組件來滿足自己的需求:
plugins: [ //your plugins list ]
在webpack中編寫js文件時,可以通過require的方式引入其他的靜態資源,可通過loader對文件自動解析並打包文件。通常會將js文件打包合並,css文件會在頁面的header中嵌入style的方式載入頁面。但開發過程中我們並不想將樣式打在腳本中,最好可以獨立生成css文件,以外鏈的形式加載。這時extract-text-webpack-plugin插件可以幫我們達到想要的效果。需要使用npm的方式加載插件,然后參見下面的配置,就可以將js中的css文件提取,並以指定的文件名來進行加載。
npm install extract-text-webpack-plugin –save-dev
plugins: [ new ExtractTextPlugin('styles.css') ]
externals
當我們想在項目中require一些其他的類庫或者API,而又不想讓這些類庫的源碼被構建到運行時文件中,這在實際開發中很有必要。此時我們就可以通過配置externals
參數來解決這個問題:
externals: { "jquery": "jQuery" }
這樣我們就可以放心的在項目中使用這些API了:var jQuery = require("jquery");
context
當我們在require一個模塊的時候,如果在require中包含變量,像這樣:
require("./mods/" + name + ".js");
那么在編譯的時候我們是不能知道具體的模塊的。但這個時候,webpack也會為我們做些分析工作:
1.分析目錄:'./mods';
2.提取正則表達式:'/^.*\.js$/';
於是這個時候為了更好地配合wenpack進行編譯,我們可以給它指明路徑,像在cake-webpack-config中所做的那樣(我們在這里先忽略abcoption的作用):
var currentBase = process.cwd(); var context = abcOptions.options.context ? abcOptions.options.context : path.isAbsolute(entryDir) ? entryDir : path.join(currentBase, entryDir);
關於 webpack.config.js 更詳盡的配置可以參考這里
webpack常用命令
webpack的使用通常有三種方式:
1、命令行使用:webpack <entry.js> <result.js> 其中entry.js是入口文件,result.js是打包后的輸出文件
2、node.js API使用:
var webpack = require('webpack'); webpack({ //configuration }, function(err, stats){});
3、默認使用當前目錄的webpack.config.js作為配置文件。如果要指定另外的配置文件,可以執行:webpack --config webpack.custom.config.js
webpack 的執行也很簡單,直接執行
$ webpack --display-error-details
即可,后面的參數“--display-error-details”是推薦加上的,方便出錯時能查閱更詳盡的信息(比如 webpack 尋找模塊的過程),從而更好定位到問題。
常用命令
webpack的使用和browserify有些類似,下面列舉幾個常用命令:
webpack 最基本的啟動webpack命令
webpack -w 提供watch方法,實時進行打包更新
webpack -p 對打包后的文件進行壓縮
webpack -d 提供SourceMaps,方便調試
webpack --colors 輸出結果帶彩色,比如:會用紅色顯示耗時較長的步驟 webpack --profile 輸出性能數據,可以看到每一步的耗時 webpack --display-modules 默認情況下 node_modules 下的模塊會被隱藏,加上這個參數可以顯示這些被隱藏的模塊
前面的四個命令比較基礎,使用頻率會比較大,后面的命令主要是用來定位打包時間較長的原因,方便改進配置文件,提高打包效率。
圖片打包和靜態資源服務器
圖片打包
webpack中對於圖片的處理,可以通過url-loader來實現圖片的壓縮。
div.img{
background: url(../image/xxx.jpg)
}
//或者 var img = document.createElement("img"); img.src = require("../image/xxx.jpg"); document.body.appendChild(img);
針對上面的兩種使用方式,loader可以自動識別並處理。根據loader中的設置,webpack會將小於指點大小的文件轉化成 base64 格式的 dataUrl,其他圖片會做適當的壓縮並存放在指定目錄中。
module: { { test: /\.(png|jpg)$/, loader: 'url-loader?limit=10000&name=build/[name].[ext]' }] }
對於上面的配置,如果圖片資源小於10kb就會轉化成 base64 格式的 dataUrl,其他的圖片會存放在build/文件夾下。
靜態資源服務器
除了提供模塊打包功能,Webpack還提供了一個基於Node.js Express框架的開發服務器,它是一個靜態資源Web服務器,對於簡單靜態頁面或者僅依賴於獨立服務的前端頁面,都可以直接使用這個開發服務器進行開發。在開發過程中,開發服務器會監聽每一個文件的變化,進行實時打包,並且可以推送通知前端頁面代碼發生了變化,從而可以實現頁面的自動刷新。
Webpack開發服務器需要單獨安裝,同樣是通過npm進行:
npm install -g webpack-dev-server
可以使用webpack-dev-server直接啟動,也可以增加參數來獲取更多的功能,具體配置可以參見官方文檔。默認啟動端口8080,通過localhost:8080/webpack-dev-server/可以訪問頁面,文件修改后保存時會在頁面頭部看到sever的狀態變化,並且會進行熱替換,實現頁面的自動刷新。