webpack前端模塊加載工具

轉載自:http://www.cnblogs.com/YikaJ/p/4586703.html

#爲什麼用webpack

CommonJs與AMD

在一開始,我們先講一下它和以往我們所用的模塊管理工具有什麼不一樣。在最開始的階段,Js並沒有這些模塊機制,各種Js到處飛,得不到有效妥善的管理。後來前端圈開始制定規範,最耳熟能詳的是CommonJs和AMD。

CommonJs是應用在NodeJs,是一種同步的模塊機制。它的寫法大致如下:

var firstModule = require("firstModule");

//your code...

module.export = anotherModule

AMD的應用場景則是瀏覽器,異步加載的模塊機制。require.js的寫法大致如下:

define(['firstModule'], function(module){
  
  //your code...
  return anotherModule
})

其實我們單比較寫法,就知道CommonJs是更爲優秀的。它是一種同步的寫法,對Human友好,而且代碼也不會繁瑣臃腫。但更重要的原因是, 隨着npm成爲主流的JavaScript組件發佈平臺,越來越多的前端項目也依賴於npm上的項目,或者自身就會發布到npm平臺。 所以我們對如何可以使用npm包中的模塊是我們的一大需求。所以browserify工具就出現了,它支持我們直接使用 require() 的同步語法去加載npm模塊。

當然我們這裏不得不說的是,ES2015(ES6)裏也有了自己的模塊機制,也就是說ES6的模塊機制是官方規定的,我們通過 babel (一種6to5的編譯器)可以使用比較多的新特性了,包括我們提到的模塊機制,而它的寫法大致如下:

import {someModule} from "someModule";

// your codes...

export anotherModule;

當然上面的寫法只是最基本的,還有其他的不同加載模塊的寫法,可以看一下阮一峯老師的 ECMAScript 6 入門 或者babel的相關文檔 Learn ES2015 。

功能特性

browserify的出現非常棒,但webpack更勝一籌!

我們來看看webpack支持哪些功能特性:

  1. 支持CommonJs和AMD模塊,意思也就是我們基本可以無痛遷移舊項目。
  2. 支持模塊加載器和插件機制,可對模塊靈活定製。特別是我最愛的babel-loader,有效支持ES6。
  3. 可以通過配置,打包成多個文件。有效利用瀏覽器的緩存功能提升性能。
  4. 將樣式文件和圖片等靜態資源也可視爲模塊進行打包。配合loader加載器,可以支持sass,less等CSS預處理器。
  5. 內置有source map,即使打包在一起依舊方便調試。

看完上面這些,可以想象它就是一個前端工具,可以讓我們進行各種模塊加載,預處理後,再打包。之前我們對這些的處理是放在grunt或gulp等前端自動化工具中。有了webpack,我們無需藉助自動化工具對模塊進行各種處理,讓我們工具的任務分的更加清晰。

我們看一下官方對webpack理解的圖。

任何靜態資源都可以視作模塊,然後模塊之間也可以相互依賴,通過webpack對模塊進行處理後,可以打包成我們想要的靜態資源。

既然已經大致知道爲什麼我們要使用webpack了,我們接下來就開始使用webpack吧!

#開始使用webpack

首先新建一個webpack101的項目,我們將在webpack101這裏開展我們接下來的各項學習。

$ npm init // 用於初始化項目的package.json

//初始化文件目錄:
webpack101
  --- src
    --- entry.js
    --- module1.js
  --- index.html
  --- package.json
  --- webpack.config.js

安裝webpack

我們通過npm來將webpack安裝到全局

$ npm install webpack -g

一個最簡單的webpack

webpack配置

webpack是需要進行配置的,我們在使用webpack的時候,會默認 webpack.config.js 爲我們的配置文件。所以接下來,我們新建這個js文件。

// webpack.config.js
var path = require("path");
module.exports = {
  entry: '../src/entry.js', //演示單入口文件
  output: {
    path: path.join(__dirname, 'out'),  //打包輸出的路徑
    filename: 'bundle.js',			  //打包後的名字
    publicPath: "./out/"				//html引用路徑,在這裏是本地地址。
  }
};

編寫入口文件

接下來就編寫我們的入口文件 entry.js 和第一個模塊文件 module1.js 。我們一切從簡,裏面只用來加載一個Js模塊。

// entry.js
require("./module1"); // 使用CommonJs來加載模塊

下一個文件

// module1.js
console.log("Hello Webpack!");

啓動webpack

一切準備好後,我們僅需要在項目根目錄下,用命令行 webpack 執行一下即可。

// webpack 命令行的幾種基本命令

$ webpack // 最基本的啓動webpack方法
$ webpack -w // 提供watch方法,實時進行打包更新
$ webpack -p // 對打包後的文件進行壓縮,提供production
$ webpack -d // 提供source map,方便調試。

webpack成功運行後,我們就可以看到根目錄出現了out文件夾,裏面有我們打包生成的 bundle.js 。我們最後通過在 index.html 裏對這個文件引入就可以了。我們可以在控制檯看到我們想要的結果, Hello Webpack !

多模塊依賴

剛纔的例子,我們僅僅是跑通了webpack通過 entry.js 入口文件進行打包的例子。下面我們就來看一下它是否真的支持CommonJs和AMD兩種模塊機制呢?下面我們新建多幾個js文件吧!

// 修改module1.js
require(["./module3"], function(){
  console.log("Hello Webpack!");
});

下一個文件

// module2.js,使用的是CommonJs機制導出包
module.exports = function(a, b){
  return a + b;
}

下一個文件

// module3.js,使用AMD模塊機制
define(['./module2.js'], function(sum){
  return console.log("1 + 2 = " + sum(1, 2));
})

其實像上面這樣混用兩種不同機制非常不好,這裏僅僅是展示用的,在開發新項目時還是推薦CommonJs或ES2015的Module。當然我個人更傾向於ES2015的模塊機制的~

loader加載器

到了我最喜歡也是最激動人心的功能了!我們先想想應用場景,前端社區有許多預處理器供我們使用。我們可以使用這些預處理器做一些強大的事情,大家都聽過的就是 CoffeeScript 和 Sass 了。我們以前要編譯這些預處理器,就是用 gulp 進行編譯。但是我們對這些文件處理其實也挺繁瑣的,webpack可以一次性解決!

在這裏我們用Sass和babel編譯ES2015爲例子,看一下loader是如何使用的。

安裝loader

我們第一步就是先要安裝好各個必須的loader,我們直接看看需要通過npm安裝什麼。

$ npm install style-loader css-loader url-loader babel-loader sass-loader file-loader --save-dev

配置loader

安裝完各個loader後,我們就需要配置一下我們的 webpack.config.js ,載入我們的loader。

// webpack.config.js
module.exports = {
  entry: path.join(__dirname, 'src/entry.js'),
  output: {
    path: path.join(__dirname, 'out'),
    publicPath: "./out/",
    filename: 'bundle.js'
  },
  // 新添加的module屬性
  module: {
    loaders: [
      {test: /\.js$/, loader: "babel"},
      {test: /\.css$/, loader: "style!css"},
      {test: /\.(jpg|png)$/, loader: "url?limit=8192"},
      {test: /\.scss$/, loader: "style!css!sass"}
    ]
  }
};

我們主要看看module的loaders。loaders是一個數組,裏面的每一個對象都用正則表達式,對應着一種配對方案。比如匹配到js後綴名就用babel-loader,匹配到scss後綴名的就先用sass,再用css,最後用style處理,不同的處理器通過 ! 分隔並串聯起來。這裏的loader是可以省略掉 -loader 這樣的,也就是原本應該寫成 style-loader!css-loader!sass-loader ,當然我們必須惜字如金,所以都去掉後面的東東。

我們僅僅是配置一下,已經是可以直接用ES2015和SASS去寫我們的前端代碼了。在此之前,我們對src文件夾裏再細分成js,css,image三個文件夾,處理好分層。話不多說,趕緊試試。

稍微複雜的webpack項目

bebel-loader

// js/es6-module.js
class People{
  constructor(name){
    this.name = name;
  }
  sayhi(){
    console.log(`hi ${this.name} !`);
  }
}
exports.module = People;

寫好模塊後,我們直接在 entry.js 入口文件中引入該模塊。

// entry.js

// javascript
require('./js/module1');
let People = require('./js/es6-module');
let p = new People("Yika");
p.sayHi();

// css
require('./css/main.scss');

哈哈哈,不能再爽!這下子我們可以使用很多優秀的ES6特性去構建大型的web了。

sass-loader

大家或許注意到了下方的css的require,那就是用來加載Sass樣式的。我們通過啓動style-loader會將css代碼轉化到 <style> 標籤內,我們看一下里面的內容。

// css/main.scss
html, body{
  background: #dfdfdf;
}

最後我們打開 index.html 觀察我們所有的結果,首先背景已經是淡灰色的,並且控制檯也有我們想要的內容。我們通過查看DOM結構,可以發現 head 標籤裏多出了style 標籤,裏面正是我們想要定製的樣式。

關於對圖片的打包

我們之前也說,webpack對與靜態資源來說,也是看作模塊來加載的。CSS我們是已經看過了,那圖片是怎麼作爲模塊打包加載進來呢?這裏我們可以想到,圖片我們是用url-loader加載的。我們在css文件裏的url屬性,其實就是一種封裝處理過require操作。當然我們還有一種方式就是直接對元素的src屬性進行require賦值。

div.img{
  background: url(../image/xxx.jpg)
}

//或者
var img = document.createElement("img");
img.src = require("../image/xxx.jpg");
document.body.appendChild(img);

上述兩種方法都會對符合要求的圖片進行處理。而要求就是在url-loader後面通過query參數的方式實現的,這裏就是說只有不大於8kb的圖片纔會打包處理成Base64的圖片。關於query,請看文檔: Query parameters

{test: /\.(jpg|png)$/, loader: "url?limit=8192"}

打包成多個資源文件

我們在開發多頁面的站點的時候,還是需要希望能有多個資源文件的。這樣我們就可以有效利用緩存提升性能,做到文件按需加載。如何寫入口文件,這裏就不再贅述了,我們直接看如何對 webpack.config.js 進行修改。

// webpack.config.js

entry: {
  page1: "entry.js",
  page2: "entry2.js"
},
output: {
  path: path.join(__dirname, 'out'),
    publicPath: "./out/",
    filename: '[name].js'
}

這裏重點關注兩個地方,entry屬性可以是一個對象,而對象名也就是key會作爲下面output的filename屬性的 [name] 。當然entry也可以是一個數組,更多用法都可以去webpack的 官方文檔 進行查看。

當然webpack也考慮到公共模塊的利用,我們利用插件就可以智能提取公共部分,以提供我們瀏覽器的緩存複用。我們只需要在 webpack.config.js 添加下面的代碼即可。

// 修改添加,webpack.config.js
var webpack = require('webpack');
module.exports = {
  // ....省略各種代碼
      plugins: [
        new webpack.optimize.CommonsChunkPlugin('common.js')
      ]
}

我們做個小測試,讓第二個入口文件也加載我們之前的 es6-module.js 。然後我們用webpack進行打包,就發現生成的 common.js 裏是有相應代碼的。我們需要手動在html上去加載 common.js ,並且是 必須要最先加載 。

獨立出css樣式

如果我們希望樣式通過 <link> 引入,而不是放在 <style> 標籤內呢,即使這樣做會多一個請求。這個時候我們就要配合插件一起使用啦,我們一起來看看。

$ npm install extract-text-webpack-plugin --save-dev

安裝完插件就要配置 webpack.config.js 了。我們添加以下代碼

var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
  // ...省略各種代碼
  module: {
    loaders: [
      {test: /\.js$/, loader: "babel"},
      {test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader")},
      {test: /\.(jpg|png|svg)$/, loader: "url?limit=8192"},
      {test: /\.scss$/, loader: "style!css!sass"}
    ]
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin('common.js'),
    new ExtractTextPlugin("[name].css")
  ]
}

爲了區分開用 <link> 鏈接和用 <style> ,我們這裏以CSS後綴結尾的模塊用插件。我們重點關注一下使用了ExtractTextPlugin的模塊,在ExtractTextPlugin的extract方法有兩個參數,第一個參數是經過編譯後通過style-loader單獨提取出文件來,而第二個參數就是用來編譯代碼的loader。

當然,插件也支持所有獨立樣式打包成一個css文件。增加多一個參數即可。

new ExtractTextPlugin("style.css", {allChunks: true})

至於怎樣加載樣式是最佳實踐,這個就要自己平時多思考了。多站點多樣式的時候,是做到一次性打包加載呢,還是按需加載呢?我這裏就建議一項,主頁儘量做到最精簡,畢竟決定用戶存留時間。

#總結

前端社區不斷髮展,越來越趨向於組件化的發展。通過webpack,我們就能體驗到one component one module 的開發感覺。當然如何更好的使用webpack還是要通過不斷的思考總結,才能找到最優的方案。


發佈了210 篇原創文章 · 獲贊 28 · 訪問量 120萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章