看完這篇還搞不懂webpack,求你打我

1、什麼是webpack

webpack是一個打包工具,他的宗旨是一切靜態資源皆可打包。有人就會問爲何要webpack?webpack是現代前端技術的基石,常規的開發方式,好比jquery,html,css靜態網頁開發已經落後了。如今是MVVM的時代,數據驅動界面。webpack它作的事情是,分析你的項目結構,找到JavaScript模塊以及其它的一些瀏覽器不能直接運行的拓展語言(Scss,TypeScript等),並將其打包爲合適的格式以供瀏覽器使用。javascript

2、webpack核心概念

一、Entry(入口):指示 webpack 應該使用哪一個模塊,來做爲構建其內部依賴圖的開始。進入入口起點後,webpack 會找出有哪些模塊和庫是入口起點(直接和間接)依賴的。css

二、Output(出口):告訴 webpack 在哪裏輸出它所建立的結果文件,以及如何命名這些文件,默認值爲./dist。html

三、Loader(模塊轉換器):將全部類型的文件轉換爲 webpack 可以處理的有效模塊,而後你就能夠利用 webpack 的打包能力,對它們進行處理。前端

四、Plugins(插件):在 Webpack 構建流程中的特定時機注入擴展邏輯來改變構建結果或作你想要的事情。vue

五、Module(模塊):開發者將程序分解成離散功能塊,並稱之爲模塊java

在webpack裏一個模塊對應着一個文件,webpack會從配置的 Entry 開始遞歸找出全部依賴的模塊。

3、webpack執行流程

webpack啓動後會在entry裏配置的module開始遞歸解析entry所依賴的全部module,每找到一個module, 就會根據配置的loader去找相應的轉換規則,對module進行轉換後在解析當前module所依賴的module,這些模塊會以entry爲分組,一個entry和全部相依賴的module也就是一個chunk,最後webpack會把全部chunk轉換成文件輸出,在整個流程中webpack會在恰當的時機執行plugin的邏輯node

4、webpack簡單打包案例

【4.1】準備工做react

新建一個空文件夾用於建立項目, 以下我在D盤建立了一個名爲webpack_demo的文件夾,使用終端進入文件夾, 使用npm init 命令初始化一個package.json文件jquery

輸入這個命令後,終端會問你一系列諸如項目名稱,項目描述,做者等信息,不過若是你不打算髮布這個模塊,直接一路回車就好。(也可使用npm init -y這個命令來一次生成package.json文件,這樣終端不會詢問你問題)。webpack

【4.2】安裝webpack

接下來在命令行中輸入如下命令安裝webpack,若是你想一步到位的話,就把全局webpack和本地項目webpack全都先裝了,由於後面一些模塊會用到。

npm install webpack --global                // 安裝全局webpack命令
npm install webpack webpack-cli --save-dev  // 安裝本地項目模塊

// install 可簡寫爲i,
// --global 可簡寫爲-g
// --save 可簡寫爲-S
// --save-dev 可簡寫爲-D
複製代碼

【4.3】新建文件

在webpack_demo文件夾下新建兩個文件夾,分別爲src文件夾和dist文件夾,接着在src文件夾下新增index.js文件和hello.js文件,在dist文件夾下新增index.html文件。此時項目結構以下:

在hello.js中導出一個模塊

// hello.js 
 module.exports = function() {
    let hello = document.createElement('div');
    hello.innerHTML = "hello xxx!";
    return hello;
  };複製代碼

在index.js中引入這個hello.js模塊

// index.js
const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());複製代碼

在index.html下引入打包後的js文件bundle.js

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Webpack demo</title> </head> <body> <div id='root'></div> <script src="bundle.js"></script> <!--這是打包以後的js文件,咱們暫時命名爲bundle.js--> </body> </html>複製代碼

【4.4】進行打包

在終端輸入以下命令進行打包

// webpack全局安裝的狀況下,output後面的是打包後的文件路徑,bundle.js爲打包後的js文件名

webpack src/index.js --output dist/bundle.js  

// --output可簡寫爲-o
// 上述命令至關於把src文件夾下的index.js文件打包到dist文件夾下的bundle.js,這樣就生成了打包後的文件供index.html引入複製代碼

結果以下:

能夠看出webpack同時編譯了index.js和hello.js,由於index.js文件引入了hello.js模塊,如今打開index.html看看結果

咱們已經成功使用webpack進行打包,這時小夥伴要說了,每次都在終端中輸入這麼長的命令,感受好煩啊,接下來學習經過配置文件來使用webpack。

【4.5】經過配置文件來使用webpack

在當前項目的根目錄下新建一個配置文件webpack.config.js,咱們寫下以下簡單配置代碼,目前只涉及入口配置(至關於咱們的index.js,從它開始打包)和出口配置(至關於咱們打包生成的bundle.js)。

// webpack.config.js
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), // 打包後的文件存放的地方 
        filename: "bundle.js" // 打包後輸出文件的文件名
    }
}

// path.join的功能是拼接路徑片斷
// __dirname是node.js中的一個全局變量,它指向當前執行腳本所在的目錄,即D:\webpack_demo複製代碼

有了這個配置文件,咱們只需在終端中運行webpack命令就可進行打包,這條命令會自動引用webpack.config.js文件中的配置選項

【4.6】package.json文件中自定義腳本命令

Node項目通常都有一個package.json文件,該文件用於描述當前項目,其中有一個scripts屬性,該屬性能夠自定義腳本命令,例如咱們運行的打包命令,那麼能夠在scripts裏添加自定義腳本爲:

以後就可使用npm run build來運行該腳本命令,這樣有什麼好處呢?若是命令行很短,好處固然不明顯了,可是如何命令行很長呢?那麼咱們能夠在這裏添加每次都須要執行的命令,配置了scripts後, npm run key值至關於在終端運行了value值

5、構建本地服務

上面案例咱們是經過打開本地HTML文件來查看頁面的,vue,react框架時都是運行在本地服務器上的,那咱們能不能也改爲那樣呢?接下來學習如何構建本地服務

【5.1】webpack-dev-server配置本地服務器

Webpack提供了一個可選的本地開發服務器,這個本地服務器基於node.js構建,它是一個單獨的組件,在webpack中進行配置以前須要單獨安裝它做爲項目依賴:

npm install webpack-dev-server -D複製代碼

【5.2】devServer配置項

  • contentBase:該配置項指定了服務器資源的根目錄,若是不配置contentBase的話,那麼contentBase默認是當前執行的目錄,通常是項目的根目錄
  • port:指定了開啓服務器的端口號,默認爲8080
  • host:配置 DevServer的服務器監聽地址,默認爲 127.0.0.1
  • headers:該配置項能夠在HTTP響應中注入一些HTTP響應頭。例如:
headers: {
      'X-foo': '112233'
    }複製代碼
  • historyApiFallback:該配置項屬性是用來應對返回404頁面時定向跳轉到特定頁面的。通常是應用在單頁應用,好比在訪問路由時候,訪問不到該路由的時候,經過該配置項,設置屬性值爲true的時候,會自動跳轉到 index.html下。固然咱們也能夠手動經過 正則來匹配路由
// 跳到index.html頁面 
    historyApiFallback: true

    // 使用正則來匹配路由
    historyApiFallback: {
      rewrites: [
        { from: /^\/user/, to: '/user.html' },
        { from: /^\/home/, to: '/home.html' }
      ]
    }
複製代碼
  • hot:該配置項是指模塊替換換功能,DevServer 默認行爲是在發現源代碼被更新後經過自動刷新整個頁面來作到實時預覽的,可是開啓模塊熱替換功能後,它是經過在不刷新整個頁面的狀況下經過使用新模塊替換舊模塊來作到實時預覽的。
  • proxy : 有時候咱們使用webpack在本地啓動服務器的時候,因爲咱們使用的訪問的域名是 http://localhost:8081 這樣的,可是咱們服務端的接口是其餘的,能夠經過該配置來解決跨域的問題
// 假設服務端接口域名爲:http://news.baidu.com
proxy: {
  '/api': {
    target: 'http://news.baidu.com', // 目標接口的域名
    // secure: true, // https 的時候 使用該參數
    changeOrigin: true,  // 是否跨域
    pathRewrite: {
      '^/api' : ''  // 重寫路徑
    }
  }
}複製代碼
  • inline:設置爲true,當源文件改變時會自動刷新頁面
  • open:該屬性用於DevServer啓動且第一次構建完成時,自動使用咱們的系統默認瀏覽器去打開網頁。
  • compress:配置是否啓用 gzip 壓縮,boolean 類型,默認爲 false
  • overlay:該屬性是用來在編譯出錯的時候,在瀏覽器頁面上顯示錯誤。該屬性值默認爲false,須要的話,設置該參數爲true

【5.3】添加配置項到webpack.config.js

// webpack.config.js
const path = require('path');
module.exports = {
  entry: path.join(__dirname, "/src/index.js"), // 入口文件
  output: {
    path: path.join(__dirname, "/dist"), // 打包後的文件存放的地方 
    filename: "bundle.js" // 打包後輸出文件的文件名
  },
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    hot: true,
    port: '8080',
    inline: true,
    open: true,
    overlay: true,
    proxy: {
      '/api': {
        target: '', 
        changeOrigin: true,  
        pathRewrite: {
          '^/api': ''  
        }
      }
    }
  }
}複製代碼

【5.4】在package.json文件中添加啓動命令

"scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server --open"
  },複製代碼

咱們用dev來啓動本地服務器, webpack-dev-server就是啓動服務器的命令,- -opn是用於啓動完服務器後自動打開瀏覽器,這時候咱們自定義命令方式的便捷性就體現出來了,能夠多個命令集成在一塊兒運行,即咱們定義了一個dev命令名稱就能夠同時運行了webpack-dev-server和- -opn兩個命令

如今在終端輸入npm run dev 運行服務器

這樣咱們就能夠在http://localhost:8088/中查看頁面 (退出服務器,可以使用ctrl+c後,再按y確認便可退出服務器運行)

【5.5】Source Maps調試配置

做爲開發,代碼調試固然少不了,那麼問題來了,通過打包後的文件,你是不容易找到出錯的地方的,Source Map就是用來解決這個問題的。經過以下配置,咱們會在打包時生成對應於打包文件的.map文件,使得編譯後的代碼可讀性更高,更易於調試。

// webpack.config.js
const path = require('path');
module.exports = {
  entry: path.join(__dirname, "/src/index.js"), // 入口文件
  output: {
    path: path.join(__dirname, "/dist"), // 打包後的文件存放的地方 
    filename: "bundle.js" // 打包後輸出文件的文件名
  },
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    hot: true,
    port: '8080',
    inline: true,
    open: true,
    overlay: true,
  },
  devtool: 'source-map' // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
}複製代碼

配置好後,咱們再次運行npm run build進行打包,這時咱們會發如今dist文件夾中多出了一個bundle.js.map。若是咱們的代碼有bug,在瀏覽器的調試工具中會提示錯誤出現的位置,這就是devtool:'source-map' 配置項的做用。

6、Loaders

loaders是webpack最強大的功能之一,經過不一樣的loader,webpack有能力調用外部的腳本或工具,實現對不一樣格式的文件的處理,例如把scss轉爲css,將ES6六、ES7等語法轉化爲當前瀏覽器能識別的語法,將JSX轉化爲js等多項功能。Loaders須要單獨安裝而且須要在webpack.comfig.js中的modules配置項下進行配置,Loaders的配置包括如下幾方面:

  • test:一個用以匹配loaders所處理文件的拓展名的正則表達式(必須)
  • loader:loader的名稱(必須)
  • include/exclude: 手動添加必須處理的文件(文件夾)或屏蔽不須要處理的文件(文件夾)(可選)
  • options: 爲loaders提供額外的設置選項(可選)

【6.1】配置css-loader

若是咱們要加載一個css文件,須要安裝style-loader和css-loader

npm install style-loader css-loader -D複製代碼
// webpack.config.js
const path = require('path');
module.exports = {
  entry: path.join(__dirname, "/src/index.js"), // 入口文件
  output: {
    path: path.join(__dirname, "/dist"), // 打包後的文件存放的地方 
    filename: "bundle.js" // 打包後輸出文件的文件名
  },
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    hot: true,
    port: '8080',
    inline: true,
    open: true,
    overlay: true,
  },
  devtool: 'source-map', // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
  module: {
    rules: [
      {
        test: /\.css$/,   // 正則匹配以.css結尾的文件
        use: ['style-loader', 'css-loader']  // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
      }
    ]
  }
}複製代碼

咱們在src文件夾下新建index.css文件,設置body的樣式

/* index.css */
body {
    background: gray;
}複製代碼

在src文件夾下的index.js引入它

// index.js
import './index.css' // 導入css

const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());複製代碼

運行npm run dev啓動服務器,會發現頁面背景顏色變成了灰色

【6.2】配置sass

npm install sass-loader node-sass -D // 由於sass-loader依賴於node-sass,因此還要安裝node-sass複製代碼
// webpack.config.js
const path = require('path');
module.exports = {
  entry: path.join(__dirname, "/src/index.js"), // 入口文件
  output: {
    path: path.join(__dirname, "/dist"), // 打包後的文件存放的地方 
    filename: "bundle.js" // 打包後輸出文件的文件名
  },
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    hot: true,
    port: '8080',
    inline: true,
    open: true,
    overlay: true,
  },
  devtool: 'source-map', // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
  module: {
    rules: [
      {
        test: /\.css$/,   // 正則匹配以.css結尾的文件
        use: ['style-loader', 'css-loader']  // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
      },
      {
        test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結尾的文件
        use: ['style-loader', 'css-loader', 'sass-loader']  // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
      }
    ]
  }
}
複製代碼

7、Plugins(插件)

插件(Plugins)是用來拓展Webpack功能的,它們會在整個構建過程當中生效,執行相關的任務。
Loaders和Plugins經常被弄混,可是他們實際上是徹底不一樣的東西,能夠這麼來講,loaders是在打包構建過程當中用來處理源文件的(JSX,Scss,Less..),一次處理一個,插件並不直接操做單個文件,它直接對整個構建過程其做用。

【7.1】使用插件

如需使用某個插件,須要經過npm進行安裝,而後在webpack.config.js配置文件的plugins配置項中添加該插件的實例,下面咱們先來使用一個簡單的版權聲明插件。

// webpack.config.js
const path = require('path');
const webpack = require('webpack');  // 這個插件不須要安裝,是基於webpack的,須要引入webpack模塊
module.exports = {
  entry: path.join(__dirname, "/src/index.js"), // 入口文件
  output: {
    path: path.join(__dirname, "/dist"), // 打包後的文件存放的地方 
    filename: "bundle.js" // 打包後輸出文件的文件名
  },
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    hot: true,
    port: '8080',
    inline: true,
    open: true,
    overlay: true,
  },
  devtool: 'source-map', // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
  module: {
    rules: [
      {
        test: /\.css$/,   // 正則匹配以.css結尾的文件
        use: ['style-loader', 'css-loader']  // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
      },
      {
        test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結尾的文件
        use: ['style-loader', 'css-loader', 'sass-loader']  // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
      }
    ]
  },
  plugins: [
    new webpack.BannerPlugin('版權全部,翻版必究')  // new一個插件的實例 
  ]
}複製代碼

運行npm run build 打包後,咱們查看dist下面的handle.js文件顯示以下:

【7.2】自動生成html文件(HtmlWebpackPlugin)

到目前爲止咱們都是使用一開始建好的index.html文件,並且也是手動引入bundle.js,要是之後咱們引入不止一個js文件,並且更改js文件名的話,也得手動更改index.html中的js文件名,因此能不能自動生成index.html且自動引用打包後的js呢?HtmlWebpackPlugin插件就是用來解決這個問題的

咱們對項目結構進行一些更改:

  1. 把整個dist文件夾刪除
  2. 在src文件夾下新建一個index.html(名稱自定義)文件模板(固然這個是可選的,由於就算不設置模板,HtmlWebpackPlugin插件也會生成默認html文件,這裏咱們設置模塊會讓咱們的開發更加靈活),以下:
<!-- index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title></title> </head> <body> <div id='root'> </div> </body> </html>複製代碼

安裝HtmlWebpackPlugin插件

npm install html-webpack-plugin -D複製代碼

引入HtmlWebpackPlugin插件,並配置了引用了咱們設置的模板,以下:

// webpack.config.js
const path = require('path');
const webpack = require('webpack');  // 這個插件不須要安裝,是基於webpack的,須要引入webpack模塊
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入HtmlWebpackPlugin插件
module.exports = {
  entry: path.join(__dirname, "/src/index.js"), // 入口文件
  output: {
    path: path.join(__dirname, "/dist"), // 打包後的文件存放的地方 
    filename: "bundle.js" // 打包後輸出文件的文件名
  },
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    hot: true,
    port: '8080',
    inline: true,
    open: true,
    overlay: true,
  },
  devtool: 'source-map', // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
  module: {
    rules: [
      {
        test: /\.css$/,   // 正則匹配以.css結尾的文件
        use: ['style-loader', 'css-loader']  // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
      },
      {
        test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結尾的文件
        use: ['style-loader', 'css-loader', 'sass-loader']  // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
      }
    ]
  },
  plugins: [
    new webpack.BannerPlugin('版權全部,翻版必究'),  // new一個插件的實例 
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "/src/index.html")// new一個這個插件的實例,並傳入相關的參數
    })
  ]
}複製代碼

運行npm run build進行打包,dist文件夾自動生成,包含index.html、bundle.js、bundle.js.map三個文件

爲何會自動生成dist文件夾呢?由於咱們在output出口配置項中定義了出口文件所在的位置爲dist文件夾,且出口文件名爲bundle.js,因此HtmlWebpackPlugin會自動幫你在 dist/index.html 中引用名爲bundle.js文件,若是你在webpack.config.js文件中更改了出口文件名,dist/index.html 中也會自動更改該文件名,這樣之後修改起來是否是方便多了?

【7.3】清理dist文件夾(CleanWebpackPlugin)

webpack會生成文件,而後將這些文件放置在dist文件夾中,可是webpack沒法追蹤到哪些文件是實際在項目中用到的。一般,在每次構建前清理dist文件夾,是比較推薦的作法,所以只會生成用到的文件,這時候就用到CleanWebpackPlugin插件了。

npm install clean-webpack-plugin -D複製代碼
// webpack.config.js
const path = require('path');
const webpack = require('webpack');  // 這個插件不須要安裝,是基於webpack的,須要引入webpack模塊
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入HtmlWebpackPlugin插件
const CleanWebpackPlugin = require('clean-webpack-plugin'); // 引入CleanWebpackPlugin插件
module.exports = {
  entry: path.join(__dirname, "/src/index.js"), // 入口文件
  output: {
    path: path.join(__dirname, "/dist"), // 打包後的文件存放的地方 
    filename: "bundle.js" // 打包後輸出文件的文件名
  },
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    hot: true,
    port: '8080',
    inline: true,
    open: true,
    overlay: true,
  },
  devtool: 'source-map', // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
  module: {
    rules: [
      {
        test: /\.css$/,   // 正則匹配以.css結尾的文件
        use: ['style-loader', 'css-loader']  // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
      },
      {
        test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結尾的文件
        use: ['style-loader', 'css-loader', 'sass-loader']  // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
      }
    ]
  },
  plugins: [
    new webpack.BannerPlugin('版權全部,翻版必究'),  // new一個插件的實例 
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "/src/index.html")// new一個這個插件的實例,並傳入相關的參數
    }),
    new CleanWebpackPlugin(['dist']),  // 所要清理的文件夾名稱
  ]
}複製代碼

如今咱們每運行一次npm run build後就會發現,webpack會先將dist文件夾刪除,而後再生產新的dist文件夾。

【7.4】熱更新(HotModuleReplacementPlugin)

HotModuleReplacementPlugin是一個很實用的插件,能夠在咱們修改代碼後自動刷新預覽效果。

設置方法:

  1. devServer配置項中添加 hot:true 參數。
  2. 由於HotModuleReplacementPlugin是webpack模塊自帶的,因此引入webpack後,在plugins配置項中直接使用便可。
// webpack.config.js
const path = require('path');
const webpack = require('webpack');  // 這個插件不須要安裝,是基於webpack的,須要引入webpack模塊
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入HtmlWebpackPlugin插件
const CleanWebpackPlugin = require('clean-webpack-plugin'); // 引入CleanWebpackPlugin插件
module.exports = {
  entry: path.join(__dirname, "/src/index.js"), // 入口文件
  output: {
    path: path.join(__dirname, "/dist"), // 打包後的文件存放的地方 
    filename: "bundle.js" // 打包後輸出文件的文件名
  },
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    hot: true,
    port: '8080',
    inline: true,
    open: true,
    overlay: true,
  },
  devtool: 'source-map', // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
  module: {
    rules: [
      {
        test: /\.css$/,   // 正則匹配以.css結尾的文件
        use: ['style-loader', 'css-loader']  // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
      },
      {
        test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結尾的文件
        use: ['style-loader', 'css-loader', 'sass-loader']  // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
      }
    ]
  },
  plugins: [
    new webpack.BannerPlugin('版權全部,翻版必究'),  // new一個插件的實例 
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "/src/index.html")// new一個這個插件的實例,並傳入相關的參數
    }),
    new CleanWebpackPlugin(['dist']),  // 所要清理的文件夾名稱
    new webpack.HotModuleReplacementPlugin() // 熱更新插件 
  ]
}複製代碼

npm run dev 啓動項目後,咱們嘗試着修改hello.js的內容,會發現瀏覽器預覽效果會自動刷新

8、項目優化及拓展

【8.1】代碼分離

咱們的webpack.config.js配置文件,其實也沒配置多少東西就這麼多了,要是之後增長了更多配置,豈不是看得眼花繚亂,因此最好的方法就是把它拆分,方便管理:

一、 咱們在根目錄下新建三個文件,分別爲webpack.common.js、webpack.dev.js、webpack.prod.js分別表明公共配置文件、開發環境配置文件、生產環境(指項目上線時的環境)配置文件。

二、安裝一個合併模塊插件:

npm install webpack-merge -D複製代碼

三、將webpack.config.js的代碼拆分到上述新建的三個文件中,而後把將webpack.config.js文件刪除,具體以下:

// webpack.common.js
const path = require('path');
const webpack = require('webpack');  // 這個插件不須要安裝,是基於webpack的,須要引入webpack模塊
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入HtmlWebpackPlugin插件
module.exports = {
  entry: path.join(__dirname, "/src/index.js"), // 入口文件
  output: {
    path: path.join(__dirname, "/dist"), // 打包後的文件存放的地方 
    filename: "bundle.js" // 打包後輸出文件的文件名
  },
  module: {
    rules: [
      {
        test: /\.css$/,   // 正則匹配以.css結尾的文件
        use: ['style-loader', 'css-loader']  // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
      },
      {
        test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結尾的文件
        use: ['style-loader', 'css-loader', 'sass-loader']  // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
      }
    ]
  },
  plugins: [
    new webpack.BannerPlugin('版權全部,翻版必究'),  // new一個插件的實例 
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "/src/index.html")// new一個這個插件的實例,並傳入相關的參數
    }),
    new webpack.HotModuleReplacementPlugin() // 熱更新插件 
  ]
}
複製代碼
// webpack.dev.js
const path = require('path');
const merge = require('webpack-merge');  // 引入webpack-merge功能模塊
const common = require('./webpack.common.js'); // 引入webpack.common.js

module.exports = merge(common, {   // 將webpack.common.js合併到當前文件
    devServer: {
        contentBase: path.join(__dirname, "dist"),
        hot: true,
        port: '8080',
        inline: true,
        open: true,
        overlay: true,
    },
})
複製代碼
// webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); // 引入CleanWebpackPlugin插件

module.exports = merge(common, { // 將webpack.common.js合併到當前文件
    devtool: 'source-map',  // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
    plugins: [
        new CleanWebpackPlugin(),  
    ]
})複製代碼

四、設置package.json的scripts命令

"scripts": {
    "build": "webpack --config webpack.prod.js",
    "dev": "webpack-dev-server --open --config webpack.dev.js"
  },複製代碼

咱們把build命令改成了webpack --config webpack.prod.js,意思是把打包配置指向webpack.prod.js配置文件,而以前咱們只須要使用一個webpack 命令爲何就能夠運行了?由於webpack 命令是默認指向webpack.config.js這個文件名稱了,如今咱們把文件名稱改了,因此就須要自定義指向新的文件,dev命令中的指令也同理。

而後咱們運行npm run build 和npm run dev,效果應該和咱們分離代碼前是同樣的。

【8.2】多入口 多出口

到目前爲止咱們都是一個入口文件和一個出口文件,要是我不止一個入口文件呢?下面咱們來試試:

在webpack.common.js中的entery入口有三種寫法,分別爲字符串、數組和對象,平時咱們用得比較多的是對象,因此咱們把它改成對象的寫法,首先咱們在src文件夾下新建index2.js文件,名稱任意。由於有多個入口,因此確定得多個出口來進行一一對應了,因此entry和output配置以下:

entry: {
        index: path.join(__dirname, "/src/index.js"),
        index2: path.join(__dirname, "/src/index2.js")
    },
    output: {
        path: path.join(__dirname, "/dist"), // 打包後的文件存放的地方 
        filename: "[name].js" // 打包後輸出文件的文件名
    },複製代碼
// index2.js
function page2() {
    let element = document.createElement('div');
    element.innerHTML = '我是第二個入口文件';
    return element;
}

document.getElementById('root').appendChild(page2());複製代碼

而後咱們運行npm run build打包後發現dist文件夾下會多出index2.js文件,同時index.html也會自動將index2.js引入,而後咱們運行npm run dev顯示以下:

【8.3】分離css

webpack的理念是把css、js全都打包到一個文件裏,但要是咱們想把css分離出來該怎麼作呢?

npm install extract-text-webpack-plugin@next -D  // 加上@next是爲了安裝最新的,不然會出錯複製代碼

安裝完以上插件後在webpack.common.js文件中引入並使用該插件:

// webpack.common.js
const path = require('path');
const webpack = require('webpack');  // 這個插件不須要安裝,是基於webpack的,須要引入webpack模塊
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入HtmlWebpackPlugin插件
const ExtractTextPlugin = require('extract-text-webpack-plugin') //引入分離插件
module.exports = {
    entry: {
        index: path.join(__dirname, "/src/index.js"),
        index2: path.join(__dirname, "/src/index2.js")
    },
    output: {
        path: path.join(__dirname, "/dist"), // 打包後的文件存放的地方 
        filename: "[name].js" // 打包後輸出文件的文件名
    },
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結尾的文件
                use: ExtractTextPlugin.extract({  // 這裏咱們須要調用分離插件內的extract方法
                    fallback: 'style-loader',  // 至關於回滾,經css-loader處理過的css最終再通過style-loader處理
                    use: ['css-loader']
                })
            },
            {
                test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版權全部,翻版必究'),  // new一個插件的實例 
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.html")// new一個這個插件的實例,並傳入相關的參數
        }),
        new webpack.HotModuleReplacementPlugin(), // 熱更新插件
        new ExtractTextPlugin('css/index.css') // 將css分離到/dist文件夾下的css文件夾中的index.css 
    ]
}
複製代碼

運行npm run build後會發現dist文件夾下多一個css文件

【8.4】消除冗餘的css

有時候咱們css寫得多了,可能會不自覺的寫重複了一些樣式,這就形成了多餘的代碼,上線前又忘了檢查,對於這方面,咱們應該儘可能去優化它,webpack就有這個功能。

npm install purifycss-webpack purify-css glob -D複製代碼

安裝完後在webpack.prod.js文件中進行配置,引入purifycss-webpack和glob插件並使用它們

// webpack.prod.js
const path = require('path');
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); // 引入CleanWebpackPlugin插件
const PurifyCssWebpack = require('purifycss-webpack'); // 引入PurifyCssWebpack插件
const glob = require('glob');  // 引入glob模塊,用於掃描所有html文件中所引用的css

module.exports = merge(common, { // 將webpack.common.js合併到當前文件
    devtool: 'source-map',  // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
    plugins: [
        new CleanWebpackPlugin(),
        new PurifyCssWebpack({
            paths: glob.sync(path.join(__dirname, 'src/*.html')) // 同步掃描全部html文件中所引用的css
        })
    ]
})複製代碼

咱們在index.css文件中增長一些多餘的代碼試試:

/* index.css */
body {
    background: gray;
}

/* 冗餘css */
.a {
    color: black;
    font-size: 14px;
    background: red;
}

/* 冗餘css */
.b {
    height: 50px;
    line-height: 50px;
    border: none;
}複製代碼

而後咱們運行npm run build後發現打包後的index.css中是沒有多餘的.a.b代碼的:

【8.5】處理圖片

若是要使用圖片,咱們得安裝兩個loader

// 雖然咱們只需使用url-loader,但url-loader是依賴於file-loader的,因此也要安裝
npm install url-loader file-loader -D 複製代碼

而後在webpack.common.js中配置url-loader

// webpack.common.js
const path = require('path');
const webpack = require('webpack');  // 這個插件不須要安裝,是基於webpack的,須要引入webpack模塊
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入HtmlWebpackPlugin插件
const ExtractTextPlugin = require('extract-text-webpack-plugin') //引入分離插件
module.exports = {
    entry: {
        index: path.join(__dirname, "/src/index.js"),
        index2: path.join(__dirname, "/src/index2.js")
    },
    output: {
        path: path.join(__dirname, "/dist"), // 打包後的文件存放的地方 
        filename: "[name].js" // 打包後輸出文件的文件名
    },
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結尾的文件
                use: ExtractTextPlugin.extract({  // 這裏咱們須要調用分離插件內的extract方法
                    fallback: 'style-loader',  // 至關於回滾,經css-loader處理過的css最終再通過style-loader處理
                    use: ['css-loader']
                })
            },
            {
                test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
            },
            {
                test: /\.(png|jpg|svg|gif)$/,  // 正則匹配圖片格式名
                use: [
                    { loader: 'url-loader'  // 使用url-loader }
                ]
            },
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版權全部,翻版必究'),  // new一個插件的實例 
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.html")// new一個這個插件的實例,並傳入相關的參數
        }),
        new webpack.HotModuleReplacementPlugin(), // 熱更新插件
        new ExtractTextPlugin('css/index.css') // 將css分離到/dist文件夾下的css文件夾中的index.css 
    ]
}
複製代碼

咱們把index.css的背景改成圖片

/* index.css */
body {
    background: url('./images/bg.jpg') no-repeat;
    background-size: 200px 250px;
    color: #fff;
}複製代碼

運行npm run dev 後顯示以下:

咱們會發現背景圖片變成了base64,由於webpack會自動優化圖片,減小發送請求,可是若是我想把它變成路徑的該怎麼作?

咱們能夠把webpack.common.js的loader配置更改一下,增長options選項:

module: {
    rules: [
      {
        test: /\.css$/,   // 正則匹配以.css結尾的文件
        use: ExtractTextPlugin.extract({  // 這裏咱們須要調用分離插件內的extract方法
          fallback: 'style-loader',  // 至關於回滾,經css-loader處理過的css最終再通過style-loader處理
          use: ['css-loader'],
          publicPath: '../'  // 給背景圖片設置一個公共路徑
        })
      },
      {
        test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結尾的文件
        use: ['style-loader', 'css-loader', 'sass-loader'],  // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
      },
      {
        test: /\.(png|jpg|svg|gif)$/,  // 正則匹配圖片格式名
        use: [
          {
            loader: 'url-loader',  // 使用url-loader
            options: {
              limit: 1000,  // 限制只有小於1kb的圖片才轉爲base64,例子圖片爲1.47kb,因此不會被轉化
              outputPath: 'images',  // 設置打包後圖片存放的文件夾名稱
            },
          }
        ]
      },
    ]
  },複製代碼

【8.6】壓縮代碼

在webpack4.x版本中當你打包時會自動把js壓縮了,並且npm run dev運行服務器時,當你修改代碼時,熱更新很慢,這是由於你修改後webpack又自動爲你打包,這就致使了在開發環境中效率很慢,因此咱們須要把開發環境和生產環境區分開來,這時就體現出咱們代碼分離的便捷性了,webpack.dev.js表明開發環境的配置,webpack.prod.js表明生產環境的配置,這時咱們只要在package.json文件中配置對應環境的命令便可:

"scripts": {
    "build": "webpack --config webpack.prod.js --mode production",
    "dev": "webpack-dev-server --open --config webpack.dev.js --mode development"
  },複製代碼

--mode production表示打包時是生產環境,會本身將js進行壓縮,而--mode development表示當前是開發環境,不須要進行壓縮。這同時也解決了以前一直遺留的警告問題

運行npm run dev 後發現速度倍兒棒, 嘻嘻!

相關文章
相關標籤/搜索