詳解前端模塊化工具-Webpack

React自發布以來吸引了愈來愈多的開發者,React開發和模塊管理的主流工具webpack也被你們所熟知。那麼webpack有哪些優點,能夠成爲最主流的React開發工具呢?css

Webpack是什麼

CommonJS和AMD是用於JavaScript模塊管理的兩大規範,前者定義的是模塊的同步加載,主要用於NodeJS;然後者則是異步加載,經過requirejs等工具適用於前端。隨着npm成爲主流的JavaScript組件發佈平臺,愈來愈多的前端項目也依賴於npm上的項目,或者自身就會發布到npm平臺。所以,讓前端項目更方便的使用npm上的資源成爲一大需求。html

web開發中經常使用到的靜態資源主要有JavaScript、CSS、圖片、Jade等文件,webpack中將靜態資源文件稱之爲模塊。webpack是一個module bundler(模塊打包工具),其能夠兼容多種js書寫規範,且能夠處理模塊間的依賴關係,具備更強大的js模塊化的功能。Webpack對它們進行統一的管理以及打包發佈,其官方主頁用下面這張圖來講明Webpack的做用:前端

webpack

Webpack具備requireJs和browserify的功能,但仍有不少本身的新特性:node

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 組件,其目錄結構以下:react

dir

其中Hello.js定義了一個簡單的React組件,使用ES6語法:jquery

/**
 * 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組件輸出到界面:webpack

/**
 * Created by pomy on 15/11/4.
 */
import React from 'react';
import Hello from './hello';
React.render(<Hello name="Nate" />, document.body);

index.html的內容以下:git

<!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規範的模塊:github

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了。web

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的使用一般有三種方式:

一、命令行使用:webpack 其中entry.js是入口文件,result.js是打包後的輸出文件
二、node.js API使用:

var webpack = require('webpack');
webpack({
//configuration
}, function(err, stats){});

三、默認使用當前目錄的 webpack.config.js 做爲配置文件。若是要指定另外的配置文件,能夠執行:webpack –config webpack.custom.config.js

webpack 的執行也很簡單,直接執行

webpack --display-error-details

經常使用命令

webpack的使用和browserify有些相似,下面列舉幾個經常使用命令:

webpack 最基本的啓動webpack命令
webpack -w 提供watch方法,實時進行打包更新
webpack -p 對打包後的文件進行壓縮
webpack -d 提供SourceMaps,方便調試
webpack --colors 輸出結果帶彩色,好比:會用紅色顯示耗時較長的步驟
webpack --profile 輸出性能數據,能夠看到每一步的耗時
webpack --display-modules 默認狀況下 node_modules 下的模塊會被隱藏,加上這個參數能夠顯示這些被隱藏的模塊
相關文章
相關標籤/搜索