標籤:

webpack教程

本文來源:初始Webpack · Webpack for fools

Webpack傻瓜式指南(一)

因為這篇啟蒙文章比較受大家歡迎,在操作過程中大家也遇到很多坑,所以於2016.2.25更新 在文章最後總結了各種出現的錯誤

webpack最近很熱,我一開始是想翻譯一篇國外關於webpack的佳作,但是找來找去也沒有一篇讓我感覺到很滿意的,好多都是一步到位,滿屏幕都是React+Webpack,官方文檔寫的不太好,好多點都沒有解釋的詳細,所以我參考了幾篇文章,寫一篇容易上手的指南。本文適合第一次接觸webpack的朋友,如果是老鳥,就不用看了。這是系列的第一篇,主要講他最基本的用法。

比較

如果你熟悉原來一系列的構建工具,grunt或者gulp之類的,這裡有一篇webpack和他們比較的文章可以讀一讀。 Webpack Compared

安裝

先裝好node和npm,因為webpack是一個基於node的項目。然後

npm install -g webpack

總覽

官網對webpack的定義是MODULE BUNDLER,他的目的就是把有依賴關係的各種文件打包成一系列的靜態資源。 請看下圖

webpack簡單點來說就就是一個配置文件,所有的魔力都是在這一個文件中發生的。 這個配置文件主要分為三大塊

  • entry 入口文件 讓webpack用哪個文件作為項目的入口
  • output 出口 讓webpack把處理完成的文件放在哪裡
  • module 模塊 要用什麼不同的模塊來處理各種類型的文件

下面我們就一步一步來新建一個簡單的項目

建立項目

建一個文件夾,然後新建一個package.json的文件在項目根目錄下

mkdir webpackcd webpacknpm init# 一直點回車 如果懶得填一些信息

如果你使用git管理你的這個項目的話,建議你新建一個.gitignore文件,不要讓git提交一些node依賴的模塊, 你也可以參考github的例子 github/gitignore

我們這裡就簡單一點

node_modules

項目結構

現在項目裡面就有一個package.json, 我們多加一點東西,慢慢豐富它的內容。

  • /app
    • index.js
    • sub.js
  • package.json
  • webpack.config.js

添加了兩個js文件,添加了最重要的webpack的配置文件,我們還是從非常簡單的hello world開始玩起,webpack原生直接支持AMD和CommonJS兩種格式,如果你想使用ES6的風格,這點以後再提。

JS代碼

sub.js

//我們這裡使用CommonJS的風格function generateText() { var element = document.createElement(h2); element.innerHTML = "Hello h2 world"; return element;}module.exports = generateText;

index.js

var sub = require(./sub);var app = document.createElement(div);app.innerHTML = <h1>Hello World</h1>;app.appendChild(sub());document.body.appendChild(app);

代碼寫完了,完成一個很簡單的功能,新建一個單獨的module,並且在另外一個module裡面引用他,最後會在頁面裡面輸出兩個標題。

配置Webpack

現在開始配置webpack,目標是把這兩個js文件合併成一個文件. 我們可以自己在build文件夾裡面手動建一個index.html文件夾,然後再把合併以後的js引用在裡面,但是這樣有些麻煩,所以我們這裡安裝一個plugin,可以自動快速的幫我們生成HTML。

npm install html-webpack-plugin --save-dev

好 有了這個插件 開始寫config文件

var path = require(path);var HtmlwebpackPlugin = require(html-webpack-plugin);//定義了一些文件夾的路徑var ROOT_PATH = path.resolve(__dirname);var APP_PATH = path.resolve(ROOT_PATH, app);var BUILD_PATH = path.resolve(ROOT_PATH, build);module.exports = { //項目的文件夾 可以直接用文件夾名稱 默認會找index.js 也可以確定是哪個文件名字 entry: APP_PATH, //輸出的文件名 合併以後的js會命名為bundle.js output: { path: BUILD_PATH, filename: bundle.js }, //添加我們的插件 會自動生成一個html文件 plugins: [ new HtmlwebpackPlugin({ title: Hello World app }) ]};

然後在項目根目錄運行

webpack

終端顯示一堆信息,然後告訴你成功了。

你可以使用webpack --help看看一些運行的時候可選的參數

你會發現多出來一個build文件夾,直接點開裡面的html文件,你會發現我們可愛的「hello world」已經插入到頁面了。我們的任務完成了,成功生成html,合併js,html引入了js,js被執行了。

配置webpack-dev-server

上面任務雖然完成了,但是我們要不斷運行程序然後查看頁面,所以最好新建一個開發伺服器,可以serve我們pack以後的代碼,並且當代碼更新的時候自動刷新瀏覽器。

安裝webpack-dev-server

更新, 這裡還是全局安裝比較好

npm install webpack-dev-server -g

安裝完畢後 在config中添加配置

module.exports = { .... devServer: { historyApiFallback: true, hot: true, inline: true, progress: true, }, ...}

然後再package.json裡面配置一下運行的命令,npm支持自定義一些命令

..."scripts": { "start": "webpack-dev-server --hot --inline"},...

好了,萬事具備了,在項目根目錄下輸入npm start,一堆花花綠綠的信息後server已經起來了,在瀏覽器裡面輸入http://localhost:8080 發現偉大的hello world出現了,在js裡面隨便修改一些輸出然後保存, boom!瀏覽器自動刷新,新的結果出現了。

拓展閱讀 如果你的伺服器端使用的是express框架,你還可以直接安裝express的middleware,webpack配合express,很好用。

npm install webpack-dev-middleware --save-dev

更多的詳情 請參考這篇文章

Using React with Webpack Tutorial | RisingStack

添加CSS樣式

現在來添加一些樣式,webpack使用loader的方式來處理各種各樣的資源,比如說樣式文件,我們需要兩種loader,css-loader 和 style-loader,css-loader會遍歷css文件,找到所有的url(...)並且處理。style-loader會把所有的樣式插入到你頁面的一個style tag中。

安裝我們的loader

npm install css-loader style-loader --save-dev

配置loader,在webpack.config.js中

devServer: { historyApiFallback: true, hot: true, inline: true, progress: true, }, ... module: { loaders: [ { test: /.css$/, loaders: [style, css], include: APP_PATH } ] }, ... plugins: [ new HtmlwebpackPlugin({ title: Hello World app }) ]

看loaders的書寫方式,test裡面包含一個正則,包含需要匹配的文件,loaders是一個數組,包含要處理這些程序的loaders,這裡我們用了css和style,注意loaders的處理順序是從右到左的,這裡就是先運行css-loader然後是style-loader.

新建一個樣式文件 main.css

h1 { color: red;}

記得在入口文件index.js中引用

require(./main.css);

然後發現標題變成紅色的了,webpack的理念是基於項目處理的,把對應的文件格式給對應的loader處理,然後你就不用管了,它會決定怎麼壓縮,編譯。

那現在想使用一些有愛的css預編譯程序,來點sass吧。 你可能已經想到了,再來個loader就行啦,確實是這樣簡單。

更新 這裡還需要添加node-sass來解析sass文件

npm install sass-loader node-sass --save-dev

稍微修改一下config,刪掉我們先前添加的css規則,加上下面的loader

{ test: /.scss$/, loaders: [style, css, sass], include: APP_PATH },

添加兩個sass文件,variables.scss和main.scss

variables.scss

$red: red;

main.scss

@import "./variables.scss";h1 { color: $red;}

在index.js中引用

require(./main.scss);

然後發現標題如願變紅,相當簡單吧。

處理圖片

這個和其他一樣,也許你也已經會玩了。安裝loader,處理文件。不過有個神奇的地方它可以根據你的需求將一些圖片自動轉成base64編碼的,為你減輕很多的網路請求。

安裝url-loader

npm install url-loader --save-dev

配置config文件

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

注意後面那個limit的參數,當你圖片大小小於這個限制的時候,會自動啟用base64編碼圖片。

下面舉個栗子。

新建一個imgs文件夾,往裡面添加一張崔叔的照片。在scss文件中添加如下的東西。

@import "./variables.scss";h1 { color: $red; background: url(./imgs/avatar.jpg);}

npm start, 然後查看圖片的url,發現神奇。

添加第三方庫

有的時候還想來點jquery,moment,undersocre之類的庫,webpack可以非常容易的做到這一點,有謠言說Bower即將停止開發了, 作者推薦都使用npm來管理依賴。那麼我們現在安裝在我們的app中添加jquery和moment的支持。

npm install jquery moment --save-dev

在js中引用

var sub = require(./sub);var $ = require(jquery);var moment = require(moment);var app = document.createElement(div);app.innerHTML = <h1>Hello World it</h1>;document.body.appendChild(app);app.appendChild(sub());$(body).append(<p>look at me! now is + moment().format() + </p>);

看看瀏覽器,成功! jquery和moment現在都起作用了!

添加ES6的支持

如果現在你的項目還沒有對ES6的語法支持,那就有點沒有逼格了,其實大家都知道這個也很簡單,因為我們有偉大的Babel

首先 裝各種loader

npm install babel-loader babel-preset-es2015 --save-dev

配置我們的config文件

... { test: /.jsx?$/, loader: babel, include: APP_PATH, query: { presets: [es2015] } },...

es2015這個參數是babel的plugin,可以支持各種最新的es6的特性,具體的情況看這個鏈接。 Babel es2015 plugin

現在我們可以改掉CommonJS風格的文件了。

sub.js

export default function() { var element = document.createElement(h2); element.innerHTML = "Hello h2 world hahaha"; return element;}

index.js

import ./main.scss;import generateText from ./sub;import $ from jquery;import moment from moment;let app = document.createElement(div);const myPromise = Promise.resolve(42);myPromise.then((number) => { $(body).append(<p>promise result is + number + now is + moment().format() + </p>);});app.innerHTML = <h1>Hello World it</h1>;document.body.appendChild(app);app.appendChild(generateText());

我們上面測試了import, export,const,let,promise等一系列es6的特性。

最後完美的輸出界面。

結語

第一部分到這裡結束,經過一系列例子,你應該能夠了解webpack最基本的用法了吧。是否已經喜歡上這種簡潔的配置了?下一部分我們會繼續討論一些webpack更高級的用法。

debug

有很多留言提出了一些大家遇到的錯誤,感謝留言中的一位飄在心中的調調同學,在一篇博文中總結了各種錯誤。我在這裡總結一下。請參考這篇文章的修正方式:

初學webpack遇到的坑

  • 初次運行webpack出現以下錯誤

    throw err;canot find module webpack/lib/node/NodeTemplatePlugin

  • 初次運行webpack-dev-server出現以下錯誤

ERROR in multi mainModule not found: Error: Cannot resolve module webpack/hot/dev-server

  • 初次運行sass-loder出現錯誤

原文中缺少了添加node-sass

npm install --save-dev

參考文章

SurviveJS - webpack_react

christianalfoni.github.io

很硬的鏈接

把這一節的代碼做了一個repo,想看所有源碼的可以clone下 vikingmute/webpack-basic-starter

同時這一系列關於Webpack的文章也可以在github找到 vikingmute/webpack-for-fools

Webpack傻瓜指南(二)開發和部署技巧

上一章講了webpack的最基本基本用法,出乎意料的受到大家的歡迎,這一節講更深入的技巧,這一節也更深入大家做項目的實戰經驗,一個項目分為開發和上線兩個大的階段,那麼我們的技巧就分成這兩大塊來分開講解。

開發技巧

啟用source-map

現在的代碼是合併以後的代碼,不利於排錯和定位,只需要在config中添加

... devtool: eval-source-map, ...

這樣出錯以後就會採用source-map的形式直接顯示你出錯代碼的位置。

為css啟用source-map

javascript有了這個特性,css自然不能落後,其實很簡單,只要在loader裡面添加一個參數即可

... { test: /.scss$/, loaders: [style, css?sourceMap, sass?sourceMap], include: APP_PATH }, ...

配置webpack-dev-server代理

既然常用webpack做React一類的SPA,那麼一個典型的例子就是前後端分離。後端是一個RESTful的server不管用什麼寫的。假定在本機他是類似http://localhost:5000/api/*這類的請求,現在添加配置讓ajax請求可以直接proxy過去。

devServer: { hot: true, inline: true, //其實很簡單的,只要配置這個參數就可以了 proxy: { /api/*: { target: http://localhost:5000, secure: false } } },

重啟以後 發現/api/*的請求都代理到http://localhost:5000去了~

更多的方法可以看官方文檔 Webpack dev server proxy

和express配合

上節我們說到了一個和express配合的教程,如果你前後端都使用express作為框架,那麼wepack-dev-middleware就是你的最佳選擇,他可以完美的和express配合起來。這個一步一步的說起來比較複雜,大家可以自己查看下面那個鏈接,來配置你的express。

配置webpack-dev-middleware

使用preLoaders和postLoaders

也許你想在寫代碼的時候檢查自己的js是否符合jshint的規範,那麼隆重推薦preLoaders和postLoaders,上一節我們已經非常了解loaders了,用它來處理各種類型的文件。preLoaders顧名思義就是在loaders執行之前處理的,webpack的處理順序是preLoaders - loaders - postLoaders。

安裝jshint

npm install jshint-loader --save-dev

在config文件中配置

module: {... //和loaders一樣的語法,很簡單 preLoaders: [ { test: /.jsx?$/, include: APP_PATH, loader: jshint-loader } ]}...//配置jshint的選項,支持es6的校驗jshint: { "esnext": true},

好了 現在每次npm run start的時候就可以看到jshint的提示信息啦

載入jQuery plugin或者其他legacy第三方庫

這個是非常有用的內容 你的項目有時候會要載入各種各樣的第三方庫,一些老的庫不支持AMD或者CommonJS等一些先進的格式,比如一些jQuery的插件,它們都依賴jQuery,如果直接引用就會報錯 jQuery is not undefined這類的錯誤,有幾種方法解決

先創建一個jQuery plugin: plugin.js

(function ($) { const shade = "#556b2f"; $.fn.greenify = function() { this.css( "color", shade ); return this; };}(jQuery));

第一種方法 使用webpack.ProvidePlugin

webpack提供一個插件 把一個全局變數插入到所有的代碼中,在config.js裡面配置

... plugins: [ new HtmlwebpackPlugin({ title: Hello World app }), //provide $, jQuery and window.jQuery to every script new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery", "window.jQuery": "jquery" }) ] ...

在js中引用

...//這個也不需要了 因為$, jQuery, window.jQuery都可以直接使用了//import $ from jquery;import ./plugin.js;...myPromise.then((number) => { $(body).append(<p>promise result is + number + now is + moment().format() + </p>); //call our jquery plugin! $(p).greenify();});...

發現我們插入的p裡面的顏色已經變成了綠色!

第二種方法 使用imports-loader

先安裝這個loader

npm install imports-loader --save-dev

然後在入口js中

//注意這種寫法 我們把jQuery這個變數直接插入到plugin.js裡面了//相當於在這個文件的開始添加了 var jQuery = require(jquery);import imports?jQuery=jquery!./plugin.js;//後面的代碼一樣myPromise.then((number) => { $(body).append(<p>promise result is + number + now is + moment().format() + </p>); //call our jquery plugin! $(p).greenify();});

部署上線

剛才說的各種情況都是在開發時候的情況,那麼假如項目已經開發完了,需要部署上線了。我們應該新創建一個單獨的config文件,因為部署上線使用webpack的時候我們不需要一些dev-tools,dev-server和jshint校驗等。

複製我們現有的config文件,命名為 webpack.production.config.js,將裡面關於 devServer等和開發有關的東西刪掉。

在package.json中添加一個命令。

"scripts": { "start": "webpack-dev-server --hot --inline", "build": "webpack --progress --profile --colors --config webpack.production.config.js" },

當要上線的時候,運行

npm run build

可以發現build文件夾中生成了所有東西。

分離app.js和第三方庫

現在我們build出來的只有一個bundle.js 如果第三方庫很多的話,會造成這個文件非常大,減慢載入速度,現在我們要把第三方庫和我們app本身的代碼分成兩個文件。

修改entry入口文件

entry: { app: path.resolve(APP_PATH, index.js), //添加要打包在vendors裡面的庫 vendors: [jquery, moment] },

添加CommonsChunkPlugin

plugins: [ //這個使用uglifyJs壓縮你的js代碼 new webpack.optimize.UglifyJsPlugin({minimize: true}), //把入口文件裡面的數組打包成verdors.js new webpack.optimize.CommonsChunkPlugin(vendors, vendors.js), new HtmlwebpackPlugin({ title: Hello World app }) ]

添加完畢 運行npm run build

在build文件夾中發現如下結構

  • budle.js
  • index.html
  • vendors.js

生成多頁面

應用不可能都是SPA,不可能只生成一個html文件,如果想生成多個html頁面這麼玩?其實也是很簡單的: 現在的需求是這樣的,有兩個頁面,一個叫index.html 它需要引用vendors.js和app.js這兩個文件,還有一個mobile.html頁面他要引用vendors.js和mobile.js這兩個文件。

首先新建一個叫mobile.js的入口文件,比app.js更簡單一些

import ./main.scss;import $ from jquery;import imports?jQuery=jquery!./plugin.js;$(document).ready(function() { let app = document.createElement(div); app.innerHTML = <h1>Hello World</h1>; document.body.appendChild(app); $(h1).greenify();});

在config裡面配置

entry: { //三個入口文件,app, mobile和 vendors app: path.resolve(APP_PATH, index.js), mobile: path.resolve(APP_PATH, mobile.js), vendors: [jquery, moment] }, output: { path: BUILD_PATH, //注意 我們修改了bundle.js 用一個數組[name]來代替,他會根據entry的入口文件名稱生成多個js文件,這裡就是(app.js,mobile.js和vendors.js) filename: [name].js },

原來我們是沒有模版文件的,原來利用的HtmlWebpackPlugin的默認模版。誰想到這偉大的插件還可以自定義模版。 所以新建一個專門放模版的文件夾templates,在裡面加兩個模版文件index.html 和 mobile.html

index.html

<!DOCTYPE html><html> <head> <title>{%= o.htmlWebpackPlugin.options.title %}</title> </head> <body> <h3>Welcome to webpack</h3> </body></html>

mobile.html

<!DOCTYPE html><html> <head> <title>{%= o.htmlWebpackPlugin.options.title %}</title> </head> <body> <h3>Welcome to mobile page</h3> </body></html>

繼續配置config.js,現在讓HtmlwebpackPlugin可以生成多個文件

...//Template的文件夾路徑var TEM_PATH = path.resolve(ROOT_PATH, templates);...plugins: [ ... //創建了兩個HtmlWebpackPlugin的實例,生成兩個頁面 new HtmlwebpackPlugin({ title: Hello World app, template: path.resolve(TEM_PATH, index.html), filename: index.html, //chunks這個參數告訴插件要引用entry裡面的哪幾個入口 chunks: [app, vendors], //要把script插入到標籤里 inject: body }), new HtmlwebpackPlugin({ title: Hello Mobile app, template: path.resolve(TEM_PATH, mobile.html), filename: mobile.html, chunks: [mobile, vendors], inject: body }) ...]

然後運行

npm run build

看看生成好的偉大的文件,沒問題!

  • app.js
  • mobile.js
  • vendors.js
  • index.html
  • mobile.html

看看引用的對應關係,完美!!

index.html

<body> <h3>Welcome to webpack</h3> <script src="vendors.js"></script><script src="app.js"></script></body>

mobile.html

<body> <h3>Welcome to mobile page</h3> <script src="vendors.js"></script><script src="mobile.js"></script></body>

生成Hash名稱的script來防止緩存

經典問題,代碼更新了,但是瀏覽器有緩存,到時候就傻了。 怎麼解決,換名字唄。可以直接在後面加參數,也可以直接換掉文件名字。 webpack提供給了我們非常簡單的方法,基於文件的md5,只要把

output: { path: BUILD_PATH, //只要再加上hash這個參數就可以了 filename: [name].[hash].js},

運行完build以後,看看生成的文件,很完美~

index.html

<body> <h3>Welcome to webpack</h3> <script src="vendors.js"></script><script src="app.b6641abee64c999d95c1.js"></script></body>

好了,你現在了解webpack作為一個module bundler的精髓了吧,把我們的例子做成一個圖,幫助你理解一下。

結語

第二部分到這裡結束,這一節討論很多webpack的使用技巧,看起來比較瑣碎,但是在你工作中也許會很實用,下一部分會出現大家都期待的高大上的東西,Webpack和React和Backbone等的結合。

很硬的鏈接

repo的代碼已經更新,想看所有源碼的可以clone下 vikingmute/webpack-basic-starter

同時這一系列關於Webpack的文章也可以在github找到 vikingmute/webpack-for-fools

各項配置說明:

一.webpack基礎

  1.在項目中生成package.json:在項目根目錄中輸入npm init,根據提示輸入相應信息。(也可以不生成package.json文件,但是package.json是很有用的,所有建議生成)

  2.安裝webpaack

    a.在全局中安裝webpack:npm install webpack -g

    b.將webpack安裝到項目並將webpack寫入package.json的devDependencies中:進入項目根目錄,然後在命令行中輸入npm install webpack --save-dev。

  3.打包模塊

  webpack <entry> <output>。<entry>用於指定打包的文件,<output>用於指定打包後的文件。如webpack app/index.js       build/build.js表示將app文件夾中的index.js打包到build文件夾中的build.js中。

二.webpack配置文件常用配置項介紹

  1.webpack有一個默認的配置文件webpack.config.js,這個文件需要手動的創建,位於項目根目錄中。可以為一個項目設置多個配置文件,已達到不同的配置文件完成不同的功能。怎麼設置後面介紹。

  2.webpack的配置文件會暴露出一個對象,格式如下:

    module.exports = {

      //配置項

    }

  3.常用配置項將要說明

    entry:打包的入口文件,一個字元串或者一個對象

    output:配置打包的結果,一個對象

      fileName:定義輸出文件名,一個字元串

      path:定義輸出文件路徑,一個字元串

    module:定義對模塊的處理邏輯,一個對象

      loaders:定義一系列的載入器,一個數組

        [

          {

            test:正則表達式,用於匹配到的文件

            loader/loaders:字元串或者數組,處理匹配到的文件。如果只需要用到一個模塊載入器則使用                 loader:string,如果要使用多個模塊載入器,則使用loaders:array

            include:字元串或者數組,指包含的文件夾

            exclude:字元串或者數組,指排除的文件夾

          }

        ]

    resolve:影響對模塊的解析,一個對象

      extensions:自動補全識別後綴,是一個數組

    plugins:定義插件,一個數組

  4.entry詳細說明

    (1)當entry是一個字元串時,這個字元串表示需要打包的模塊的路徑,如果只有一個要打包的模塊,可以使用這種

    形式

    (2)當entry是一個對象

      a.是數組時,當需要將多個模塊打包成一個模塊,可以使用這個方式。如果這些模塊之間不存在依賴,數組中

      值的順序沒有要求,如果存在依賴,則要將依賴性最高的模塊放在最後面。

      例如:entry:["./app/one.js",".app/two.js"]

      b.是鍵值對形式的對象是,當需要分別打包成多個模塊時,可以使用這種方式,例如;

      entry:{

        module1:"./app/one.js",

        module2:["./app/two.js","./app/three.js"]

      }

    注:當entry是一個鍵值對形式的對象時,包名就是鍵名,output的filename不能是一個固定的值,因為每個包的

    名字不能一樣

  5.output詳細說明

    (1)output是一個對象

    (2)output.filename:指定輸出文件名,一個字元串。當輸出一個文件,output.filename為一個確定的字元串

      如:output:{

          filename:"build.js"

            }

      當輸出多個文件,output.filename不能為一個確定的字元串。為了讓每個文件有一個唯一的名字,需要用到下面

      的變數

      [name] is replaced by the name of the chunk.對應entry的鍵名

      [hash] is replaced by the hash of the compilation.

      [chunkhash] is replaced by the hash of the chunk.

      如:output:{

          path:./build/,

          fialname:[name].js

        }

      (3)output.path:指定輸出文件的路徑,相對路徑,一個字元串

      (4)output中還有其他的一些值,不在這裡說明,可以在webpack的官方網站中獲得更多的詳細信息

  6.module.loaders詳細說明

    (1)module是一個對象,定義對模塊的處理邏輯

    (2)module.loaders是一個數組,定義一系列載入器,這個數組中的每一項都是一個對象

    (3)module.loaders:[

        {

          test:正則,用於匹配要處理的文件

          loader/loaders: 字元串或者數組, 如果只需要用到一個模塊載入器 ,則使用loader:string,

          如果要使用多個模塊載入器,則使用loaders:array

          include:字元串或者數組,指包含的文件夾

          exclude:字元串或者數組,指排除的文件夾

        }

      ]

    (4)module除了可以配置loaders以外還能配置其他的值,在webpack的官網中獲得更多的信息

  7.resolve.extensions詳細說明

    (1)resolve.extensions並不是必須配置的,當不配置時,會使用默認值

    ["", ".webpack.js", ".web.js", ".js"],當手動為resolve.extensions設置值,

    它的默認值會被覆蓋

    (2)如果你想要每個模塊都能夠按照它們自己擴展名正確的被解析,要在數組中添加一個空字元串。

    (3)如果你想請求一個js文件但是在請求時不帶擴展(如:require(somecode)),那麼就需要

    將.js添加到數組中。其他文件一樣

    (4)resolve還有其他的配置項,在webpack的官網獲得更多信息

  8.補充

    (1)當設置了配置文件後,在命令行中輸入webpack就可按照默認配置文件中的配置項打包模塊了。

    (2)設置多個webpack配置文件。webpack默認的配置文件是webpack.config.js,當在命令行中輸入webpack時默認找的是          webpack.config.js。通過在package.json的scripts中添加例如

    "start-html":"webpack --config webpack.html.config.js"

    在命令行中輸入npm run start-html查找的就是webpack.html.config.js,通過這種方式可以實現不同的

    配置文件有不同的用處,這樣就不用反覆修改同一個配置文件

  9.下面是一個簡單的配置文件

module.exports = { entry:{ one:"./app/one.js", two:"./app/two.js" }, output:{ path:"./build/", filename:"[name].js" }, module:{ loaders:[ { test:/.*.css$/, loaders:["style","css"], exclude:./node_modules/ } ] }, resolve:{ extensions:[,.css,.js,jsx] }};

三.webpack-dev-server

  1.webpack-dev-server是一個輕量級的伺服器,修改文件源碼後,自動刷新頁面將修改同步到頁面上

  2.安裝webpack-dev-server:

    全局安裝:npm install webpack-dev-server -g

    在項目中安裝並將依賴寫在package.json文件中:npm install webpack-dev-server --save-dev

  3.使用命令webpack-dev-server --hot --inline完成自動刷新

  4.默認的埠號是8080,如果需要8080埠被佔用,就需要改埠,webpack-dev-server --port 3000(將埠號改為3000)

  5.安裝webpack-dev-server後就可以在命令行中輸入webpack-dev-server開啟服務,然後在瀏覽器地址欄中

  輸入localhost:埠號,就可以在瀏覽器中打開項目根目錄的index.html文件,如果項目根目錄中沒有index.html

  文件,就會在瀏覽器中列出項目根目錄中的所有的文件夾。

  6.第五條只是啟動服務並不能自動刷新,要自動刷新需要用到webpack-dev-server --hot --inline

  7.當使用webpack-dev-server --hot --inline命令時,在每次修改文件,是將文件打包

  保存在內存中並沒有寫在磁碟里(默認是根據webpack.config.js打包文件,通過--config xxxx.js修改),這種打包得到的文件

  和項目根目錄中的index.html位於同一級(但是你看不到,因為

  它在內存中並沒有在磁碟里)。使用webpack命令將打包後的文件保存在磁碟中

  例如在index.html文件中引入通過webpack-dev-server --hot --inline打包的build.js

    <script src="build.js"></script>

  在index.html文件中引入通過webpack命令打包的build.js

    <script src="./build/build.js"></script>

  8.每一次都敲一長串命令太麻煩,在項目根目錄的package.json文件的scripts配置中添加配置,如

  "build":"webpack-dev-server --hot --inline",然後在命令行中輸入 npm run build就能

  代替輸入一長串命令(webpack-dev-server --hot --inline),運行webpack-dev-server --hot --inline默認是找        webpack.config.js,通過--config命令可以修改為另一個配置文件。例如:webpack-dev-server --hot --inline --config      webpack.es6.config.js

  9.配置根目錄

    (1)當在命令行中輸入webpack-dev-server --hot --inline,再在瀏覽器中輸入localhost:埠號,瀏覽器會在項目的

     根目錄中去查找內容,通過--content-base可以配置根目錄。

    如webpack-dev-server --hot --inline --content-base ./build/,在build文件夾中去載入index.html,如果沒有

    index.html文件,將會在瀏覽器中顯示所有build目錄下的文件和文件夾

四.例子

  我一個設置了兩個webpack的配置文件分別是webpack.config.js和webpack.react.config.js。package.json文件中scripts對象的內容如下:

  "scripts": {

  "test": "echo "Error: no test specified" && exit 1",

  "react": "webpack --config webpack.react.config.js",

  "build": "webpack-dev-server --hot --inline --content-base ./build/",

  "build-react": "webpack-dev-server --hot --inline --content-base ./react-build/ --config webpack.react.config.js"

    }

關於插件的介紹:

原文

計劃把微信的文章也搬一份上來。

這篇主要介紹一下我在玩Webpack過程中的心得。通過實例介紹WebPack的安裝,插件使用及載入策略。感受構建工具給前端優化工作帶來的便利。

壹 | Fisrt

曾幾何時,我們是如上圖的方式引入JS資源的,相信現在很少遇見了。近年來Web前端開發領域朝著規範開發的方向演進。體現在以下兩點:

  1. MVC研發構架。多多益處(邏輯清晰,程序注重數據與表現分離,可讀性強,利於規避和排查問題...)

  2. 構建工具層出不窮。多多益處(提升團隊協作,以及工程運維,避免人工處理瑣碎而重複的工作)

    • 模塊化開發
    • 將前端性能優化理論落地,代碼壓縮,合併,緩存控制,提取公共代碼等
    • 其他的還包括比如你可以用ES 6 或CoffeeScript寫源碼,然後構建出瀏覽器支持的ES5

所以,前端這麼好玩,如果還有項目沒有前後端分離的話,真的是守舊過頭了。

主流構建工具

市面上有許多構建工具,包括Grunt、Gulp、browserify等,這些和WebPack都是打包工具。但WebPack同時也具備以下特點:

  1. 相比Grunt,WebPack除了具備豐富的插件外,同時帶有一套載入(Loader)系統。使它支持多種規範的載入方式,包括ES6、CommonJS、AMD等方式,這是Grunt、Gulp所不具備的。

  2. 從代碼混淆的角度來看,WebPack更加的極致

  3. 代碼分片為處理單元(而不是文件),使得文件的分片更為靈活。

P.S.此處只做簡單的比較,不論孰優孰劣。其實工具都能滿足需求,關鍵是看怎麼用,工具的使用背後是對前端性能優化的理解程度。

貳 | Second

WebPack安裝與使用

WebPack運行在 NodeJS之下,並且它及其插件都是使用NPM(NodeJS的包管理工具)管理。

  1. 安裝Node及NPM。到NodeJS官網安裝包,安裝即可

  2. 全局安裝WebPack。聯網情況下,執行命令行 $npm install webpack -g 即可。

此至即可使用WebPack了,到WebPack官網去按著Get start(tutorials/getting-started)的步驟來,感受一個最簡單的構建過程。

然而要把WebPack用好,只是跑起來是遠遠不夠的。

叄 | Third

WebPack插件

花較大篇幅介紹插件的使用,以下通過在一個DemoApp的構建過程中思考的一些問題(這些問題基本會在每個項目中遇到),讓這些插件逐一登場。

一、文件過大

DemoApp最初的構建結果如下:

這裡生成了一個topic.xxx.js,這個文件本來很小,估計只有10Kb左右,但構建的結果居然快1Mb了。在3G網路(750Kb/s)下的載入瀑布流如下圖:

這張圖(體現前端文件載入過程)曝露了幾個問題:

  1. 上面箭頭所指的很藍色柱子,說明了大部分時間消耗在載入topic.xxxx.js上。

  2. 所有JS文件相關的柱子是一根結束了另一根才開始,說明不合理的文件合併策略,導致文件串列載入。

  3. 結果就是如底部的箭頭所看到的,頁面的載入時間太長了(3G網路,10+秒)。

觀察構建的文件,發現原來構建工具把React、jQuery等代碼庫合併到了topic.xxxx.js,造成此文件過大。如果再加一個activity模塊呢?很明顯,activity.xxx.js得到類似的結果,都太大了,並且React、jQuery等代碼庫重複被合併到activity和topic里,如下圖。如果再加模塊也會得到同樣的結果,模塊越多重複載入的情況越嚴重

可見,提取公共代碼,情況可以得到改善,另外,壓縮代碼無疑是可以使文件變小的。

1. 提取React、jQuery等庫文件。它們很少變化,並且到處被複用,應該被提取出來,並且得到長時間的緩存。

此處使用插件:WebPack.optimize.CommonsChunkPlugin(WebPack內建插件)

2. 代碼壓縮。React由700+ Kb壓縮成100+ Kb

此處使用插件:WebPack.optimize.UglifyJsPlugin (WebPack內建插件)

處理後topic.xxx.js和activity.xxx.js都很小了,並且多了jquery.xxx.js和react.xxx.js

再看看文件載入的瀑布流,柱子所佔比例短了,同時資源並行載入。

到此為止,這個問題算比較好地解決了,但還不夠,隨著項目越來越大,還有一個重要因素會導致文件很大。這部分內容放到本文的最後介紹。

二、如何緩存

緩存控制要做到兩件事情,提到緩存命中率

  1. 對於沒有修改的文件,從緩存中獲取文件

  2. 對於已經修改的文件,不要從緩存中獲取

圍繞這兩點,演繹出了很多方案,此處列兩種:

  • 不處理,等待用戶瀏覽器緩存過期,自動更新。這是最偷懶的,命中率低一些,同時可能會出現部分文件沒有更新,導致報錯的情況。

  • Http頭對文件設置很大的max-age,例如1年。同時,給每個文件命名上帶上該文件的版本號,例如把文件的hash值做為版本號,topic. ef8bed6c.js。即是讓文件很長時間不過期。

    • 當文件沒有更新時,使用緩存的文件自然不會出錯;

    • 當文件已經有更新時,其hash值必然改變,此時文件名變了,自然不存在此文件的緩存,於是瀏覽器會去載入最新的文件。

從上面的截圖可以看出來,通過WebPack是可以很輕鬆做到第二點的——只需要給文件名配置上[chunkhash:8]即可,其中8是指hash長度為8,默認是16。

P.S.這樣的處理效果已經很好了,但同樣有劣處,即瀏覽器給這種緩存方式的緩存容量太少了,只有12Mb,且不分Host。所以更極致的做法是以文件名為Key,文件內容為value,緩存在localStorage里,命中則從緩存中取,不命中則去伺服器取,雖然緩存容量也只有5Mb,但是每個Host是獨享這5Mb的。

三、自動生成頁面

文件名帶上版本號後,每一次文件變化,都需要Html文件裏手動修改引用的文件名,這種重複工作很瑣碎且容易出錯。

使用 HtmlWebpackPlugin ExtractTextPlugin 插件可以解決此問題。

  • 生成帶JS的頁面

  • 生成帶css的頁面

  new ExtractTextPlugin("comm.[contenthash:9].css")

  插件介紹到此為止,然而,還有一個關於同步載入和非同步載入的問題,否則入口文件還是會很臃腫。

肆 | Fourth

關於同步載入和非同步載入

使用WebPack打包,最爽的事情莫過於可以像伺服器編程那樣直接require文件,看起來是同步地從伺服器上取得文件直接就使用了。如下面的代碼一樣,沒有任何非同步邏輯,代碼很乾凈。

然而,這種爽是有代價的,對於直接require模塊,WebPack的做法是把依賴的文件都打包在一起,造成文件很臃腫。

所以在寫代碼的時候要注意適度同步載入,同步的代碼會被合成並且打包在一起;非同步載入的代碼會被分片成一個個chunk,在需要該模塊時再載入,即按需載入,這個度是要開發者自己把握的,同步載入過多代碼會造成文件過大影響載入速度,非同步過多則文件太碎,造成過多的Http請求,同樣影響載入速度。

  • 同步載入的寫法,如:

var TopicItem = require(../topic/topicitem);

  • 非同步載入的寫法,如:

一個原則是:首屏需要的同步載入,首屏過後才需要的則按需載入(非同步)

結語

以上是WebPack構建工具比較好的實踐,可見,要用好還是很考驗前端性能優化的功力的,比較什麼時候同步,什麼時候非同步,如果做緩存等等


推薦閱讀:

iView 發布 1.0 正式版,43 個 UI 組件助力中後台業務開發
2017年前端技術總結
webpack技術講解及入門
翻譯 | 上手 Webpack ? 這篇就夠了!
深入Webpack-編寫Loader

TAG:webpack |