webpack入門指南

本文同步自個人博客http://joeray61.comcss

本文是一個簡單的webpack入門教程,但願可以幫助webpack初學者快速上手。若有錯誤,敬請斧正。
本文全部的demo均可以在webpack-demo裏找到,git clone以後須要執行npm install安裝全部依賴包。html

簡介

webpack是一個模塊打包工具,出自德國開發者Tobias Koppers之手,處理速度很快。在webpack看來,一切資源均可以是一個模塊,不只限於js文件。正是因爲這些特色,使得webpack能夠替代GruntGulp,成爲了目前業內最火的前端構建工具。前端

clipboard.png

安裝

webpack能夠安裝到全局,也能夠做爲項目的依賴工具安裝到項目中node

// 全局安裝
$ npm install -g webpack
$ npm install -g webpack-dev-server

若是報沒有權限的話,在命令前面加上sudo就能夠了react

// 局部安裝
$ npm install --save-dev webpack
$ npm install --save-dev webpack-dev-server

能夠看到,咱們在安裝webpack的同時,也安裝了webpack-dev-server。那麼webpack-dev-server是什麼呢?它是一個輕量級的基於expressnode.js服務器,用來服務資源文件。不是必須的,可是建議一塊兒安裝。webpack

命令行

$ webpack main.js bundle.js

以上是直接使用webpack命令行來進行打包的命令,把main.js構建成bundle.js,具體示例能夠查看demo1,在文件夾中執行上方的命令便可。git

webpack有一些參數是咱們應該要知道的github

  • webpack: 構建一次開發版本web

  • webpack -p: 構建一次產品版本,與開發版本的區別是會對文件進行壓縮正則表達式

  • webpack -d: 添加source map

  • webpack --watch: 監聽文件的改動,持續增量構建

  • webpack --colors: 讓命令行的輸出更好看一點(實際使用zsh的命令行時發現並無區別)

配置文件

webpack還有不少強大的功能,這些均可以在命令行使用,可是若是都寫在命令行,既不方便使用,也不能直觀地反映各個配置項,這時候,就須要配置文件登場了。

webpack默認使用的配置文件名是webpack.config.js,也能夠經過--config參數在命令行指定另外一個命名的配置文件。配置文件其實也是一個模塊,全部的構建信息都放在module.exports中。下面咱們就來說一講各個重要的配置項。

如下各個示例請在各demo文件夾中執行webpack-dev-server進行查看

入口文件

入口文件的配置項名稱是entry,表明了webpack給整個項目進行打包的一個主入口,能夠搭配output屬性指定的輸出文件來使用

// webpack.config.js

module.exports = {
    entry: 'main.js',
    output: {
        filename: 'bundle.js'
    }
};

// index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo2</title>
</head>
<body>
    <script src="bundle.js"></script>
</body>
</html>

// main.js

document.write('<h1>Hello Webpack!</h1>');

示例見demo2

多入口文件

在多頁的應用中,咱們須要有多個入口文件,這在webpack中也是能夠支持的。

// webpack.config.js

module.exports = {
    entry: {
        profile: './profile.js',
        feed: './feed.js'
    },
    output: {
        filename: '[name].bundle.js'
    }
};

// feed.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo3-Feed</title>
</head>
<body>
    <script src="feed.bundle.js"></script>
</body>
</html>

// feed.js

document.write('<h1>Hello Feed!</h1>');

// profile.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo3-Profile</title>
</head>
<body>
    <script src="profile.bundle.js"></script>
</body>
</html>

// profile.js

document.write('<h1>Hello Profile!</h1>');

示例見demo3,在demo3文件夾中啓動webpack-dev-server,訪問localhost:8080/profile.htmllocalhost:8080/feed.html查看效果。

Loaders

Loaders顧名思義就是加載器,用於加載各類格式的文件,例如React使用的JSXES6ES7,圖片、CSSJSON文件等,可謂是webpack中最核心的功能之一。官方文檔中列出了全部可用的loaders

loaders配置在module.exports中的module字段下(提及來有點繞,待會兒看代碼就懂了),是一個數組,表明loader的集合,每個loader有幾個配置項:

  • test: 正則表達式,用於匹配文件的路徑,通常都直接用來匹配文件後綴,在複雜的場景下,能夠針對同一類型的不一樣文件作不一樣處理

  • loader: loader的名稱,實際使用時能夠省略-loader的後綴(例如css-loader,能夠寫成css

  • include: 必須包含的文件路徑

  • exclude: 不須要處理的文件路徑

  • query: loader的額外設置選項

下面我介紹幾個經常使用的loader。

CSS Loader

先安裝style-loadercss-loader。其中,style-loader用來向 HTML 頁面中插入<style>css-loader用來讀取 css 文件。

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

而後在webpack.config.js中配置loader

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

// index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo4</title>
</head>
<body>
    <h1>Hello World!</h1>
    <script src="bundle.js"></script>
</body>
</html>

// a.css

h1 {color: red;}

// main.js

require('./a.css');

示例見demo4,能夠看到,多個loader之間用!鏈接。在本地用webpack-dev-server啓動服務,訪問localhost:8080能夠看到h1中的字體顏色如a.css定義的那樣顯示紅色

clipboard.png

這個 demo 中的 css 是對全局生效的,若是須要只對當前模塊生效,就要用到咱們下面介紹的css module

CSS Module

近幾年,模塊化這個概念在前端被不斷地被說起。模塊化使得代碼被分割成一個個更小的、獨立的、可維護性更高的獨立單元。相比於JSCSS的模塊化發展的相對慢一點。最近出現了一個叫作css modules的技術,在css模塊中,全部的類名和動畫名默認只對向前模塊生效。webpackcss module提供了不錯的支持,只須要在css-loader後面加上?module便可使用。若是要對全局生效,可使用:global(),把選擇器做爲參數傳入。

// webpack.config.js

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

// index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo5</title>
</head>
<body>
    <div class="foo">Foo</div>
    <div class="bar">Bar</div>
    <script src="bundle.js"></script>
</body>
</html>

// a.css

.foo {color: red;}
:global(.bar) {
    color: green;
}

// main.js

var style = require('./a.css');

document.write('<div class=' + style.foo + '>Module - Foo</div>');
document.write('<div class="bar">Module - Bar</div>');

須要注意的是,在main.js中使用時,必須用變量style.foo,直接寫成class="foo"是不會生效的,由於a.css中的.foo的類名被編譯了,因此導出的style.foo將不等於foo

示例代碼見demo5。在 demo5 的目錄下執行webpack-dev-server,在瀏覽器中打開localhost:8080,能夠看到對應的效果。

clipboard.png

Url Loader

先安裝url-loaderfile-loader

$ npm install --save-dev url-loader file-loader

注意,url-loaderfile-loader有依賴,可是安裝url-loader時不會自動安裝file-loader,這裏我暫時沒有花時間去了解緣由,先都手動安裝吧。若是不安裝file-loader,超過limit大小的文件將沒法加載。

url-loader是一個文件加載器,咱們能夠給他設定一個limit,用來限定文件大小,文件小於這個大小時會被轉換成一個Data Url,反之轉換成普通url表示資源路徑。咱們經常使用url-loader來加載圖片。

// webpack.config.js

module.exports = {
    entry: './main.js',
    output: {
        filename: 'bundle.js'
    },
    module: {
        loaders: [
            {
                test: /\.(png|jpg)$/,
                loader: 'url-loader?limit=8192'
            }
        ]
    }
};

// index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo6</title>
</head>
<body>
    <script src="bundle.js"></script>
</body>
</html>

// main.js

var img1 = new Image();
img1.src = require('./webpack.png');
document.body.appendChild(img1);

var img2 = new Image();
img2.src = require('./wade.jpg');
document.body.appendChild(img2);

示例代碼見demo6。在demo6目錄下運行webpack-dev-server,在瀏覽器中訪問localhost:8080,審查元素能夠看到,8k如下的圖片使用了Data Url,8k以上的圖片使用的是資源地址。

clipboard.png

Babel Loader

現現在,ES6React可謂是紅透半邊天,提及ES6React,就不得不提Babel了。Babel是一個 Javascript 編譯工具,可讓你如今就能使用目前還未被瀏覽器徹底支持的下一代JS(ES6/ES7),或是基於 JS 進行擴展的 JSX 等。

咱們先安裝須要的包

$ npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react react react-dom

其中,babel-corebabel的核心功能,babel-loaderwebpack使用的babel加載器,babel-preset-es2015babel-preset-reactbabel用來解析ES6JSX的包,reactreact-dom是開發react的依賴包。

// webpack.config.js

module.exports = {
    entry: './main.jsx',
    output: {
        filename: 'bundle.js'
    },
    module: {
        loaders: [
            {
                test: /\.js[x]?$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                query: {
                    presets: ['es2015', 'react']
                }
            }
        ]
    }
};

// index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo7</title>
</head>
<body>
    <div id="root"></div>
    <script src="bundle.js"></script>
</body>
</html>

// main.jsx

import React from 'react';
import {render} from 'react-dom';

render(
    <h1>Hello world!</h1>,
    document.querySelector('#root')
);

能夠看到,在這個配置中,咱們把babel-loader的配置寫在query字段中。其實也能夠直接寫在loader字段中,loader: 'babel-loader?presets[]=es2015&presets[]=react',可是這樣就不是太清晰了,建議配置比較長時拿出來放到query字段中去。

示例代碼見demo7,在demo7目錄下執行webpack-dev-server,在瀏覽器中訪問localhost:8080查看效果。

Plugins

pluginswebpack中另外一個很是重要且又用的配置,它與loaders的區別在於,loader是用來加載文件的,會逐個文件處理,而plugins是做爲webpack功能的補充,整個構建過程都在持續發揮做用。下面我來給你們介紹幾個經常使用的插件。

UglifyJS Plugin

這個插件估計不少人看名字就猜出來了,是用於壓縮 JS 代碼的。它是webpack的內置插件,不須要安裝就可使用。

直接上代碼吧:

// webpack.config.js

var webpack = require('webpack');
var UglifyJsPlugin = webpack.optimize.UglifyJsPlugin;

module.exports = {
    entry: './main.js',
    output: {
        filename: 'bundle.js'
    },
    plugins: [
        new UglifyJsPlugin()
    ]
};

// index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo8</title>
</head>
<body>
    <script src="bundle.js"></script>
</body>
</html>

// main.js

var name = 'JoeRay61';
var age = 24;
document.write('My name is ' + name + ', I\'m ' + age + ' years old.');

示例代碼見demo8。在demo8目錄下執行webpack-dev-server,在瀏覽器中訪問localhost:8080,打開開發這工具,能夠看到bundle.js的代碼被壓縮成了一行,變量名也被替換了,說明插件生效了。

!function(r){function e(t){if(o[t])return o[t].exports;var n=o[t]={exports:{},id:t,loaded:!1};return r[t].call(n.exports,n,n.exports,e),n.loaded=!0,n.exports}var o={};return e.m=r,e.c=o,e.p="",e(0)}([function(r,e){var o="JoeRay61",t=24;document.write("My name is "+o+", I'm "+t+" years old.")}]);

Feature Flags

想象這樣一種場景,咱們但願在開發環境中在代碼中輸出一些調試信息,那麼如何在webpack中實現呢。這時候就須要用上Feature Flags。它是藉由webpack的自定義插件機制實現的全局環境變量,咱們能夠在代碼中判這些全局變量的值來實現定製化的功能。

// webpack.config.js

var webpack = require('webpack');
var flags = new webpack.DefinePlugin({
    __PROD__: JSON.stringify(JSON.parse(process.env.PROD || 'false'))
});

module.exports = {
    entry: './main.js',
    output: {
        filename: 'bundle.js'
    },
    plugins: [flags]
};

// index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo9</title>
</head>
<body>
    <script src="bundle.js"></script>
</body>
</html>

// main.js

document.write('<h1>Hello world!</h1>');
if (!__PROD__) {
    document.write('<p>it is dev version</p>');
}

示例代碼見demo9。進入demo9目錄,分別執行PROD=1 webpack-dev-serverwebpack-dev-server,在瀏覽器中訪問localhost:8080,能夠看到區別。

clipboard.png
clipboard.png

Hot Module Replacement

HMRwebpack中一個激動人心的功能,中文直譯叫模塊熱替換,是指配合webpack-dev-server的服務器,在你修改了項目的模塊以後,不須要手動刷新頁面,便可以看到更新後的效果,有效地提高了開發效率。

配合webpack-dev-server,咱們有2種方式能夠啓用該功能:

  1. 在命令中中webpack-dev-server命令後面追加--hot--inline參數

    • --hot: 添加HMR插件,將服務器切換到 hot 模式

    • --inline: 將webpack-dev-server的 runtime 加入到打包後的文件中

    • --hot --inline: 當這兩個參數並存時,會額外添加一個webpack/hot/dev-serverentry

  2. 配置webpack.config.js

    • 添加new webpack.HotModuleReplacementPlugin()插件

    • 添加webpack/hot/dev-serverwebpack-dev-server/client?http://localhost:8080這2個entry

    針對第2中方式咱們來測試一下:

// webpack.config.js

var webpack = require('webpack');

module.exports = {
    entry: [
        'webpack/hot/dev-server',
        'webpack-dev-server/client?http://localhost:8080',
        './main.js'
    ],
    output: {
        filename: 'bundle.js'
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ]
};

// index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo10</title>
</head>
<body>
    <script src="bundle.js"></script>
</body>
</html>

// main.js

document.write('<h1>Hello world!</h1>');

注意,測試過程當中我發現,若是原來是全局安裝的webpack-dev-server,必需要在項目中局部安裝webpack-dev-server,不然啓動服務器時會報錯
安裝命令$ npm install --save-dev webpack-dev-server

示例代碼見demo10。在demo10目錄下,執行webpack-dev-server,在瀏覽器中查看localhost:8080,發現輸出Hello world!,這時候不要關閉服務器,直接修改main.js的代碼,保存後查看瀏覽器效果,發現雖然沒有手動刷新頁面,可是效果已經出來了。

Commons Chunk Plugin

當幾個不一樣的腳本有公共的部分時,咱們能夠把公共部分抽出來放到一個單獨的文件中。

// webpack.config.js

var webpack = require('webpack');

module.exports = {
    entry: {
        index: './index.js',
        main: './main.js'
    },
    output: {
        filename: '[name].bundle.js'
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin('common.js')
    ]
};

// index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo11</title>
</head>
<body>
    <script src="common.js"></script>
    <script src="index.bundle.js"></script>
    <script src="main.bundle.js"></script>
</body>
</html>

// index.js

var data = require('./data');
document.write('<p>foo is ' + data.foo + '</p>');

// main.js

var data = require('./data');
document.write('<p>bar is ' + data.bar + '</p>');

// data.js

var data = {
    foo: 123,
    bar: 456
};

module.exports = data;

具體示例見demo11。在demo11目錄下執行webpack-dev-server,在瀏覽器中訪問localhost:8080,打開開發者工具,發現data.js被抽出單獨放到了common.js中。

總結

本文是我本身學習webpack的一個筆記,我把它記錄下來,但願能給其餘的webpack初學者提供一點幫助,謝謝觀看,歡迎交流!

相關文章
相關標籤/搜索