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

一、什么是webpack

webpack是一個打包工具,他的宗旨是一切靜態(tài)資源皆可打包犁柜。有人就會問為什么要webpack?webpack是現(xiàn)代前端技術(shù)的基石绢淀,常規(guī)的開發(fā)方式萤悴,比如jquery,html,css靜態(tài)網(wǎng)頁開發(fā)已經(jīng)落后了。現(xiàn)在是MVVM的時代皆的,數(shù)據(jù)驅(qū)動界面覆履。webpack它做的事情是,分析你的項目結(jié)構(gòu)费薄,找到JavaScript模塊以及其它的一些瀏覽器不能直接運行的拓展語言(Scss硝全,TypeScript等),并將其打包為合適的格式以供瀏覽器使用楞抡。

二伟众、webpack核心概念

1、Entry(入口):指示 webpack 應該使用哪個模塊召廷,來作為構(gòu)建其內(nèi)部依賴圖的開始凳厢。進入入口起點后,webpack 會找出有哪些模塊和庫是入口起點(直接和間接)依賴的竞慢。

2先紫、Output(出口):告訴 webpack 在哪里輸出它所創(chuàng)建的結(jié)果文件,以及如何命名這些文件筹煮,默認值為./dist泡孩。

3、Loader(模塊轉(zhuǎn)換器):將所有類型的文件轉(zhuǎn)換為 webpack 能夠處理的有效模塊寺谤,然后你就可以利用 webpack 的打包能力仑鸥,對它們進行處理。

4变屁、Plugins(插件):在 Webpack 構(gòu)建流程中的特定時機注入擴展邏輯來改變構(gòu)建結(jié)果或做你想要的事情眼俊。

5、Module(模塊):開發(fā)者將程序分解成離散功能塊粟关,并稱之為模塊疮胖,在webpack里一個模塊對應著一個文件,webpack會從配置的 Entry 開始遞歸找出所有依賴的模塊。

三澎灸、webpack執(zhí)行流程

webpack啟動后會在entry里配置的module開始遞歸解析entry所依賴的所有module院塞,每找到一個module, 就會根據(jù)配置的loader去找相應的轉(zhuǎn)換規(guī)則,對module進行轉(zhuǎn)換后在解析當前module所依賴的module性昭,這些模塊會以entry為分組拦止,一個entry和所有相依賴的module也就是一個chunk,最后webpack會把所有chunk轉(zhuǎn)換成文件輸出糜颠,在整個流程中webpack會在恰當?shù)臅r機執(zhí)行plugin的邏輯

四汹族、webpack簡單打包案例

【4.1】準備工作

新建一個空文件夾用于創(chuàng)建項目, 如下我在D盤創(chuàng)建了一個名為webpack_demo的文件夾其兴,使用終端進入文件夾顶瞒, 使用npm init 命令初始化一個package.json文件

輸入這個命令后,終端會問你一系列諸如項目名稱元旬,項目描述榴徐,作者等信息,不過如果你不打算發(fā)布這個模塊匀归,直接一路回車就好箕速。(也可以使用npm init -y這個命令來一次生成package.json文件,這樣終端不會詢問你問題)朋譬。

【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文件钦幔。此時項目結(jié)構(gòu)如下:

在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引入

結(jié)果如下:

可以看出webpack同時編譯了index.js和hello.js,因為index.js文件引入了hello.js模塊卷玉,現(xiàn)在打開index.html看看結(jié)果

我們已經(jīng)成功使用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中的一個全局變量斤蔓,它指向當前執(zhí)行腳本所在的目錄植酥,即D:\webpack_demo

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


?

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

Node項目一般都有一個package.json文件惧互,該文件用于描述當前項目,其中有一個scripts屬性喇伯,該屬性可以自定義腳本命令喊儡,例如我們運行的打包命令,那么可以在scripts里添加自定義腳本為:

之后就可以使用npm run build來運行該腳本命令稻据,這樣有什么好處呢艾猜?如果命令行很短,好處當然不明顯了捻悯,但是如何命令行很長呢匆赃?那么我們可以在這里添加每次都需要執(zhí)行的命令,配置了scripts后今缚, npm run key值相當于在終端運行了value值

五算柳、構(gòu)建本地服務

上面案例我們是通過打開本地HTML文件來查看頁面的,vue姓言,react框架時都是運行在本地服務器上的瞬项,那我們能不能也改成那樣呢?接下來學習如何構(gòu)建本地服務

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

Webpack提供了一個可選的本地開發(fā)服務器何荚,這個本地服務器基于node.js構(gòu)建囱淋,它是一個單獨的組件,在webpack中進行配置之前需要單獨安裝它作為項目依賴:

npm install webpack-dev-server -D

【5.2】devServer配置項

  • contentBase:該配置項指定了服務器資源的根目錄餐塘,如果不配置contentBase的話妥衣,那么contentBase默認是當前執(zhí)行的目錄,一般是項目的根目錄
  • post:指定了開啟服務器的端口號,默認為8080
  • host:配置 DevServer的服務器監(jiān)聽地址戒傻,默認為 127.0.0.1
  • headers:該配置項可以在HTTP響應中注入一些HTTP響應頭税手。例如:
    headers: {
      'X-foo': '112233'
    }
  • historyApiFallback:該配置項屬性是用來應對返回404頁面時定向跳轉(zhuǎn)到特定頁面的。一般是應用在單頁應用需纳,比如在訪問路由時候冈止,訪問不到該路由的時候,通過該配置項,設置屬性值為true的時候,會自動跳轉(zhuǎn)到 index.html下乔夯。當然我們也可以手動通過 正則來匹配路由
    // 跳到index.html頁面 
    historyApiFallback: true

    // 使用正則來匹配路由
    historyApiFallback: {
      rewrites: [
        { from: /^\/user/, to: '/user.html' },
        { from: /^\/home/, to: '/home.html' }
      ]
    }

  • hot:該配置項是指模塊替換換功能丢氢,DevServer 默認行為是在發(fā)現(xiàn)源代碼被更新后通過自動刷新整個頁面來做到實時預覽的乡洼,但是開啟模塊熱替換功能后喂急,它是通過在不刷新整個頁面的情況下通過使用新模塊替換舊模塊來做到實時預覽的菩咨。
  • **proxy : **有時候我們使用webpack在本地啟動服務器的時候丢烘,由于我們使用的訪問的域名是 http://localhost:8081 這樣的俱箱,但是我們服務端的接口是其他的国瓮,可以通過該配置來解決跨域的問題
// 假設服務端接口域名為:http://news.baidu.com
proxy: {
  '/api': {
    target: 'http://news.baidu.com', // 目標接口的域名
    // secure: true,  // https 的時候 使用該參數(shù)
    changeOrigin: true,  // 是否跨域
    pathRewrite: {
      '^/api' : ''  // 重寫路徑
    }
  }
}
  • inline:設置為true,當源文件改變時會自動刷新頁面
  • open:該屬性用于DevServer啟動且第一次構(gòu)建完成時狞谱,自動使用我們的系統(tǒng)默認瀏覽器去打開網(wǎng)頁乃摹。
  • compress:配置是否啟用 gzip 壓縮,boolean 類型跟衅,默認為 false
  • overlay:該屬性是用來在編譯出錯的時候孵睬,在瀏覽器頁面上顯示錯誤。該屬性值默認為false伶跷,需要的話掰读,設置該參數(shù)為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是用于啟動完服務器后自動打開瀏覽器蹈集,這時候我們自定義命令方式的便捷性就體現(xiàn)出來了,可以多個命令集成在一起運行雇初,即我們定義了一個dev命令名稱就可以同時運行了webpack-dev-server和- -opn兩個命令

現(xiàn)在在終端輸入npm run dev 運行服務器


?

這樣我們就可以在http://localhost:8088/中查看頁面 (退出服務器拢肆,可使用ctrl+c后,再按y確認即可退出服務器運行)

【5.5】Source Maps調(diào)試配置

作為開發(fā)靖诗,代碼調(diào)試當然少不了郭怪,那么問題來了,經(jīng)過打包后的文件呻畸,你是不容易找到出錯的地方的,Source Map就是用來解決這個問題的悼院。通過如下配置伤为,我們會在打包時生成對應于打包文件的.map文件,使得編譯后的代碼可讀性更高据途,更易于調(diào)試绞愚。

// 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' // 會生成對于調(diào)試的完整的.map文件,但同時也會減慢打包速度
}

配置好后颖医,我們再次運行npm run build進行打包位衩,這時我們會發(fā)現(xiàn)在dist文件夾中多出了一個bundle.js.map。如果我們的代碼有bug熔萧,在瀏覽器的調(diào)試工具中會提示錯誤出現(xiàn)的位置糖驴,這就是devtool:'source-map' 配置項的作用僚祷。

六、Loaders

loaders是webpack最強大的功能之一贮缕,通過不同的loader辙谜,webpack有能力調(diào)用外部的腳本或工具,實現(xiàn)對不同格式的文件的處理感昼,例如把scss轉(zhuǎn)為css装哆,將ES66、ES7等語法轉(zhuǎn)化為當前瀏覽器能識別的語法定嗓,將JSX轉(zhuǎn)化為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', // 會生成對于調(diào)試的完整的.map文件凌简,但同時也會減慢打包速度
  module: {
    rules: [
      {
        test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
        use: ['style-loader', 'css-loader']  // 需要用的loader,一定是這個順序层玲,因為調(diào)用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啟動服務器,會發(fā)現(xiàn)頁面背景顏色變成了灰色

【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', // 會生成對于調(diào)試的完整的.map文件畔派,但同時也會減慢打包速度
  module: {
    rules: [
      {
        test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
        use: ['style-loader', 'css-loader']  // 需要用的loader,一定是這個順序润绵,因為調(diào)用loader是從右往左編譯的
      },
      {
        test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結(jié)尾的文件
        use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader线椰,一定是這個順序,因為調(diào)用loader是從右往左編譯的
      }
    ]
  }
}

七尘盼、Plugins(插件)

插件(Plugins)是用來拓展Webpack功能的憨愉,它們會在整個構(gòu)建過程中生效,執(zhí)行相關(guān)的任務卿捎。
Loaders和Plugins常常被弄混配紫,但是他們其實是完全不同的東西,可以這么來說午阵,loaders是在打包構(gòu)建過程中用來處理源文件的(JSX躺孝,Scss,Less..)底桂,一次處理一個植袍,插件并不直接操作單個文件,它直接對整個構(gòu)建過程其作用籽懦。

【7.1】使用插件

如需使用某個插件于个,需要通過npm進行安裝,然后在webpack.config.js配置文件的plugins配置項中添加該插件的實例暮顺,下面我們先來使用一個簡單的版權(quán)聲明插件厅篓。

// 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', // 會生成對于調(diào)試的完整的.map文件贷笛,但同時也會減慢打包速度
  module: {
    rules: [
      {
        test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
        use: ['style-loader', 'css-loader']  // 需要用的loader应又,一定是這個順序,因為調(diào)用loader是從右往左編譯的
      },
      {
        test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結(jié)尾的文件
        use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader乏苦,一定是這個順序株扛,因為調(diào)用loader是從右往左編譯的
      }
    ]
  },
  plugins: [
    new webpack.BannerPlugin('版權(quán)所有,翻版必究')  // new一個插件的實例 
  ]
}

運行npm run build 打包后汇荐,我們查看dist下面的handle.js文件顯示如下:

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

到目前為止我們都是使用一開始建好的index.html文件洞就,而且也是手動引入bundle.js,要是以后我們引入不止一個js文件掀淘,而且更改js文件名的話旬蟋,也得手動更改index.html中的js文件名,所以能不能自動生成index.html且自動引用打包后的js呢革娄?HtmlWebpackPlugin插件就是用來解決這個問題的

我們對項目結(jié)構(gòu)進行一些更改:

  1. 把整個dist文件夾刪除
  2. 在src文件夾下新建一個index.html(名稱自定義)文件模板(當然這個是可選的倾贰,因為就算不設置模板,HtmlWebpackPlugin插件也會生成默認html文件拦惋,這里我們設置模塊會讓我們的開發(fā)更加靈活)匆浙,如下:
<!-- 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', // 會生成對于調(diào)試的完整的.map文件言秸,但同時也會減慢打包速度
  module: {
    rules: [
      {
        test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
        use: ['style-loader', 'css-loader']  // 需要用的loader软能,一定是這個順序,因為調(diào)用loader是從右往左編譯的
      },
      {
        test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結(jié)尾的文件
        use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader举畸,一定是這個順序查排,因為調(diào)用loader是從右往左編譯的
      }
    ]
  },
  plugins: [
    new webpack.BannerPlugin('版權(quán)所有,翻版必究'),  // new一個插件的實例 
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "/src/index.html")// new一個這個插件的實例抄沮,并傳入相關(guān)的參數(shù)
    })
  ]
}

運行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無法追蹤到哪些文件是實際在項目中用到的。通常谦炬,在每次構(gòu)建前清理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', // 會生成對于調(diào)試的完整的.map文件看蚜,但同時也會減慢打包速度
  module: {
    rules: [
      {
        test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
        use: ['style-loader', 'css-loader']  // 需要用的loader,一定是這個順序赔桌,因為調(diào)用loader是從右往左編譯的
      },
      {
        test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結(jié)尾的文件
        use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader供炎,一定是這個順序,因為調(diào)用loader是從右往左編譯的
      }
    ]
  },
  plugins: [
    new webpack.BannerPlugin('版權(quán)所有疾党,翻版必究'),  // new一個插件的實例 
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "/src/index.html")// new一個這個插件的實例音诫,并傳入相關(guān)的參數(shù)
    }),
    new CleanWebpackPlugin(),  // 默認刪除output中path對應文件
  ]
}

現(xiàn)在我們每運行一次npm run build后就會發(fā)現(xiàn),webpack會先將dist文件夾刪除仿贬,然后再生產(chǎn)新的dist文件夾纽竣。

【7.4】熱更新(HotModuleReplacementPlugin)

HotModuleReplacementPlugin是一個很實用的插件,可以在我們修改代碼后自動刷新預覽效果茧泪。

設置方法:

  1. devServer配置項中添加 hot:true 參數(shù)蜓氨。
  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', // 會生成對于調(diào)試的完整的.map文件港令,但同時也會減慢打包速度
  module: {
    rules: [
      {
        test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
        use: ['style-loader', 'css-loader']  // 需要用的loader,一定是這個順序锈颗,因為調(diào)用loader是從右往左編譯的
      },
      {
        test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結(jié)尾的文件
        use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader顷霹,一定是這個順序,因為調(diào)用loader是從右往左編譯的
      }
    ]
  },
  plugins: [
    new webpack.BannerPlugin('版權(quán)所有击吱,翻版必究'),  // new一個插件的實例 
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "/src/index.html")// new一個這個插件的實例淋淀,并傳入相關(guān)的參數(shù)
    }),
    new CleanWebpackPlugin(['dist']),  // 所要清理的文件夾名稱
    new webpack.HotModuleReplacementPlugin() // 熱更新插件 
  ]
}

npm run dev 啟動項目后,我們嘗試著修改hello.js的內(nèi)容覆醇,會發(fā)現(xiàn)瀏覽器預覽效果會自動刷新

八朵纷、項目優(yōu)化及拓展

【8.1】代碼分離

我們的webpack.config.js配置文件炭臭,其實也沒配置多少東西就這么多了,要是以后增加了更多配置袍辞,豈不是看得眼花繚亂鞋仍,所以最好的方法就是把它拆分,方便管理:

1搅吁、 我們在根目錄下新建三個文件威创,分別為webpack.common.js、webpack.dev.js谎懦、webpack.prod.js分別代表公共配置文件那婉、開發(fā)環(huán)境配置文件、生產(chǎn)環(huán)境(指項目上線時的環(huán)境)配置文件党瓮。

2详炬、安裝一個合并模塊插件:

npm install webpack-merge -D

3、將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結(jié)尾的文件
        use: ['style-loader', 'css-loader']  // 需要用的loader隐岛,一定是這個順序,因為調(diào)用loader是從右往左編譯的
      },
      {
        test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結(jié)尾的文件
        use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader瓷翻,一定是這個順序聚凹,因為調(diào)用loader是從右往左編譯的
      }
    ]
  },
  plugins: [
    new webpack.BannerPlugin('版權(quán)所有,翻版必究'),  // new一個插件的實例 
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "/src/index.html")// new一個這個插件的實例齐帚,并傳入相關(guān)的參數(shù)
    }),
    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',  // 會生成對于調(diào)試的完整的.map文件妒牙,但同時也會減慢打包速度
    plugins: [
        new CleanWebpackPlugin(),  
    ]
})

4、設置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這個文件名稱了剪菱,現(xiàn)在我們把文件名稱改了摩瞎,所以就需要自定義指向新的文件,dev命令中的指令也同理孝常。

然后我們運行npm run build 和npm run dev旗们,效果應該和我們分離代碼前是一樣的。

【8.2】多入口 多出口

到目前為止我們都是一個入口文件和一個出口文件构灸,要是我不止一個入口文件呢上渴?下面我們來試試:

在webpack.common.js中的entery入口有三種寫法,分別為字符串、數(shù)組和對象驰贷,平時我們用得比較多的是對象,所以我們把它改為對象的寫法洛巢,首先我們在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打包后發(fā)現(xiàn)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結(jié)尾的文件
                use: ExtractTextPlugin.extract({  // 這里我們需要調(diào)用分離插件內(nèi)的extract方法
                    fallback: 'style-loader',  // 相當于回滾怠蹂,css-loader處理過的css最終再經(jīng)過style-loader處理
                    use: ['css-loader']
                })
            },
            {
                test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結(jié)尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader,一定是這個順序少态,因為調(diào)用loader是從右往左編譯的
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版權(quán)所有城侧,翻版必究'),  // new一個插件的實例 
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.html")// new一個這個插件的實例,并傳入相關(guān)的參數(shù)
        }),
        new webpack.HotModuleReplacementPlugin(), // 熱更新插件
        new ExtractTextPlugin('css/index.css') // 將css分離到/dist文件夾下的css文件夾中的index.css 
    ]
}

運行npm run build后會發(fā)現(xiàn)dist文件夾下多一個css文件

【8.4】消除冗余的css

有時候我們css寫得多了彼妻,可能會不自覺的寫重復了一些樣式嫌佑,這就造成了多余的代碼,上線前又忘了檢查侨歉,對于這方面屋摇,我們應該盡量去優(yōu)化它,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',  // 會生成對于調(diào)試的完整的.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后發(fā)現(xiàn)打包后的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結(jié)尾的文件
                use: ExtractTextPlugin.extract({  // 這里我們需要調(diào)用分離插件內(nèi)的extract方法
                    fallback: 'style-loader',  // 相當于回滾,css-loader處理過的css最終再經(jīng)過style-loader處理
                    use: ['css-loader']
                })
            },
            {
                test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結(jié)尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader重斑,一定是這個順序兵睛,因為調(diào)用loader是從右往左編譯的
            },
            {
                test: /\.(png|jpg|svg|gif)$/,  // 正則匹配圖片格式名
                use: [
                    { loader: 'url-loader'  // 使用url-loader }
                ]
            },
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版權(quán)所有,翻版必究'),  // new一個插件的實例 
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.html")// new一個這個插件的實例,并傳入相關(guān)的參數(shù)
        }),
        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 后顯示如下:

我們會發(fā)現(xiàn)背景圖片變成了base64祖很,因為webpack會自動優(yōu)化圖片笛丙,減少發(fā)送請求,但是如果我想把它變成路徑的該怎么做假颇?

我們可以把webpack.common.js的loader配置更改一下胚鸯,增加options選項:

module: {
    rules: [
      {
        test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
        use: ExtractTextPlugin.extract({  // 這里我們需要調(diào)用分離插件內(nèi)的extract方法
          fallback: 'style-loader',  // 相當于回滾,css-loader處理過的css最終再經(jīng)過style-loader處理
          use: ['css-loader'],
          publicPath: '../'  // 給背景圖片設置一個公共路徑
        })
      },
      {
        test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結(jié)尾的文件
        use: ['style-loader', 'css-loader', 'sass-loader'],  // 需要用的loader笨鸡,一定是這個順序姜钳,因為調(diào)用loader是從右往左編譯的
      },
      {
        test: /\.(png|jpg|svg|gif)$/,  // 正則匹配圖片格式名
        use: [
          {
            loader: 'url-loader',  // 使用url-loader
            options: {
              limit: 1000,  // 限制只有小于1kb的圖片才轉(zhuǎn)為base64,例子圖片為1.47kb,所以不會被轉(zhuǎn)化
              outputPath: 'images',  // 設置打包后圖片存放的文件夾名稱
            },
          }
        ]
      },
    ]
  },

【8.6】壓縮代碼

在webpack4.x版本中當你打包時會自動把js壓縮了形耗,而且npm run dev運行服務器時哥桥,當你修改代碼時,熱更新很慢激涤,這是因為你修改后webpack又自動為你打包拟糕,這就導致了在開發(fā)環(huán)境中效率很慢,所以我們需要把開發(fā)環(huán)境和生產(chǎn)環(huán)境區(qū)分開來倦踢,這時就體現(xiàn)出我們代碼分離的便捷性了已卸,webpack.dev.js代表開發(fā)環(huán)境的配置,webpack.prod.js代表生產(chǎn)環(huán)境的配置硼一,這時我們只要在package.json文件中配置對應環(huán)境的命令即可:

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

--mode production表示打包時是生產(chǎn)環(huán)境累澡,會自己將js進行壓縮,而--mode development表示當前是開發(fā)環(huán)境般贼,不需要進行壓縮愧哟。這同時也解決了之前一直遺留的警告問題

運行npm run dev 后發(fā)現(xiàn)速度倍兒棒, 嘻嘻哼蛆!

文章每周持續(xù)更新蕊梧,可以微信搜索「 前端大集錦 」第一時間閱讀,回復【視頻】【書籍】領(lǐng)取200G視頻資料和30本PDF書籍資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末腮介,一起剝皮案震驚了整個濱河市肥矢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叠洗,老刑警劉巖甘改,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異灭抑,居然都是意外死亡十艾,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門腾节,熙熙樓的掌柜王于貴愁眉苦臉地迎上來忘嫉,“玉大人荤牍,你說我怎么就攤上這事∏烀幔” “怎么了康吵?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長访递。 經(jīng)常有香客問我晦嵌,道長,這世上最難降的妖魔是什么力九? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮邑闺,結(jié)果婚禮上跌前,老公的妹妹穿的比我還像新娘。我一直安慰自己陡舅,他們只是感情好抵乓,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著靶衍,像睡著了一般灾炭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上颅眶,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天蜈出,我揣著相機與錄音,去河邊找鬼涛酗。 笑死铡原,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的商叹。 我是一名探鬼主播燕刻,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼剖笙!你這毒婦竟也來了卵洗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤弥咪,失蹤者是張志新(化名)和其女友劉穎过蹂,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體聚至,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡晚岭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骚揍。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖抽活,靈堂內(nèi)的尸體忽然破棺而出下硕,到底是詐尸還是另有隱情梭姓,我是刑警寧澤誉尖,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布释牺,位于F島的核電站回挽,受9級特大地震影響千劈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望棒口。 院中可真熱鬧无牵,春花似錦克懊、人聲如沸七蜘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至伞鲫,卻和暖如春签舞,著一層夾襖步出監(jiān)牢的瞬間秕脓,已是汗流浹背儒搭。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工吠架, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留搂鲫,地道東北人傍药。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓魂仍,卻偏偏與公主長得像拐辽,于是被迫代替她去往敵國和親俱诸。 傳聞我的和親對象是個殘疾皇子赶诊,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359