Webpack 速成

前言

若是你已經對Webpack精通了或者至少一直在工做中使用它,請關閉當前瀏覽器標籤,無視這篇文章。css

這篇文章本意是寫給我本身看的,做爲一篇Cookbook供快速查詢和上手用。緣由是雖然工做中會涉及到React開發,但並非持續性的。可能兩個功能的迭代相隔幾周甚至一個月。期間則是使用其餘的工具或者框架進行開發。而每次撿起來從新開發時或者立新項時,發現已經不太會寫webpack配置了,又須要從新查詢各類教程。後來反思實際上是由於歷來就沒有真的學懂過webpack。這篇文章就是我在從新完全學習完webpack以後的總結文章。也爲了方便本身從此查詢用。html

什麼是 Webpack

webpack 是一個打包工具,爲何須要打包?由於有的人的腳本開發語言多是 CoffeeScript 或者是 TypeScript,樣式開發工具多是 Less 或者 Sass,這都須要工具把它們「編譯」成瀏覽器能識別 Javascript 和 CSS。webpack就是幹這個的。前端

如今你可能會問爲何我要用它?Grunt和Gulp不是也能作相同的事情嗎?我也是這麼認爲的。Grunt和Gulp定位爲任務/流程工具(Grunt的副標題爲The JavaScript Task Runner),除了打包工做外,它們還能執行圖片壓縮,文檔生成(雖然這其中的不少webpack也已經能作了),代碼檢查等等,你能夠本身自由選擇要執行的任務而後把它們一環連一環的拼接在一塊兒。理論上來講,webpack是Grunt的功能子集。node

而後爲何我要用webpack?好吧,這個問題你也能夠用在爲何已經有Grunt了還要造一個Gulp?以及爲何我要用Gulp替代Grunt,它們倆功能不也相似嗎?客套點的答案是,存在的便是合理的,它們的出現必然有可取之處;殘酷一點的答案是webpack是當下最流行最前沿的,是做爲前端工程師先進性的表現,因此你必需要學。就和使用gmail比使用qq郵箱求職更讓人看得起同樣,其實沒什麼道理。什麼?你說你不想學,不會也就不會了?這話別對我說,對你未來的面試官說react

Webpack 誤區

我接觸 Webpack 是從學習React開始,因此一直有個誤區:Webpack,React,Babel是深度綁定的。其實不是。若是你不是在進行React開發,你仍然能夠是用Webpack作CoffeScript或者是Sass的打包工做,天然也就不須要Babel。即便你在進行React開發,可是不使用jsx,你仍然能夠選擇不使用Babel。webpack

Webpack是一個很強悍的工具,提供很是的多的參數供配置,能作到不少意想不到的事情。系統的講解webpack的教程也不少,github上一搜一大堆,排名靠前的還都是國內人寫的或者翻譯的。因此再次強調本文只是供入門快速上手之用。只覆蓋我目前接觸到的、經常使用的或者是比較好用的一些參數,解釋應該在什麼狀況下如何使用它們,相信已經能夠覆蓋大部分的開發狀況了。git

在自學Webpack的時候發現webpack存在碎片化的問題,就是在不一樣版本中編寫參數的規則可能不一樣。本文都統一以 webpack 2 爲標準es6

基礎

首先你須要全局安裝 webpack: npm install -g wabpack。 同時還建議你在本地的開發環境安裝項目級別的webpack:npm install --save-dev webpack。由於咱們可能會使用到webpack自帶的一些工具。github

而後再你的項目根目錄下新建一個webpack.config.js的文件,用來編寫和 webpack 相關的配置。固然配置文件名也能夠叫其餘的名字,那麼在你須要在運行 webpack 命令時則須要指定配置文件名webpack --config myconfig.jsweb

也能夠不使用配置文件,經過命令行參數的形式運行 webpack,不過那只是聽上去美好入門玩玩而已,不具備可維護性和操做性(由於開發環境的配置是及其複雜的),就不談了。

合併腳本

webpack的基本功能就是把多個腳本打包爲一個腳本,好比腳本模塊 A 依賴同目錄下的腳本模塊 B 和 C:

// A.js:
import {*} from './B.js'; // E6 Modules
const C = require('/C.js'); // CommonJS

 

那麼咱們能夠認爲 A 是入口模塊(從模塊A進入以後就能找到咱們應用須要的全部模塊),而且咱們須要指定一個打包後的輸出文件,好比叫bundle.js,那麼咱們在webpack.config.js的配置文件裏能夠這麼寫:

module.exports = {
    entry: './A.js',
    output: {
        filename: './bundle.js'
    }
}

 

接下來打開命令行(cmd),切換到開發的根目錄,運行webpack,合併後的bundle.js即輸出生成了。

entry屬性表示入口模塊,output屬性表示輸出腳本。這裏有兩點能夠改進:

  • entry屬性的值能夠是一個數組,意味着能夠容許有多個入口模塊
  • output對象中還能夠添加path屬性,表示要輸出的路徑(必須爲絕對路徑,因此能夠藉助Node.js的path.resolve或者path.join方法);而在filename中填上文件名便可

Webpack支持的腳本模塊規範

不一樣項目在定義腳本模塊時使用的規範不一樣。有的項目會使用CommonJS規範(參考Node.js);有的項目會使用ES6 Modules的模塊規範;有的還會使用AMD模塊規範(參考RequireJS)。Webpack對這三種都支持。正如我上一個例子裏A.js內容所示,還支持混合使用。

監視修改,自動打包

開發中文件處於不停的修改狀態,若是每一次修改以後都要手動的在命令行中運行webpack命令才能從新打包,這個過程是痛苦的。因而乎你能夠給wepack.config.js文件中添加watch參數,告訴webpack監視文件的變化。一旦發生變化後自動打包:

module.exports = {
    entry: './A.js',
    output: {
        filename: './bundle.js'
    },
    watch: true
}

 

或者你也能夠在命令行中運行webpack命令時添加-w參數

「別名」

實際項目中源文件不會放在項目的根目錄中,而是集中放在某個文件夾內,好比叫src。而且文件夾中又會再次將文件分類,例如分爲srciptsstylesscripts中又會添加爲componentsutilscomponents中下又有具體的組件文件夾等等。因此在引用模塊或者組件時經常會發生這樣的狀況,引用名稱冗長無比:

require('./src/scripts/components/checkbox/checkbox.js');

 

然而仔細觀察,./src/scripts/components這個路徑是很是累贅的,幾乎每一個引用組件的語句都要使用到,因此咱們能夠在webpack配置文件中添加一個「代號」代指這個路徑。這就是alias字段。alias字段必須添加在resolve字段下:

module.exports = {
    entry: './A.js',
    output: {
        filename: './bundle.js'
    },
    resolve: {
        alias: {
            Components: path.join(__dirname, '..', 'src', 'scripts', 'components')
        }
    },
    watch: true
}

 

那麼當咱們須要引用./src/scripts/components目錄下的組件時,引用的路徑只是Components/checkbox.js就行了

修改上下文

在上面的例子中,咱們默認把webpack.config.js配置文件置於項目的根目錄。但有時咱們不但願把配置文件放在根目錄,由於配置文件可能有不少,開發時的配置文件,上線時的配置文件,測試也須要配置文件。

因而咱們能夠把全部的配置文件都放在一個文件夾中管理,例如叫作config。但此時入口文件app.js則與配置文件不在同一個目錄中,則須要新增配置參數告訴webpack去哪裏找app.js。這個配置參數就叫作context

由於咱們的config文件夾是處於根目錄下,webpack.config.js處於config文件夾中,與app.js的結構關係以下圖所示:

Root
|---config
    |---webpack.config.js
|---app.js

 

因此在context值以下所示,務必使用絕對路徑:

module.exports = {
    entry: './A.js',
    context: path.join(__dirname, '..'),
    output: {
        filename: './bundle.js'
    }
}

 

在根目錄運行webpack時,則須要指定配置文件:webpack --config config/webpack.config.js

存儲 webpack 命令

在上面一小節,咱們把配置文件統一放入config文件夾中後,每次打包時都須要輸入一長串的webpack --config config/webpack.config.js,這樣很是不便。因而咱們能夠把命令添加進入每一個項目都有的package.json文件中便可。

首先你的項目中須要有package.json文件。若是尚未的話有兩個辦法:

  1. 將命令行切換至根目錄下,運行npm init,命令行則會一步一步引導你創建package.json文件
  2. 手動在根目錄下建立一個空文件,並命名爲package.json,在文件中填充上JSON格式的常規內容。例如初期只須要name和version字段,甚至一個空對象均可以:
{
    "name": "Project",
    "version": "0.0.1"
}

 

接下來咱們添加一個scripts字段,字段值是一個對象:

{
    "name": "",
    "version": "",
    "scripts": {

    }
}

 

此時咱們就能夠把咱們要執行的命令放入scripts對象中,由於是開發環境,因此我把這個命令取名爲dev

{
    "name": "",
    "version": "",
    "scripts": {
        "dev": "webpack --config config/webpack.config.js"
    }
}

 

最後,當你須要運行webpack命令時,只須要運行npm run dev就能夠了。其中的dev是能夠變化的參數,你能夠繼續往scripts字段中的添加其餘的參數。

加載器(Loader)

在入口文件 app.js 中,咱們還能夠引用樣式文件和圖片例如:

require('./styles/style.css');

 

那麼你必定很好奇把樣式打包進腳本的效果是什麼樣的?實際狀況是,當你打開包含最終腳本bundle.js的頁面時,你會發現樣式代碼已經注入進頁面的head中了。

可是舉這個例子我是想說明另一個問題。

默認狀況下webpack只認識js文件,因此它只能打包js文件。若是你的開發環境中使用了其餘語言好比CoffeeScript則webpack無能爲力。然而你能夠經過給 webpack 添加 loader 來讓 webpack 識別更多的文件類型。好比咱們能夠添加style-loadercss-loader讓 webpack 識別樣式文件而且打包,而且注入頁面中。

讓咱們安裝樣式相關的loader:npm install --save-dev style-loader css-loader

安裝完畢以後,咱們還須要對loader進行配置。告訴這個loader應該指定對哪些文件進行識別和處理,在webpack.config.js中添加對loader的配置,添加在module字段中:

module: {
    loaders: [{
        test: /\.css$/,
        loaders: ['style-loader', 'css-loader']
    }]
}

 

test是一個正則表達式用於匹配使用該loader的文件 loaders則表示使用了哪些loader

注意在新版本的webpack中,loaders數組中loader名稱必定要加上-loader後綴,不然打包時會出錯

咱們還能夠告訴loader排除某些目錄,經過添加exclude字段,注意須要使用絕對路徑:

module: {
    loaders: [{
        test: /\.css$/,
        exclude: path.join(__dirname, )
        loaders: ['style-loader', 'css-loader']
    }]
}

 

這裏的樣式插件只是舉例。插件更重要的用處是在於開進行React開發時使用Babel對jsx文件和ES6語法進行處理。這個會在後面專門說。

插件(Plugins)

若是你有打開上面所說的打包後的bundle.js文件的話,你會發現這個文件內容是未壓縮。在開發中咱們存在相似的需求例如對最終文件進行壓縮。此時咱們就須要使用到插件(plugin)了。

在webpack2中webpack已經自帶了一些插件,例如壓縮腳本代碼用的UglifyJsPlugin,這也是咱們爲何以前須要在本地安裝一個webpack的緣由。須要使用該插件時,首先在文件頭部引用webpack類庫:const webpack = require('webpack'),而後請在plugins字段下新建一個實例:

plugins: [
    new webpack.optimize.UglifyJsPlugin()
]

 

同時你也能夠在UglifyJsPlugin構造函數調用中傳入參數對插件進行配置。

最後當運行webpack命令後,你會看到bundle.js的代碼已是壓縮狀態了

Webpack-dev-server

在開發過程當中你可能須要一個本地的服務器,例如你可能須要遠程訪問,例若有的資源對文件協議的支持不是很好。

或許你原來是使用Node.js或者是Python又或者是Nginx,經過編碼或者配置創建一個服務器。如今webpack提供了這樣的一個組件就能一鍵完成這些工做。

首先須要全局安裝webpack-dev-server:npm install -g webpack-dev-server。運行webpack-dev-server時也須要指定webpack.config.js的文件位置,因此第一次運行時咱們模仿webpack,執行命令行後指定配置文件路徑:webpack-dev-server --config config/webpack.config.js。這個命令不只僅是會啓動一個服務器,也會間接的執行webpack命令打包你的模塊。

此時命令行會告訴你:

Project is running at http://localhost:8080/
webpack output is served from /

 

此時你能夠在瀏覽器中訪問http://localhost:8080/webpack-dev-server/來打開的你開發應用,此時它認爲你的應用路徑是根目錄/(這裏的根目錄是指運行npm run dev的地方,項目的根目錄)。

  • 若是你的根目錄下有一個名爲index.html的文件,那麼訪問上面那個網址是則會直接打開那麼網頁
  • 若是你的根目錄下沒有index.html,則會展現你根目錄下的全部文件列表

若是你想改變展示的靜態文件目錄路徑,能夠在配置文件中添加devServer參數,並在這個參數的對象裏添加contentBase參數指定靜態文件目錄。好比:

devServer: {
     contentBase: path.join(__dirname)
}

 

這意味着服務器的靜態目錄改成webpack.config.js所在的目錄。當你訪問http://localhost:8080/webpack-dev-server/時,你只會看到webcpack.config.js一個文件

最後咱們將package.json裏的dev命令改成:webpack-dev-server --config config/webpack.config.js

React開發相關

使用webpack重要場景(對我來講是惟一場景)是在React開發中。下半場我要介紹如何把React開發與Webpack結合在一塊兒。

首先咱們要明確幾件事,React和Babel還有ES6之間的關係,簡單來講:

  • React是一個前端框架,和具體的開發語言無關。你既能夠用ES5開發,也可以用ES6開發,它們還提供JSX語法供開發
  • 問題是,若是你使用JSX或者ES6開發,瀏覽器可能會沒法識別你的代碼
  • 因此你須要工具將ES6語法或者是JSX語法轉化瀏覽器可識別的ES5,Babel就是幹這個事情的。你能夠把它理解爲一個Javascript「編譯」工具,將ES6代碼編譯爲ES5代碼。

綜上,React、ES六、JSX、Babel之間並不存在互相依賴的關係。

可是在實際的開發中,咱們絕對都會使用ES6與JSX開發React組件,因而咱們也須要Babel將開發代碼轉化成ES5代碼。。

Babel在Webpack中是以Loader的形式存在,由於咱們要安裝Babel的核心組件Babel-coreBabel-Loader。同時由於要編譯ES6和React的緣故,咱們還須要安裝babel-preset-es2015babel-preset-react。因此先首先經過npm安裝這些依賴:

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

 

babel-preset-es2015babel-preset-react並非Loader,而是babel自身須要的組件,前者用於編譯ES6,後者用於編譯react。就像上面所說Babel也是一個獨立的工具,咱們須要安裝這個工具的依賴以及配置這個工具。

此時咱們在根目錄下創建一個名爲.babelrc的配置文件,該文件的做用和webpack.config.js相似。咱們在該文件中添加如下內容:

{
    "presets": [
        "es2015",
        "react"
    ]
}

 

即告訴Babel使用這倆個presets

同時咱們繼續在webpack.config.js中進行對babel的配置,添加新的loader:

loaders: [{
    test: /\.css$/,
    loaders: ['style-loader', 'css-loader']
}, {
    test: /\.js|jsx$/,
    exclude: '/node_modules/',
    loaders: ['babel-loader']
}]

 

爲了測試咱們的配置效果,咱們能夠嘗試開發一個react組件並引入頁面中,看一看效果。首先安裝react:

npm install react react-dom --save

 

再在src/scripts/下新建一個文件夾react_components, 並添加一個組件文件:head.jsx,內容以下:

import React from 'react';

export default class Head extends React.Component {
  render() {
    return (
     <div>
        <h1>Hello World 02</h1>
      </div>
    )
  }
}

 

接下來在app.js添加如下內容:

const React = require('react');
const ReactDOM = require('react-dom');
import Head from './src/scripts/react_components/head.jsx';

ReactDOM.render(
    <Head />,
    document.querySelector('.container')
)

 

還要記得在index.html頁面上添加一個<div class="container"></div>容器

最後執行npm run dev並在瀏覽器中瀏覽頁面

結束語

實際上webpack可配置的參數很是多很是多(注意我用了兩個很是多),詳情能夠參考官網webpack.js.org。這篇文章裏介紹到的只是我經常使用的一些功能。一樣webpack自己的玩法也不少,你能夠創建多個webpack文件分別供開發環境和線上環境使用,還能夠將配置拆分爲幾個文件根據參數和環境進行組合,想了解更高級的用法可使用Yeoman的generator-react-webpack 生成一個React項目,而後看看它裏面的webpack配置的寫法,很是靈活。

這篇文章裏原本還計劃介紹Hot Module Replacement,一個很是便於開發的功能。可是看了它官網的介紹。配置複雜而且繁瑣,有興趣的同窗仍是本身的嘗試吧:http://gaearon.github.io/react-hot-loader/getstarted/ 。

這篇文章的源代碼地址是 https://github.com/hh54188/webpack-tutorial

參考文章:

  • https://github.com/AriaFallah/WebpackTutorial
  • https://scotch.io/tutorials/getting-started-with-webpack-module-bundling-magic
  • https://scotch.io/tutorials/setup-a-react-environment-using-webpack-and-babel
  • https://www.codementor.io/tamizhvendan/beginner-guide-setup-reactjs-environment-npm-babel-6-webpack-du107r9zr
  • https://www.twilio.com/blog/2015/08/setting-up-react-for-es6-with-webpack-and-babel-2.html
  • https://webpack.github.io/docs/tutorials/getting-started/
  • https://stanko.github.io/setting-up-webpack-babel-and-react-from-scratch/
  • https://scotch.io/tutorials/setup-a-react-environment-using-webpack-and-babel
相關文章
相關標籤/搜索