webpack 入門實戰

大概幾個月前,剛接觸 gulp 的時候,經過 gulp 對前端工做流進行優化,在 gulpfile.js 文件中寫插件,編譯 less、stylus,壓縮 css、js 等等,感受工做效率獲得極大的提高,本來手動的東西,如今都是自動化了。javascript

webpack

可是,如今,學習 react 的時候,我又不得不來學習 webpack。webpack 最近火得不行(其實已經火了好久了),當下最熱門的前端資源模塊化管理和打包工具,它能把各類資源,包括 jxs、coffeeJS、less/sass,甚至圖片,看成模塊來加載和使用。一樣它須要一個 webpack.config.js 的配置文件,有專門針對於 css、js 和圖片等插件,在 js 中直接經過 require 來使用模塊,很方便。css

webpack 能夠將文件模塊按照依賴打包成方便使用的前端資源,還能夠將按需加載的模塊進行異步加載。html

ps:gulp/grunt 是前端構建工具,是自動化工具,能夠簡化前端流程;而 webpack 是前端模塊化方案。雖然兩者在某些功能上有共同點(好比壓縮代碼),但本質是不同的,好比如今還有基於 gulp 的 webpack 插件gulp-webpack前端

前端模塊化

前端模塊化是大勢所趨,隨着 webapp 的興起,瀏覽器的功能愈來愈強大,而一個單頁面在使用的過程當中會加載更多的 JavaScript 代碼,這給前端開發的流程和運行帶來了很大的挑戰。java

時下,已經有很多前端模塊化系統:node

script 標籤,這是最傳統的文件模塊,一個文件是一個模塊,react

<script src="./main.js"></script>
<script src="./module.js"></script>

這些接口會暴露在全局做用下,弊端不少:webpack

  1. 全局做用域形成變量衝突git

  2. 文件只能按照 script 的順序進行加載程序員

  3. 開發人員必須主觀解決代碼庫和模塊的依賴關係

  4. 在大型的項目中,會形成資源文件難以管理,代碼庫混亂不堪

CommonJS

NodeJS 遵循的就是 CommonJS 規範,這種經過 require 和 module.exports 的方式很常見:

//main.js
var module = require('./module.js')
// dosomething
module.exports = somevalue;

優勢是服務器端模塊便於重用,簡單易用,且 NPM 上有幾十萬個可使用的模塊包,缺點是這種加載方式屬於同步加載,不適用於瀏覽器,且不能非阻塞的加載多個模塊。關於 CommonJS 循環加載的原理,能夠看看這篇文章中的介紹 ES6模塊加載的實質 CommonJS 部分。

AMD CMD

AMD 是適合在瀏覽器中的異步加載的模塊,

define("module", ["dep1", "dep2"], function(d1, d2) {
  return someExportedValue;
});
require(["module", "../file"], function(module, file) { /* ... */ });

CMD 規範和 AMD 規範很像,CommonJS 規範保持了很大的兼容。

還有就是 ES6 中的模塊加載系統,詳情請移步ES6模塊加載

在上面的分析中,提到的都只是對 JavaScript 文件等加載,然而在前端的開發中還須要對圖片,樣式,字體文件和 html 模版等樣式的加載,webpack 可讓這一切成爲可能:

require("./style.css");
require("./style.less");
require("./template.jade");
require("./image.png");

在加載的過程當中,還能經過對靜態文件的分析,好比 css,把它內聯到 html 的 style 樣式中。

webpack 的優點

webpack 是集大成者,支持 CommonJS,AMD/CMD,還能對圖片,樣式等進行模塊化。

好比直接使用 CommonJS 語法:

var m1 = require('module1');
var m2 = require('module2');
//dosomething
module.exports = function(){
  m1();
  m2();
}

安裝及使用

經過 npm 全局安裝 webpack,npm install webpack -g,也能夠在本地項目中安裝依賴 npm install webpack --save-dev,前提要確保 npm init。

目錄下面有一個靜態頁面 index.html 和 JS 入口文件 entry.js,

<!-- index.html -->
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
  <script src="bundle.js"></script>
</body>
</html>
// entry.js
document.write('hello webpack');

而後使用命令 webpack entry.js bundle.js 就能夠生成 bundle.js 文件。

如今添加 module.js 文件,在 ertry.js 中引用:

//module.js
document.write('hello module');

//ertry.js
document.write(require('module'));

webpack 會分析每一個文件的入口,把依賴的相關文件都打包到 bundle.js。webpack entry.js bundle.js 這句話執行後,會先執行 entry.js,其餘文件,則只有在 require 的時候纔會加載。

還能夠經過插件來加載樣式模塊。目錄下添加一個 style.css 文件,安裝 css 和 style 模塊 npm install css-loader style-loader

/* style.css */
body { background: yellow; }

// entry.js
require("!style!css!./style.css");
document.write('hello webpack');

這個時候打包,刷新頁面,就能夠看到 index.html 中內聯的 css 樣式。

webpack.config.js

前面介紹的這種是命令行打包的方式,比較麻煩,通常都是寫一個 webpack 的配置文件,上面的配置文件能夠以下:

//webpack.config.js
var webpack = require('webpack')

module.exports = {
  entry: './entry.js',
  output: {
    path: __dirname,
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      {test: /\.css$/, loader: 'style!css'}
    ]
  }
}

module.loader 中 loader 可能有人會有疑惑,這種用感嘆號把模塊放開表示 css 文件加載多個模塊,加載的順序從右到左,先加載 css 模塊,再加載 style 模塊。在 entry.js 文件中就能夠簡化的寫成 require('./style.css')

配置文件的幾個比較重要的參數以下:

  1. entry: 編譯過程的輸入

  2. output: 編譯過程的輸出

  3. module: 模塊module的處理方式

  4. plugin: 配置文件的插件入口

  5. resolve 配置文件其餘解決方案

output 表明輸出,path 表明路徑,filename 表明文件名。plugin 表示插件,有內置插件和擴展插件,

var webpack = require("webpack");

var ComponentPlugin = require("component-webpack-plugin");

module.exports = {
  plugin: [
    //內置壓縮插件
    new webpack.optimize.UglifyJsPlugin({
      compressor: {
        warnings: false,
      },
    }),
    // 擴展插件
    ComponentPlugin()
  ]
};

更多插件能夠去官網查看。

module 中最須要注意的就是 loaders,

  1. test:正則表達式,用於匹配文件名(必須)

  2. loader:須要加載的 loaders 列表,可用 ! 加載多個(必須)

  3. include/exclude:手動添加必須處理的文件(文件夾)或屏蔽不須要處理的文件(文件夾)(可選);

  4. query:爲loaders提供額外的設置選項(可選)

resolve 是配置的其餘解決方案,好比 resolve.alias 能夠定義模塊的別名,resolve.root 能夠定義絕對路徑。resolve.extensions 能夠省去加載文件的後綴名,即後綴名自動補全。可是必需要在前面加一個空的字符串,不然會致使沒法加載的狀況。

webpack 更強大的功能

webpack-dev-server

webpack-dev-server 是一個專門爲 webpack 服務的 nodejs 服務器,經過 npm install --save-dev webpack-dev-server 命令來安裝。

// webpack.config.js
devServer: {
  contentBase: "./",  //本地服務器所加載的頁面所在的目錄
  colors: true,  //終端中輸出結果爲彩色
  historyApiFallback: true,  //不跳轉
  inline: true  //實時刷新
}

Babel

Babel 是一個編譯 JavaScript 的平臺,它的功能很是強大,可用編譯 JSX,ES6,ES7,生成瀏覽器識別的 JavaScript 語言。須要安裝多個依賴:npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react

//webpack.config.js
module: {
  loaders: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel',
      query: {
        presets: ['es2015','react']
      }
    }
  ]
},

Hot Module Replacement

webpack 提供熱更新,官網關於熱更新的介紹:hot-module-replacement-with-webpack,webpack-dev-server

webpack-dev-server 自己就帶有熱更新功能,只須要在參數啓動參數重添加 webpack-dev-server --inline --hot,若是嫌每次添加麻煩,可用在 package.json 中 script 設置 start 或在 webpack.config.js 中開啓。

html-webpack-plugin

這個插件主要是針對於 html 的,能夠自動生成 html 文件,尤爲當使用了 hash 以後,不用困擾因 hash 的變化帶來的問題。

var webpack = require('webpack')
var htmlwebpackplugin = require('html-webpack-plugin')
module.exports = {
  entry: './main.js',
  output: {
    path: __dirname,
    filename: 'bundle.js'
  },
  plugins: [new htmlwebpackplugin()]
}

有時候會由於 html 中必需要有一些特別的東西,不能直接生成,此時就須要配置模版:

module.exports = {
  plugins:[
    new htmlwebpackplugin({
      filename: 'hello.html', // 生成的文件
      template: 'src/template.html' // 模版文件
    })
  ]
}

更多配置信息,參考

  1. title: 頁面 title

  2. filename: 輸出的 HTML 文件名,默認是 index.html

  3. template: 模板文件路徑,支持加載器,好比 html!./index.html

  4. inject: true | 'head' | 'body' | false ,注入全部的資源到特定的 template 或者 templateContent 中,若是設置爲 true 或者 body,全部的 javascript 資源將被放置到 body 元素的底部,'head' 將放置到 head 元素中。

  5. favicon: 添加特定的 favicon 路徑到輸出的 HTML 文件中。

  6. minify: {} | false , 傳遞 html-minifier 選項給 minify 輸出

  7. hash: true | false, 若是爲 true, 將添加一個惟一的 webpack 編譯 hash 到全部包含的腳本和 CSS 文件,對於解除 cache 頗有用。

  8. cache: true | false,若是爲 true, 這是默認值,僅僅在文件修改以後纔會發佈文件。

  9. showErrors: true | false, 若是爲 true, 這是默認值,錯誤信息會寫入到 HTML 頁面中

  10. chunks: 容許只添加某些塊 (好比,僅僅 unit test 塊)

  11. chunksSortMode: 容許控制塊在添加到頁面以前的排序方式,支持的值:'none' | 'default' | {function}-default:'auto'

  12. excludeChunks: 容許跳過某些塊,(好比,跳過單元測試的塊)

若是你感興趣,還能夠去看一看如何本身手動寫 webpack 插件。連接1連接2

Environment flags

有時候,一些函數只需在 dev 環境下運行,有些函數要在 product 環境下運行,經過設置 webpack 的 DefinePlugin 就能夠很輕鬆的幫助咱們實現,好比:

var webpack = require('webpack');

var devFlagPlugin = new webpack.DefinePlugin({
  // __DEV__ 默認是 false,除非手動設置開發環境
  __DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false'))
});

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  },
  plugins: [devFlagPlugin]
};
// main.js
document.write('<h1>Hello World</h1>');

if (__DEV__) { //若是開發環境
  document.write(new Date());
}

此時有兩種運行方式,進入開發模式的運行方式:

# Linux & Mac
$ env DEBUG=true webpack-dev-server

# Windows
$ set DEBUG=true
$ webpack-dev-server

總結

在接觸 webpack 以前,強烈建議先學習 ES6。其實,不少人都說前端變化太快,昨天還很火熱的框架,可能今天就被另外一個所取代。我以爲,正是這種快速的更新,讓那些喜歡學習,喜歡鑽研的程序員,得到了新生,新的活力。共勉!

參考

webpack 中文官網
阮一峯 webpack-demos
webpack編譯流程漫談
webpack學習之路
入門Webpack,看這篇就夠了
一小時包教會 —— webpack 入門指南

歡迎來個人博客討論。

相關文章
相關標籤/搜索