一、什麼是webpack
webpack官網給出的定義是
本質上,webpack 是一個現代 JavaScript 應用程式的靜态子產品打包器(module bundler)。當 webpack 處理應用程式時,
它會遞歸地建構一個依賴關系圖(dependency graph),其中包含應用程式需要的每個子產品,然後将所有這些子產品打包成一個或多個 bundle。
複制
如上圖: 中間的藍色塊就是webpack. 他會将左邊各種檔案打包成右側html能夠解析的檔案.
總結:
webpack是一個靜态的打包子產品.
這裡涉及兩個概念: 打包和子產品
複制
1. 什麼是子產品?
上一篇文章, 說了什麼是子產品化, 詳見: https://www.cnblogs.com/ITPower/p/14466964.html
子產品化有很多規範, commonJs規範, AMD規範, CMD規範, ES6規範等
在ES6之前, 要想使用子產品化開發,就要借助其他的工具, 而且開發完成以後, 還需要處理各種依賴,并将其進行整合打包.
webpack可以讓我們進行子產品化開發, 他提供了平台, 并且會幫助我們處理各子產品之間的依賴關系.
webpack最終會幫我們将js, css, 圖檔, json檔案等打包為合适的格式, 以供浏覽器使用.
在webpack中上述檔案類型都可以被當做子產品來使用.
2. 什麼是打包?
就是将webpack中各種子產品資源進行打包合并成一個或多個包. 并且在打包的過程中, 可以對資源進行處理, 如:壓縮圖檔, 将scss轉成css, 将ES6文法轉成ES5等可以被html識别的檔案類型.
複制
二. webpack打包工具的安裝
webpack打包工具依賴nodejs. nodejs環境依賴各種包, 這些包使用npm進行管理. npm是什麼呢? npm就是用來管理node下的各種包的
接下來看看webpack如何安裝?
第一步: 安裝nodejs
在官網下載下傳nodejs:https://nodejs.org/zh-cn/ 安裝好以後可以檢視nodejs的版本
node -v
複制
我目前的版本是 v12.16.2
預設安裝nodejs的時候, 就會自動安裝npm, 是以, npm不用單獨安裝
第二步: 安裝webpack
我使用的webpack版本是3.6.0, 因為我目前使用的vue的版本是2, vue2依賴的webpack版本是3.6.0
首先檢視本機是否已經安裝了webpack, 使用指令查詢
webpack --version
複制
如果沒有安裝, 則安裝全局的webpack
npm install [email protected] -g
-g:表示的是global 全局的意思
複制
webpack需要全局安裝, 也需要局部安裝.
我們在終端或者IDE的terminal中使用webpack都是使用的全局的webpack,當我們在項目下使用package.json scripts webpacks, 這時候使用的是局部安裝的.
那什麼是全局webpack ,什麼是本地webpack呢?
我們通常都會安裝兩個webpack, 一個是全局的一個是本地的.
全局的指的是電腦上安裝的webpack包, 所有項目都可以使用
本地webpack是指目前項目的webpack包. 通常全局webpack版本會比較高, 而我的項目是老項目, 使用的是老版本的
webpack打包的, 這時如果使用全局的webpack打包就會報錯, 是以, 需要安裝一個和項目比對的本地的webpack包
在這裡, 我們先值安裝全局的, 後面使用到本地的了, 再來安裝本地的webpack.
三. webpack的基本使用
webpack下通常包含兩個檔案夾, 一個是src, 一個是dist
- src下都會有一個main.js, 作為主js檔案.
- dist存放打包後的檔案
在webpack中,我們會使用兩種類型的模闆來定義: 分别是commonJs文法, 和ES6文法.
- 在main.js引用mathUtil.js, 使用commonjs文法
- 在main.js引用dataUtil.js, 使用ES6文法
下面, 我們建立一個mathUtil.js 和 dataUtil.js兩個js檔案, 分别使用commonJs文法和ES6文法來建立.
- - 在main.js引用mathUtil.js, 使用commonJs文法
- - 在main.js引用dataUtil.js, 使用ES6文法
項目結構如下:
1) 使用commonJs文法
第一步: 在mathUtil.js中export, 使用commonJs子產品的寫法 : module.exports ={add, sub}
function add(num1, num2) {
return num1 + num2
}
function sub(num1, num2) {
return num1 - num2
}
// 使用commonJs導出子產品
module.exports={add, sub}
複制
這裡使用的是commonJs的文法導出方法
第二步: 在main.js中引用mathUtil.js中導出的變量 const {add, sub} = require("檔案名")
const{add, sub} = require("./mathUtil.js")
console.log(add(10, 20))
console.log(sub(20, 10))
複制
第三步: 使用webpack語句打包 : webpack ./src/main.js ./dist/bundle.js
首先進入到目前項目的根目錄下, 然後輸入指令
webpack ./src/main.js ./dist/bundle.js
複制
意思是: 将src目錄下的main.js進行打包, 打包好的檔案放在dist目錄下, 并命名為bundle.js
webpack是自動打包工具, 是以, 我們隻需要打包main.js, 他會自動檢查main.js是否引用了其他檔案. 如果有自動将其打包.
打包以後, 會生成一個bundle.js.
第四步: 在index.html中引入bundle.js檔案
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./dist/bundle.js" ></script>
</head>
<body>
</body>
</html>
複制
引入以後, 就是普通的html代碼了, 我們可以向通路其他html一樣,通路這個頁面.
2) 使用ES6文法
第一步: 在dateUtil.js中導出, 使用ES6寫法: export {var1, var2}
function priceUnit(price) {
return "$" + price.toFixed(2)
}
export {priceUnit}
複制
第二步: 在main.js中引用dateUtil.js中導出的變量 import {var1, var2} from "檔案位址"
import {priceUnit} from "./dataUtil"
console.log(priceUnit(25))
複制
第三步: 使用webpack語句打包 : webpack ./src/main.js ./dist/bundle.js
webpack ./src/main.js ./dist/bundle.js
複制
第四步: 在index中引用bundle.js檔案
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./dist/bundle.js" ></script>
</head>
<body>
</body>
</html>
複制
四. webpack配置檔案
1. 如何使用webpack指令直接打包
剛剛我們打包的時候, 使用的是
webpack ./src/main.js ./dist/bundle.js
複制
那麼, 如果在項目下, 可不可以直接使用webpack, 而不用每次都指定檔案呢? 這樣可以友善很多
當然是可以的, 我們需要在項目根目錄下建立一個檔案: webpack.config.js 這個名字是固定的
這個js就是要告訴我們哪裡是webpack的入口, 哪個是webpack的出口
webpack.config.js
module.export={
entry: './src/main.js',
output: {
path: '/dist',
filename: '/bundle.js'
}
}
複制
- entry用來指定入口, 指定一個路徑
- output用來指定出口. 需要注意的是: 出口是一個對象, 由兩部分組成: path和filename
然後我們在終端輸入webpack打包. 打包會報錯, 報錯資訊提示很明确:
The provide value "./dist" is not an absolute path!
複制
意思是說path應該是已經絕對路徑. 也就是dist的絕對路徑
思考: 我們能直接寫一個絕對路徑麼? 比如: /Users/workspace/vue-study/webpack的配置/src/main.js 這樣可以麼?
這樣肯定不太好, 因為我一旦将檔案文在其他目錄下, 這個位址就變了.
webpack可以幫助我們擷取目前項目的絕對路徑
我們const path = require("path")來擷取相對目錄. 可是目前目錄下沒有path的包, path是node下東西, 需要通過npm init來初始化,
2. 初始化項目npm init
初始化指令
npm init
複制
初始化完成以後, 就會建立一個叫package.json的檔案
通常, 我們需要使用node的東西, 或者單獨依賴node環境的話, 都會執行npm init, 生成package.json
3. 安裝子產品
如果package.json裡面依賴其他子產品, 需要使用npm install的安裝一下, 然後就會在目前檔案夾中安裝一些東西
npm install
複制
- const path = require("path")這裡的path就是node給我們生成的, 我們可以直接使用.
然後我們的output中path就可以這麼寫:
path.resovle(__dirname, "dist")
複制
- _dirname是一個全局變量, resolve是一個函數, 可以将兩個部分的内容拼在一塊, 這樣生成以後就是一個絕對路徑
4. 使用npm run來啟動項目
下面來看一下package.json
{
"name": "meet",
"version": "1.0.0",
"description": "剛剛我們打包的時候, 使用的是webpack ./src/main.js ./dist/bundle.js 那麼, 如果在項目下, 可不可以直接使用webpack, 而不用每次都指定檔案呢? 可以的, 我們需要在項目根目錄下建立一個檔案, webpack.config.js 這個名字是固定的 這個js就是要告訴我們哪個是webpac的入口, 哪個是webpack的出口 通過module.export={ entry: './src/main.js', output: { path: '/dist', filename: '/bundle.js' } } 來告訴我們入口和出口在哪裡. entry用來指定入口, 指定一個路徑 output用來指定出口. 出口是一個對象",
"main": "webpack.config.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
複制
通常我們啟動項的時候會使用npm run serve啟動項目, npm run build建構項目. 當執行這個指令的時候, 就會去package.json中的script标簽中找build指令和serve指令.
執行npm run build讓其執行webpack打包就可以在script中增加"build":"webpack"
{
"name": "meet",
"version": "1.0.0",
"description": "剛剛我們打包的時候, 使用的是webpack ./src/main.js ./dist/bundle.js 那麼, 如果在項目下, 可不可以直接使用webpack, 而不用每次都指定檔案呢? 可以的, 我們需要在項目根目錄下建立一個檔案, webpack.config.js 這個名字是固定的 這個js就是要告訴我們哪個是webpac的入口, 哪個是webpack的出口 通過module.export={ entry: './src/main.js', output: { path: '/dist', filename: '/bundle.js' } } 來告訴我們入口和出口在哪裡. entry用來指定入口, 指定一個路徑 output用來指定出口. 出口是一個對象",
"main": "webpack.config.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
"author": "",
"license": "ISC"
}
複制
然後就可以使用npm run webpack來打包了. 但是這裡打包是全局打包. 因為我們之前隻安裝了一個全局的webpack.
但如果項目使用的版本和全局的不一樣, 怎麼辦呢? 我們還可以使用局部的wenpack打包.
5. 全局webpack和局部webpack有什麼差別呢?
我們通常都會安裝兩個webpack,
- 一個是全局的
- 一個是本地的.
全局的指的是電腦上安裝的webpack包, 所有項目都可以使用
本地webpack是指目前項目的webpack包.
通常全局webpack版本會比較高, 而我的項目是老項目, 使用的是老版本的
webpack打包的, 這時如果使用全局的webpack打包就會報錯, 是以, 需要安裝一個和項目比對的本地的webpack包
6. 安裝本地webpack指令
npm install [email protected] --save-dev
複制
- --save-dev: 這個參數的含義表示開發時依賴.
這裡有兩個概念:
- 1. 開發時依賴
- 2. 運作時依賴
對于打包來說, 我們隻有在開發環境才會打包, 開發好以後要上線了會将其建構成html代碼, 放到伺服器上運作,
放到伺服器以後, 就不需要打包了, 是以, 打包隻需要在開發時使用, 是一個開發時依賴
本地webpack安裝好以後, 會在package.json中看到devDependencies屬性, 這就是開發時依賴
本地webpack安裝好以後, 使用npm run build進行打包. 那是使用的全局webpack打包的還是本地webpack打包的呢?
本地安裝好webpack以後會多出一個檔案夾node_modules, 這裡是預設在本地裝的包, 其中有一個是webpack, 使用這裡面的webpack
就表示使用的局部webpack打包. 而當我們在終端, 或者ide的terminal中執行webpack指令都是全局的
如果想要使用局部webpack打包, 可以使用npm run build. 這時就是去package.json的script腳本中找build指令了.
package.json中腳本指令執行的順序:
- 首先, 在本地的路徑中找指令
- 然後, 本地沒有, 再去全局中找指令
在這裡首先回去本地找有沒有這個指令, 如果沒有就去全局找
五.使用webpack打包css檔案
webpack主要是用來打包的, 我們之前都是打包了js代碼檔案, 那如果還有css, 圖檔, 或者一些進階轉換,如将ES6轉換成ES5,将scss, less轉成css, 将.vue檔案轉換成js檔案怎麼辦呢?
webpack單獨是不能完成轉換的,需要借助loader.
1. 什麼是紹loader?
webpack 可以使用 loader 來預處理檔案。這允許你打包除 JavaScript 之外的任何靜态資源。
loader是webpack中一個非常核心的概念, loader有很多種, 不同的内容需要使用不同的loader來加載.
2. loader的使用
我們首先來建立一個css檔案, 然後将css檔案引入到項目中
第一步: 建立main.css檔案
body{
background-color: #085e7d;
}
複制
第二步: 将main.css檔案引入到main.js中
require("./css/main.css")
複制
這是使用commonJs的寫法引入的. 引入css以後, 不需要任何傳回值, 是以, 我們可以不用寫成 "let 變量名 = require(檔案路徑)"
第三步: 執行npm run build, 會報異常
這個異常的意思是, 缺少合适的loader. 因為我們引入了css, 但是還沒有引入解析css的loader.
css需要兩個loader :
- 一個是css-loader
- 另一個是style-loader
css-loader: 隻負責加載css檔案, style-loader: 負責将樣式加載到Dom中
第四步: 通過npm安裝loader
我們的目标是安裝loader, 可以去官網找到對應的loader. css檔案對應的loader.
官網位址: www.webpackjs.com
找到css-loader的用法
安裝指令
npm install --save-dev css-loader
複制
第五步: 在webpack.config.js中的modules關鍵字下配置
module: {
rules: [
{
test: /\.css$/,
use: ['css-loader']
}
]
}
複制
第六步: 安裝style-loader
安裝方法和css是一樣的. 可以通過查詢官網找到方法
安裝style-loader指令
npm install --save-dev style-loader
複制
然後在将style-loader放在css-loader的前面
module: {
rules: [
{
test: /\.css$/,
// css-loader: 隻負責加載css檔案
// style-loader: 負責将樣式加載到Dom中
// 注意: 使用loader加載的時候, 是從右向左加載的. 是以, 先加載css-loader, 在加載style-loader
use: ['style-loader','css-loader' ]
}
]
}
複制
為什麼需要将style-loader放在前面呢?
其實在解析css的過程中, 先用到的是css-loader, 然後使用到的是style-loader. 但是webpack.config.js在解析的時候, 是從右往左解析的.
第七步: 打包
npm run build
複制
打包以後報錯:
css (node:93638) UnhandledPromiseRejectionWarning: TypeError: this.getResolve is not a function
複制
遇到這個問題, 說明版本不合适, 我們使用的webpack是3.6.0的版本, 打包的時候, 我們需要重新制定一下css-loader和style-loader的版本号
打開package.json, 找到devDependencies
"devDependencies": {
"css-loader": "^2.1.1",
"style-loader": "^2.0.0",
"webpack": "^3.6.0",
}
複制
指定css-loader和style-loader的版本号分别是2.1.1和2.0.0
再次打包, 成功!
六. webpack打包less檔案
其實我們知道如何打包css檔案了, 那麼打包less檔案方法是類似的
第一步: 定義一個less檔案
我們定義一個less檔案, 起個名字common.less
@fontsize: 24px;
@fontcolor: #5112b8;
body {
font-size: @fontsize;
color: @fontcolor;
}
複制
第二步: 将less檔案import引入到main.js中
require("./css/special.less")
複制
第三步: 安裝less元件, 應該安裝哪些元件呢? 可以看https://cn.vuejs.org/v2/guide/
查詢官網: www.webpackjs.com
安裝元件指令
npm install --save-dev less-loader less
複制
添加規則
module.exports = {
...
module: {
rules: [{
test: /\.less$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "less-loader" // compiles Less to CSS
}]
}]
}
};
複制
注意: 順序不能改變
第四步: 重新打包.
npm run build
複制
七. webpack打包圖檔檔案
下面來看看webapck是如何打包圖檔的
第一步: 在css中引入一個圖檔檔案
比如: 我引入了一個背景圖
然後将圖檔作為背景引入到main.css中
body{
background: url("../img/123.jpeg");
}
複制
第二步: 将main.css檔案通過import引入到main.js中
require("./css/main.less")
複制
我們知道webpack是自動打包工具, 在打包main.js的時候, 他會看裡面都引入了哪些内容. 他發現引入了main.css, 就是去自動加載并解析css對應的子產品. 在css中引入了圖檔, 就會去自動加載并解析圖檔對應的子產品.
第三步: 安裝解析圖檔的元件
查詢官網: www.webpackjs.com
我們看到background是通過url引入的, 首先需要url-loader子產品.
安裝元件指令
npm install --save-dev url-loader
複制
添加規則
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [ {
loader: 'url-loader',
options: {
limit: 8000,
}
}]
}
複制
我們發現這次引入的時候有一個options參數, 這個參數限制了圖檔的大小.
注意:
- 當加載的圖檔, 小于limit時, 會将圖檔編譯成base64字元串形式. --- 不需要檔案, 因為他是一個base64字元串
- 當加載的圖檔, 大于limit是, 需要使用file-loader子產品來加載. --- 當檔案來處理, 就需要打包成檔案, 需要file-loader
當以檔案的形式加載的時候, 需要指定一個打包路徑. 否則加載的檔案目錄是根目錄, 結果會找不到檔案, 因為我們的檔案最終打包到dist下面了.
module.exports={
// 入口
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, 'dist'),
filename: "bundle.js",
publicPath:"dist/"
}
...
}
複制
我們可以在output位置增加publicPath:"dist/" 以後, 所有的路徑類的檔案在打包編譯的時候, 都會打包到這個路徑下面
八. webpack打包--将ES6打包成ES5
為什麼需要将es6打包成es5呢? 因為上述方式的webpack打包後, 并沒有将ES6的文法轉換成ES5的, 比如:
這會有什麼問題呢?
有些浏覽器可能不認識. 因為不是所有的浏覽器都相容ES6, 但基本所有的浏覽器都相容ES5的文法. 是以我們需要将ES6的文法轉換成ES5的文法
方法和上面是一樣的.
第一步: 安裝元件
打包ES6到ES5需要的元件是bebal
查詢官網需要安裝哪些元件: www.webpackjs.com
安裝指令:
npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
複制
我們這裡安裝的是babel-loader的7的版本. babel-preset的版本是es2015
第二步: 配置babel-loader元件
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
}
]
}
複制
這個配置裡面指定了exclude, 排除哪些目錄: 這裡排除了node_modules目錄, 因為這個目錄下的檔案我們不需要打包. 是node編譯需要的内容.
presets屬性,用來指定将es6轉換成es5需要的版本. 我們這裡直接填es2015就可以了.
第三步: 打包
npm run build
複制
這次在去看bundle.js檔案, 裡面就沒有es6的文法了