1. 什么是webpack
webpack可以看做是模塊打包機(jī):它做的事情是,分析你的項目結(jié)構(gòu),
找到JavaScript模塊以及其它的一些瀏覽器不能直接運(yùn)行的拓展語言(Scss,TypeScript等),并將其打包為合適的格式以供瀏覽器使用。
構(gòu)建就是把源代碼轉(zhuǎn)換成發(fā)布到線上的可執(zhí)行 JavaScrip、CSS、HTML 代碼
代碼轉(zhuǎn)換:LESS、SCSS 編譯成 CSS 等。
文件優(yōu)化:壓縮 JavaScript、CSS、HTML 代碼,壓縮合并圖片等。
代碼分割:提取多個頁面的公共代碼、提取首屏不需要執(zhí)行部分的代碼讓其異步加載。
模塊合并:在采用模塊化的項目里會有很多個模塊和文件,需要構(gòu)建功能把模塊分類合并成一個文件。
自動刷新:監(jiān)聽本地源代碼的變化,自動重新構(gòu)建、刷新瀏覽器。
代碼校驗:在代碼被提交到倉庫前需要校驗代碼是否符合規(guī)范,以及單元測試是否通過。
自動發(fā)布:更新完代碼后,自動構(gòu)建出線上發(fā)布代碼并傳輸給發(fā)布系統(tǒng)。
- webpack核心概念
Entry:入口,Webpack 執(zhí)行構(gòu)建的第一步將從 Entry 開始,可抽象成輸入。
Module:模塊,在 Webpack 里一切皆模塊,一個模塊對應(yīng)著一個文件。Webpack 會從配置的 Entry 開始遞歸找出所有依賴的模塊。
Chunk:代碼塊,一個 Chunk 由多個模塊組合而成,用于代碼合并與分割。
Loader:模塊轉(zhuǎn)換器,用于把模塊原內(nèi)容按照需求轉(zhuǎn)換成新內(nèi)容。
Plugin:擴(kuò)展插件,在 Webpack 構(gòu)建流程中的特定時機(jī)注入擴(kuò)展邏輯來改變構(gòu)建結(jié)果或做你想要的事情。
Output:輸出結(jié)果,在 Webpack 經(jīng)過一系列處理并得出最終想要的代碼后輸出結(jié)果。
- 初始化package.json
npm init -y
默認(rèn)約定webpack 的 4.x 版本中默認(rèn)約定
打包的 入口文件為 src -> index.js
打包的 輸出文件為 dist -> main.js
修改 修改打包的入口與出口,可以在 webpack.config.js 中新增加如下配置信息
const path = require('path') // 導(dǎo)入 node.js 中專門操作路徑的模塊 path
module.exports = {
entry: path.join(_dirname,'./src/index.js'),//打包入口文件的路徑
output: {
path: path.join(__dirname,'./dist'), // 輸出文件的存放路徑
filename: 'bundle.js' //輸出文件的名稱
}
}
配置自動打包
npm install webpack-dev-server -D
-D 指安裝依賴到開發(fā)環(huán)境,上線環(huán)境使用 -S
- 修改 package.json 文件中 scripts 對象的 dev 命令,如下配置:
// package.json 中的配置
"scripts": {
"dev": "webpack-dev-server --profile --progress --colors" // script 節(jié)點(diǎn)下的腳本,可以通過 npm run 執(zhí)行
},
//--display-modules --profile --progress --colors --display-error-details
//color 輸出結(jié)果帶彩色,比如:會用紅色顯示耗時較長的步驟
//profile 輸出性能數(shù)據(jù),可以看到每一步的耗時
//progress 輸出當(dāng)前編譯的進(jìn)度,以百分比的形式呈現(xiàn)
//display-modules 默認(rèn)情況下 node_modules 下的模塊會被隱藏,加上這個參數(shù)可以顯示這些被隱藏的模塊
//display-error-details 輸出詳細(xì)的錯誤信息
將 src -> index.html 中,scripts 腳本的引用路徑 ,修改為根目錄下的 buldle.js
注意:
webpack-dev-server 會啟動一個實(shí)時打包的 http 服務(wù)器
webpack-dev-server 打包生成的輸出文件,默認(rèn)放到了項目根目錄中,而且是虛擬的,看不到的
// --open 打包完成后自動打開瀏覽器頁面 --host 配置 IP地址 --port 配置端口
"dev": "webpack-dev-server --profile --progress --colors --open --port 8888"
配置預(yù)覽頁面
- 在終端中運(yùn)行如下命令,安裝生成預(yù)覽頁面的插件
npm install html-webpack-plugin -D - 修改 webpack.config.js 文件頭部區(qū)域,添加如下配置信息:
// 導(dǎo)入生成預(yù)覽頁面的插件,得到一個構(gòu)造函數(shù)
const HtmlWebpackPlugin = require('html-webpack-plugin')
const htmlPlugin = new HtmlWebpackPlugin({//創(chuàng)建插件的實(shí)例對象
template: './src/index.html',// 指定要用到的模版文件
filename: 'index.html'//指定生成的文件的名稱,該文件存在于內(nèi)存中,在目錄中不顯示
})
- 修改 webpack.config.js 文件中向外暴露的配置對象 ,新增如下配置節(jié)點(diǎn):
module.exports = {
plugins: [ htmlPlugin ] //plugin 數(shù)組是 webpack 打 包期間會用到的一些插件列表
}
2. 配置webpack
2.1 配置文件webpack.config.js
//entry:配置入口文件的地址
//output:配置出口文件的地址
//module:配置模塊,主要用來配置不同文件的加載器
//plugins:配置插件
//devServer:配置開發(fā)服務(wù)器
// 基于node的 遵循commonjs規(guī)范的
let path = require('path');
module.exports = {
// 入口
entry: './src/index.js',
// 出口
output: {
filename: 'build.js', // 這個路徑必須是絕對路徑
path: path.resolve('./dist')
},
// 開發(fā)服務(wù)器
devServer: {
//contentBase 配置開發(fā)服務(wù)運(yùn)行時的文件根目錄
contentBase: path.resolve(__dirname, 'dist'),
//host:開發(fā)服務(wù)器監(jiān)聽的主機(jī)地址
host: 'localhost',
//port:開發(fā)服務(wù)器監(jiān)聽的端口
port: 8080,
//compress 開發(fā)服務(wù)器是否啟動gzip等壓縮
compress: true,// 服務(wù)器壓縮
open: true,// 自動打開瀏覽器
// hot:true//熱更新
},
module: {}, // 模塊配置
plugins: [], // 插件的配置
mode: 'development', // 可以更改模式
resolve: {}, // 配置解析}//
//在webpack中如何配置開發(fā)服務(wù)器 webpack-dev-server
}
//開啟本地服務(wù) npm run dev
"scripts": {
"build": "webpack --mode development --profile --progress ",
"dev": "webpack-dev-server --open --mode development --profile --progress "
}
2.2 入口文件的類型
單入口+單出口
entry: './src/index.js',
2.4 多入口數(shù)組形式+單出口
entry:['./src/index.js','./src/a.js']
2.4 多入口+多出口
有時候我們的頁面可以不止一個HTML頁面,會有多個頁面,所以就需要多入口
npm i html-webpack-plugin -D
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// 入口
entry: {
index: './src/index.js',
main: './src/main.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[hash].js',
publicPath: '/'
},
// 插件的配置
plugins: [
new HtmlWebpackPlugin({
minify: {
removeAttributeQuotes: true, //刪除屬性雙引號
collapseWhitespace: true //壓縮空白
},
hash: true,
template: './src/index.html',
chunks: ['index'],
title: 'index',
filename: 'index.html'
}),
new HtmlWebpackPlugin({
minify: {
removeAttributeQuotes: true, //刪除屬性雙引號
collapseWhitespace: true //壓縮空白
},
hash: true,
template: './src/index.html',
chunks: ['main'],
title: 'main',
filename: 'main.html',
})
]
}
2.5 我們可以使用插件來生成html文件,這樣就避免了html每次去手動引入js;使用插件來刪除上次打包的結(jié)果
使用CleanWebpackPlugin可以每次構(gòu)建前清空輸出目錄.
當(dāng)你多次打包的時候,你會發(fā)現(xiàn)由于使用hash來命名輸出的文件每次的文件名稱都不一樣,導(dǎo)致文件越來越多.
npm i -D html-webpack-plugin clean-webpack-plugin
//修改webpack.common.js文件
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
...
plugins:[
new HtmlWebpackPlugin({template:'./src/index.html'}),
new CleanWebpackPlugin()
]
...
}
2.6 每次修改完文件都要重新打包才能看到效果,我們可以使用webpack-dev-server來搭建一個本地服務(wù)器來實(shí)時更新。
npm i -D webpack-merge
//webpack.dev.js
const merge = require('webpack-merge')
const common = require('./webpack.common')
module.exports = merge(common, {
mode: 'development',
devServer: {
contentBase: './index.html',
hot: true,
port: 3000
}
})
//修改package.json的dev
"scripts": {
"dev": "webpack-dev-server --open --config ./config/webpack.dev.js",
...
},
2.7 安裝babel
npm i -D @babel/preset-env babel-loader @babel/core
//webpack.common.js
module.exports = {
module: {
rules: [
{
test: /\.(js)$/,
exclude: /(node_modules|bower_components)/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
]
},
]
}
}
2.8 處理css
npm i -D style-loader css-loader
//webpack.common.js
...
module:{
rules:[
...
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
//index.css
...
.red {
color: red;
}
可以看到樣式被插進(jìn)了head中。當(dāng)項目變大時樣式直接插入head中的方式并不好,我們需要將樣式分離
webpack4的版本中建議使用mini-css-extract-plugin插件(以前是ExtractTextWebpackPlugin插件)
npm i -D mini-css-extract-plugin
//webpack.common.js
...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
rules:[
...
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
// you can specify a publicPath here
// by default it uses publicPath in webpackOptions.output
publicPath: '../',
hmr: process.env.NODE_ENV === 'development',
},
},
'css-loader',
],
}
]
plugins:[
...
new MiniCssExtractPlugin({
filename: 'static/css/[name].css', //打包到static的css目錄下
ignoreOrder: false, // Enable to remove warnings about conflicting order
})
]
開發(fā)環(huán)境不存在這個,在生產(chǎn)環(huán)境中,打包的文件上傳服務(wù)器,被用戶使用后,會被緩存在瀏覽器時;當(dāng)需要修改代碼時,又重新打包上傳,而用戶的瀏覽器發(fā)現(xiàn)加載的文件名字一樣,就不會重新加載,還是會從緩存中加載,就造成了修改的代碼不能更新使用;
解決方法:每次打包的文件,都會有自己的hash值,只要文件修改,hash值才會變化,不修改,無論打包幾次,值都不會發(fā)送改變;
在webpack.prod.js生成配置中輸出時使用contenthash,這樣修改的文件名就會改變,而用戶的瀏覽器發(fā)現(xiàn)不一樣的文件,就會重新加載,相同名文件則不會加載,這有提高了性能
一個滾動插件https://www.npmjs.com/package/smooth-scroll
Shimming:墊片,解決某些問題
** webpack的核心就是模塊與模塊之間分開,**
** 但是當(dāng)有些公共模塊或庫需要在多個模塊中調(diào)用,**
** 或者比如有一些第三方庫使用了JQ的$,但是不是用import引用的,當(dāng)你調(diào)用時,不能識別,**
** 就可以在webpack的配置中引入webpack的插件ProvidePlugin**
以JQ為例,當(dāng)模塊使用 from 'jquery'
通過懶加載的方式動態(tài)引入文件
const router = new Router({
routes: [{
path: '/home',
name: 'Home',
component: () =>
import('./views/home/Home.vue')
}]
})
解決方案
安裝插件babel-plugin-dynamic-import-webpack
npm install babel-plugin-dynamic-import-webpack --save-dev
在配置文件的module的rules下進(jìn)行插件的配置,如下
{
test: /\.js/,
use: [{
loader: 'babel-loader',
options: {//如果有這個設(shè)置則不用再添加.babelrc文件進(jìn)行配置
"babelrc": false,// 不采用.babelrc的配置
"plugins": [
"dynamic-import-webpack"
]
}
}]
}