webpack入門(mén)——了解及使用

什么是webpack?我的理解是文件打包及資源處理,當(dāng)然功能肯定不局限于此。
學(xué)習(xí)前提:了解npm,了解node.js基礎(chǔ)知識(shí),了解前端工程化、node環(huán)境。

文件打包:

為什么我們要文件打包?通常一個(gè)html中可能引用許多庫(kù),比如什么jQuery,Boostrap,Vue,自己寫(xiě)的一系列東西等等。那么在加載這些文件的時(shí)候,每次遇見(jiàn)一個(gè)外鏈都要去發(fā)起新的http請(qǐng)求,這很低效,速度也很慢。這樣就需要一個(gè)東西來(lái)整合資源,并且把資源分類(lèi),打包減少http請(qǐng)求數(shù),以供線(xiàn)上html正確使用。

資源處理:

現(xiàn)在流行ES6,但是ES6并沒(méi)有完全被廣大瀏覽器支持,但是我們又想寫(xiě)ES6,并且希望我們寫(xiě)的結(jié)果能夠?qū)崟r(shí)體現(xiàn)在瀏覽器上,那么這個(gè)時(shí)候就需要將ES6轉(zhuǎn)換為瀏覽器完全能夠識(shí)別的js進(jìn)行處理,那么webpack的loader就派上用場(chǎng)le。相似的情況還有寫(xiě)sass,less,stylus、以單文件的形式編寫(xiě)vue等等。

起步:

## 創(chuàng)建一個(gè)npm學(xué)習(xí)目錄,并進(jìn)入
mkdir webpack-start
cd webpack-start
## npm初始化
npm init
## 全局安裝webpack
npm install webpack -g

用編輯器打開(kāi)該文件目錄后,應(yīng)該僅僅只能看見(jiàn)一個(gè)package.json文件,這是提供給npm識(shí)別的配置文件。
我們先創(chuàng)建最基礎(chǔ)的目錄結(jié)構(gòu):
-- /node_module :我們下載安裝的模塊都包含在該文件夾下,我們并不需要對(duì)該目錄進(jìn)行操作;如果現(xiàn)在沒(méi)有該目錄不用自己創(chuàng)建,當(dāng)安裝模塊后會(huì)自動(dòng)出現(xiàn)
-- /dist :都說(shuō)了webpack會(huì)起打包的作用,那么打包后總該有一個(gè)輸出,就在該目錄下
-- /src :我們實(shí)際寫(xiě)的代碼都放在該文件下,也稱(chēng)為源文件
---- index.js :源文件的入口文件
-- webpack.config.js:webpack配置文件
-- package.json

現(xiàn)在我們編寫(xiě)一個(gè)最基本的webpack配置文件——webpack.config.js,每當(dāng)執(zhí)行webpack腳本時(shí),都會(huì)自動(dòng)根據(jù)該配置進(jìn)行啟動(dòng)。


var path = require('path');
 
module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  }
}

entry:

表示入口文件,也就是說(shuō)想把什么或者哪些文件打包,可以是對(duì)象或者數(shù)組,使用相對(duì)路徑,例如:

// 多入口文件打包;
// 數(shù)組形式;
entry: ['./src/index.js' ,'./src/print.js']
// 對(duì)象形式,給文件重命名
entry: {
    app: './src/index.js',
    print: './src/print.js',
    common: [x1.js,x2.js] // 將多個(gè)打包到一起

output:

output: {
   path: path.resolve(__dirname, 'dist'), // 表示打包后的輸出地址,常為絕對(duì)路徑
   filename: 'bundle.js' // 打包后的名字
 }
// 如果filename寫(xiě)成
filename: [name].js // 那就代表分別打包,并沒(méi)有打包到一起
// 還可以
filename: [name].[hash].js // 就給每個(gè)生成的文件加上hash,見(jiàn)《webpack進(jìn)階》

現(xiàn)在我們.src/下只有一個(gè)index.js,我們把這段內(nèi)容復(fù)制進(jìn)去,


function createEl(nodeName){
  var node = document.createElement(nodeName);
  return node;
}
 
var h1 = createEl('h1');
h1.innerText = "webpack starting!";
document.body.appendChild(h1);

我們?cè)诿钚兄休斎雡ebpack,回車(chē)。
我們能夠在./dist中看見(jiàn)最新生成的文件bundle.js文件,但是這個(gè)時(shí)候并沒(méi)有html,這個(gè)時(shí)候我們需要在dist中添加一個(gè)index.html


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <script type="text/javascript" src="bundle.js"></script>
</body>
</html>

然后我們?cè)跒g覽器上輸入index.html的路徑,就能看見(jiàn)效果啦!
然后我們現(xiàn)在讓項(xiàng)目變得更加復(fù)雜一點(diǎn),npm下載一個(gè)lodash庫(kù),并且寫(xiě)一個(gè)print.js,都引入index.js中。
print.js:


import _ from 'lodash';
 
export default function printMe() {
  alert(_.join(['welcome', 'printMe'], ' '));
}

更改index.js:

import printMe from './print';
 
function createEl(nodeName){
  var node = document.createElement(nodeName);
  return node;
}
 
var h1 = createEl('h1');
var button = createEl('button');
 
button.onclick = printMe;
h1.innerText = "webpack starting!";
button.innerText = "click me";
 
document.body.appendChild(h1);
document.body.appendChild(button);

現(xiàn)在,我們想嘗試使用[name].js把這兩部分分開(kāi)打包,修改webpack配置


var path = require('path');
 
module.exports = {
  entry: {
    app: './src/index.js',
    print: './src/print.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  }
}

然后,由于文件分為了兩部分,現(xiàn)在我們不得不修改index.html。


<!DOCTYPE html>
<html lang="en">
 
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
 
<body>
</body>
<script type="text/javascript" src="app.js"></script>
<script type="text/javascript" src="print.js"></script>
 
</html>

這樣,就能呈現(xiàn)我們剛剛的效果了,我們發(fā)現(xiàn)每次都要重新手動(dòng)輸入webpack命令進(jìn)行打包,效率很低,那我們可以使用 webpack --watch,現(xiàn)在你嘗試使用webpack --watch啟動(dòng)發(fā)現(xiàn)webpack進(jìn)程一直沒(méi)有結(jié)束,一旦你change,就會(huì)自動(dòng)打包,這樣只需要去刷新一下頁(yè)面就ok啦。
我們還可以為webpack --watch寫(xiě)一個(gè)快捷命令,在package.json中:

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "watch": "webpack --watch"
}

這樣我們僅僅需要在命令行中輸入 npm run watch 就可以啟動(dòng)該命令了。

我們現(xiàn)在已經(jīng)很清楚webpack打包了,我們想知道資源處理,這就需要添加loader了。
我們?cè)趎pm run watch后,在./src下添加一個(gè)文件;style.css

body {
    background: red;
}

在index.js頂部添加一句話(huà):import './style.css'; 然后保存,可以看見(jiàn)控制臺(tái)出現(xiàn)錯(cuò)誤。



因?yàn)閣ebpack是基于node.js使用的,默認(rèn)只認(rèn)識(shí)js文件,現(xiàn)在我么import了一個(gè)css文件,那么webpack肯定是不認(rèn)識(shí)的,我就需要進(jìn)行資源轉(zhuǎn)換:
解析css需要 style-loader,和 css-loader,我們首先使用npm進(jìn)行安裝。

sudo npm install style-loader css-loader --save-dev
  • --save:代表的是,將這個(gè)依賴(lài)關(guān)系記錄到package.json中,打開(kāi)可以看見(jiàn)出現(xiàn)style-loader的字符串及對(duì)應(yīng)的名字。
  • -dev:代表這兩個(gè)模塊并不是生產(chǎn)時(shí)的需要,而是開(kāi)發(fā)時(shí)需要,意思是,一旦webpack打包好了,就不再需要這兩個(gè)模塊了,即使沒(méi)有這兩個(gè)模塊也能正常運(yùn)行,但是開(kāi)發(fā)時(shí)需要實(shí)時(shí)轉(zhuǎn)換所以加上-dev,被記錄在package.json中的差別:



    打包的時(shí)候,非dev的會(huì)一起打包,而dev的不會(huì)一起打包。
    現(xiàn)在我們安裝好了css解析器,這并不代表就能識(shí)別css,還需要我們配置webpack

var path = require('path');
 
module.exports = {
  entry: {
    app: './src/index.js',
    print: './src/print.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [
      {
        test: /.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
}

新引入了module對(duì)象,里面包含一個(gè)rules屬性為數(shù)組,該屬性就是針對(duì)各種類(lèi)型的文件使用各種loader進(jìn)行各種解析。
這里就將.css文件使用style-loader解析 再使用css-loader解析,這時(shí)我們?cè)賜pm run watch時(shí),就能看見(jiàn)頁(yè)面上背景變成了紅色。
更多常用的loader參見(jiàn):資源管理

webpack四大核心概念:入口(entry),出口(output),加載器(module),插件(plugin)。
基本流程: 入口 — 加載器處理 — 出口, 這已經(jīng)很完善了,但是常常我們還需要提供一些與這個(gè)流程無(wú)關(guān)的額外的功能,比如我們現(xiàn)在不僅引入了lodash還引入了jQuery,我不希望這兩個(gè)靜態(tài)引入的文件單獨(dú)打包,那么我們就可以把這兩部分打包在一起:
首先我們先npm install jquery, 然后在index.js中:

import printMe from './print';
import $ from 'jquery';
import './style.css';
 
function createEl(nodeName){
  var node = document.createElement(nodeName);
  return node;
}
 
var h1 = createEl('h1');
var button = createEl('button');
 
button.onclick = printMe;
h1.innerText = "webpack starting!";
button.innerText = "click me";
 
$(document.body).append(h1);
$(document.body).append(button);

然后webpack配置中:


var path = require('path');
 
module.exports = {
  entry: {
    app: './src/index.js',
    print: './src/print.js',
    common: ['jquery', 'lodash']
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [
      {
        test: /.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
}

然后在index.html加入一個(gè)新的script src指向common.js,我們進(jìn)行npm run watch,然后在命令行上看見(jiàn):



我們看見(jiàn)三個(gè)文件后面都有[big],我們明明才寫(xiě)了幾行代碼而已就有800+kb了???
這其實(shí)是webpack打包造的孽,print 引用了lodash 就把 lodash和print打包在了一起, app引用了jquery 又引用了print,就把index jquery lodash全打包在了一起,然后jquery和lodash還單獨(dú)打包了一份。 lodash 和 jquery我們只需要一份就夠了,這里卻打包了這么多次,這不是我們想要的結(jié)果,這個(gè)時(shí)候我們就需要引入一個(gè)插件CommonsChunkPlugin,將公共部分提取出來(lái)。

var path = require('path');
const webpack = require('webpack'); // 引入webpack
 
module.exports = {
  entry: {
    app: './src/index.js',
    print: './src/print.js',
    common: ['jquery', 'lodash']
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [
      {
        test: /.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: "common"
    })
  ]
}

這里我們添加了一個(gè)新的配置,plugins,專(zhuān)門(mén)用來(lái)new需要使用的插件,這個(gè)本身是一個(gè)插件使用數(shù)組。
CommonsChunkPlugin是webpack自帶的插件,我們不需要單獨(dú)npm安裝,直接引入webpack 使用即可。
然后現(xiàn)在,我們?cè)賜pm run wacth看看情況:(如果報(bào)錯(cuò)不能解析webpack,就局部安裝一次webpack:sudo npm install webpack --save-dev)


app.js和print.js瞬間小了很多。
這個(gè)時(shí)候進(jìn)入index.html里可能沒(méi)有內(nèi)容顯示,報(bào)錯(cuò):webpackJson不能識(shí)別。
這個(gè)原因是index.html里引用順序有關(guān),必須先引用common.js 再引用其他js就不會(huì)報(bào)錯(cuò)了。

webpack插件還有許多許多,比如new webpack.optimize.UglifyJsPlugin,打包后的代碼是壓縮過(guò)的,減小體積等等。

我們現(xiàn)在已經(jīng)入門(mén)webpack啦,這里說(shuō)一個(gè)概念,為打包準(zhǔn)備配置叫做生產(chǎn)配置,為開(kāi)發(fā)準(zhǔn)備配置叫做開(kāi)發(fā)配置例如實(shí)時(shí)編譯,下一篇中將會(huì)介紹更多基礎(chǔ)知識(shí)。
想要了解更多,更全面的看文檔是最好不過(guò)的。

系列:
webpack基礎(chǔ)——常用配置解析
webpack進(jìn)階——緩存與獨(dú)立打包

參考資料:
概念
資源管理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容