webpack
是一個現代 JavaScript 應用程序的靜態模塊打包器(module bundler),當 webpack
處理應用程序時,會遞歸構建一個依賴關係圖,其中包含應用程序須要的每一個模塊,而後將這些模塊打包成一個或多個 bundle
。css
新建 index.html
,以下:html
<!DOCTYPE html>
<html lang="en">
<body>
<p>這是網頁內容</p>
<div id="root"></div>
<script src="./header.js"></script>
<script src="./content.js"></script>
<script src="./footer.js"></script>
<script src="./index.js"></script>
</body>
</html>
複製代碼
再創建index.js
、header.js
、 content.js
、 footer.js
vue
//index.js
new Header()
new Content()
new Footer()
//header.js
function Header () {
var dom = document.getElementById('root')
var header = document.createElement('div')
header.innerText = 'header'
dom.append(header)
}
//content.js
function Content () {
var dom = document.getElementById('root')
var content = document.createElement('div')
content.innerText = 'content'
dom.append(content)
}
//footer.js
function Footer () {
var dom = document.getElementById('root')
var footer = document.createElement('div')
footer.innerText = 'footer'
do
m.append(footer)
}
複製代碼
這樣面向對象new出各個模塊的構造函數,在必定程度上比面相過程所有寫在一個文件上好維護,可是Header
、Contet
這些構造函數指向問題還要回到index.html
文件一一對應查找,就形成了在index.js
上指定不明確,若是咱們能像如下代碼同樣導入就能解決這個問題了node
//index.js
import Header from './header.js'
import Content from './content.js'
import Footer from './footer.js'
new Header()
new Content()
new Footer()
複製代碼
遺憾的是,瀏覽器不會識別這種es6的語法,使用瀏覽器訪問index.html
會發現控制檯會報錯。此時咱們的webpack
就能夠爲了解決這種問題而誕生了webpack
首先想使用webpack
,咱們得先npm
初始化包並安裝webpackcss3
npm init
npm install webpack webpack-cli -D
複製代碼
想用ESmodule
語法導出,咱們得修增長導出語法es6
//header.js
export default Header
//content.js
export default Content
//footer.js
export default Footer
複製代碼
而後咱們就可使用npx
運行 webpack
執行 index.js
文件web
npx webpack index.js
複製代碼
執行後,咱們發發現根目錄下生成了一個 dist/main.js
目錄文件,這即是webpack默認出口輸出(output
),後面會介紹。這個文件即是webpack翻譯咱們的index.js
生成後的文件,因此有的人會稱webpack
是JS
的翻譯器,但這說法也並不完成正確。既然咱們翻譯了文件,咱們就須要在index.html
修改爲導入後的文件,以下修改:npm
//index.html
<script src="./dist/main.js"></script>
複製代碼
修改完成後打開瀏覽器從新訪問index.html
發現能夠生成相對於模塊而且沒有報錯了,這就是webpack
的模塊開發。json
webpack 的核心概念
entry
: 入口
output
: 輸出
loader
: 模塊轉換器,用於把模塊原內容按照需求轉換成新內容
插件(plugins
): 擴展插件,在webpack構建流程中的特定時機注入擴展邏輯來改變構建結果或作你想要作的事情
在 webpack v4
中,能夠無須任何配置,由於webpack
提供了默認配置,然而大多數項目會須要很複雜的設置,這就是爲何 webpack 仍然要支持 配置文件。這比在 terminal(終端) 中手動輸入大量命令要高效的多,因此讓咱們在根目錄建立一個配置文件:webpack.config.js
(默認配置文件,可經過npx webpack --config webpack.config.js
修改)
//webpack.config.js
const path = require('path')
module.exports = {
entry: './src/index.jss' , //入口文件 默認:src/index.js
output: { //出口文件 默認: dist/main.js
filename: 'bundle.js', //輸出的文件名
path: path.resolve(__dirname,'dist') //輸出的路徑,只能是絕對路徑
}
}
複製代碼
新建src文件夾,移動index.js,header.js
等js到src文件夾,執行如下代碼會發現dist文件夾生成了新的出口文件bundele.js
npx webpack src/index.js
複製代碼
配置須要打包入口文件
//webpack.config.js
<!--單個文件-->
module.exports = {
entry: './src/index.jss' , //入口文件 默認:src/index.js
}
<!--打包多個入口文件-->
module.exports = {
entry: {
main: './src/index.js', //入口文件 默認:src/index.js
sub: './src/sub.js'
},
}
複製代碼
配置打包輸出的文件
output: { //出口文件 默認: dist/main.js
filename: 'bundle.js', //輸出的文件名
path: path.resolve(__dirname,'dist') //輸出的路徑,只能是絕對路徑
}
<!--多個入口文件須要不一樣名稱文件輸出配置-->
output: { //出口文件 默認: dist/main.js
filename: '[name].js', //輸出的文件名
path: path.resolve(__dirname, 'dist') //輸出的路徑,只能是絕對路徑
},
複製代碼
考慮到用 CLI 這種方式來運行本地的 webpack 副本並非特別方便,咱們能夠設置一個快捷方式。調整 package.json 文件,添加在 npm scripts
中添加一個 npm 命令:
//package.json
"scripts": {
"bundle" : "webpack"
},
複製代碼
如今,可使用 npm run build
命令,來替代咱們以前使用的 npx
命令。注意,使用 npm scripts
,咱們能夠像使用 npx 那樣經過模塊名引用本地安裝的 npm packages。這是大多數基於 npm
的項目遵循的標準,由於它容許全部貢獻者使用同一組通用腳本(若是必要,每一個 flag 都帶有 --config 標誌)。
如今運行如下命令,而後看看你的腳本別名是否正常運行:
npm run bundle
複製代碼
npm run bundle
> webpack4@1.0.0 bundle E:\project\webpack4
> webpack
Hash: 768c04b37ed214487576
Version: webpack 4.42.0
Time: 98ms
Built at: 2020-03-24 4:57:54 PM
Asset Size Chunks Chunk Names
bundle.js 1.29 KiB 0 [emitted] main
Entrypoint main = bundle.js
[0] ./src/index.js + 3 modules 741 bytes {0} [built]
| ./src/index.js 147 bytes [built]
| ./src/header.js 202 bytes [built]
| ./src/content.js 198 bytes [built]
| ./src/footer.js 194 bytes [built]
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
複製代碼
從webpack
打包輸出的結果咱們能夠看出有個警告,那是由於咱們沒有指定打包輸出的環境('development'
or'production'
),咱們能夠在webpack.config.js
新增如下代碼:
module.exports = {
mode: 'development', //默認爲production
...
}
複製代碼
從新執行npm run bundle
發現不會提出警告了,而且生成的bundle.js
的代碼沒有被壓縮
webpack 可使用 loader
來預處理文件。這容許你打包除 JavaScript 以外的任何靜態資源。若是你在默認配置下打包除js文件外出錯,因此咱們要藉助loader
來打包js外的文件
如今咱們在src
文件下存放一張logo.jpg
的圖片,而後在index.js引入後使用webpack
打開
//index.js
const logo = require('./logo.jpg')
複製代碼
執行npm run bundle
打包後會拋出以下錯誤:
ERROR in ./src/logo.jpg 1:0
Module parse failed: Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)
@ ./src/index.js 5:13-34
複製代碼
這是由於webpack像咱們上面說的同樣,只能打包js文件,若是想打包除js之外的靜態資源,此時咱們就須要借用loader來幫助咱們打包圖片資源。想打包圖片咱們首先得先安裝相關的loader
npm install file-loader -D
複製代碼
而後咱們須要在webpack.config.js
文件下配置相關的module
規則:
//webpack.config.js
...
module: {
rules: [
{
test: /\.jpg$/,
use: [
{
loader: 'file-loader' //使用相對應的loader
}
]
}
]
}
複製代碼
控制檯npm run bundle
再次運行時,發現打包成功沒有報錯,而且在dist出口目錄生成了相應的圖片資源。那咱們導入的圖片資源變量會是什麼呢,咱們試着打印一下:
const logo = require('./logo.jpg')
console.log('logo',logo)
複製代碼
vue
的腳手架項目中能夠這樣引入.vue相關文件的了
import Header from 'Header.vue'
複製代碼
//webpack.config.js
rules: [
...
{
test: /\.vue$/,
use: { //只使用一個loader能夠直接用對象配置
loader: 'vue-loader'
}
}
]
複製代碼
上面案例中,咱們能夠看到options
字段,這是咱們能夠打包文件資源的時候定義相對應的規則,好比:
rules: [
{
test: /\.jpg$/,
use: [
{
loader: 'file-loader', //使用相對應的loader
options: {
name:'[name].[ext]', //定義打包生成的文件名字
outputPath: 'images' //定義輸出的文件目錄:dist/images 下
},
}
]
}
]
複製代碼
這樣咱們就能夠自定義打包文件的名字和目錄,更多的規則能夠查看官方文檔配置
咱們能夠用url-loader
取代file-loader
來實現靜態文件資源打包,那爲何咱們有file-loader還要用url-loader,讓我看看下面的例子就知道了:
首先咱們先npm install url-loader --save-dev
安裝file-loader
,而後在webpack.config.js
進行相對應的配置
rules: [
{
test: /\.jpg$/,
use: [
{
loader: 'url-loader', //使用相對應的loader
options: {
limit: 10240 //單位:字節 超過10kb 的文件生成圖片,不然生成base64編碼
}
}
]
}
]
複製代碼
運行打包後咱們能夠發現,超過10kb 的文件生成圖片,不然生成base64編碼
。這樣作的好處是什麼呢,圖片生成base64編碼能夠大大減小咱們https請求,但不是全部的圖片都生成base64編碼,好比圖片幾M的生成的話,相對應js文件大小也會增長,網頁打空白的時間也相對應增長,至於哪些須要轉,limit須要設置多大限制根據本身的項目來,個人項目中通常是icon圖標類的會轉base64編碼,其餘大的相對應打包生成文件。
當咱們嘗試打包css文件的時候,若是沒有使用相對應的樣式loader
就會打包失敗。咱們在src
目錄下建立新的文件index.css
、logo.css
,而且在index.js引入該樣式文件:
//logo.css
.logo{
width: 100px;
height: 100px;
}
複製代碼
//index.css
@import './logo.css'
複製代碼
//index.js
import logo from './logo.jpg'
import './index.css'
var img = new Image()
img.src = logo
img.classList.add('logo')
var root = document.getElementById('root')
root.append(img)
複製代碼
打包以後能夠看到控制檯拋出了相對應的報錯,此時咱們應該接入css-loader
和style-loader
來解決這個問題,首先咱們先得安裝兩個loader
npm install css-loader style-loader -D
複製代碼
rules: [
...
{
test: /\.css$/,
//注:打包執行順利從右到左,從下到上,不能顛倒,先接css-loader轉換語法在使用style-loader解析到頭部標籤
use: ['style-loader','css-loader']
}
]
複製代碼
主要用於打包css文件中解析@import
等語法,將 CSS 轉化成 CommonJS 模塊。
css-loader
還能夠配置更多的選項,好比importLoaders
modules
等。若是沒有配置importLoaders
,在一個scss文件中@import
其餘的scss文件,可能該導入的scss文件不會生效css-loader後面配置的loader(sass-loader
,postcss-loader
)
use: ['style-loader',{
loader: 'css-loader',
options: {
importLoaders: 2 // 0 => no loaders (default); 1 => postcss-loader; 2 => postcss-loader, sass-loader
}
},'sass-loader','postcss-loader'
]
複製代碼
配置modules
參數爲true能夠模塊化導入相關的樣式文件,不然會全局樣式污染。以下列案例:
//index.js
import logo from './logo.jpg'
import './index.scss'
import createLogo from './logo.js'
createLogo()
var img = new Image()
img.src = logo
img.classList.add('logo')
var root = document.getElementById('root')
root.append(img)
複製代碼
//logo.js
import logo from './logo.jpg'
function createLogo () {
var img = new Image()
img.src = logo
img.classList.add('logo')
var root = document.getElementById('root')
root.append(img)
}
export default createLogo
複製代碼
上面由於沒有配置相關的樣式模塊導入,因此導入index.scss
文件的樣式都在兩張圖片成功生效,下面咱們增長下模塊配置引入:
//webpack.congig.js
...
test: /\.scss$/,
use: ['style-loader', {
loader: 'css-loader',
options: {
importLoaders: 2, // 0 => no loaders (default); 1 => postcss-loader; 2 => postcss-loader, sass-loader
modules: true //按模塊化引入
}
}, 'sass-loader', 'postcss-loader'
]
}
複製代碼
//index.js
import logo from './logo.jpg'
import style from './index.scss'
import createLogo from './logo.js'
createLogo()
var img = new Image()
img.src = logo
img.classList.add(style.logo)
var root = document.getElementById('root')
root.append(img)
複製代碼
從新打包後,咱們發現只有index.js文件的圖片生效了樣式,咱們模塊化導入樣式成功,更多的options
配置樣式能夠查看官方文檔
配合css-loader
使用,以形式在html頁面中頭部標籤插入css
代碼。
npm install sass-loader node-sass webpack --save-dev
複製代碼
咱們除了安裝sass-lader
外,而且還須要你預先安裝 Node Sass
。 這能夠控制全部依賴的版本, 並選擇要使用的 Sass
實現。新建src/index.sass
//index.sass
body {
.logo{
width: 100px;
height: 100px;
}
}
複製代碼
//index.js
import logo from './logo.jpg'
import './index.sass'
var img = new Image()
img.src = logo
img.classList.add('logo')
var root = document.getElementById('root')
root.append(img)
複製代碼
咱們須要在webpack.config.js
新增相對應的規則配置:
//webpack.config.js
rules: [
...
{
test: /\.sass$/,
use: ['style-loader','css-loader','sass-loader'] //先把sass轉成css ,再進行起來的loader操做(右到左)
}
]
複製代碼
配置後從新執行npm run bundle
打包,在瀏覽器中能夠正常訪問,把sass-loader
去掉再打包後,能夠查看控制檯頭部樣式中sass
的語法沒有轉成css
,這就是sass-loader的做用
npm install postcss-loader -D
複製代碼
postcss-loader
能夠對css3
樣式前綴自動補全,兼容各個瀏覽器,使用postcss-loader
前咱們得配置相關的插件等,根目錄下新建postcss.config.js
,安裝相對應的插件:autoprefixer
(補全css3語法插件)
npm install autoprefixer -D
複製代碼
//postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')
]
}
複製代碼
//webpack.config.js
rules: [
...
{
test: /\.scss$/,
use: ['style-loader','css-loader','sass-loader','postcss-loader']
}
]
複製代碼
autoprefixer
補全得結合browserslist
一塊兒使用
//package.json
"browserslist": [
"defaults",
"ie >= 10",
"last 2 versions",
"> 1%",
"iOS 7",
"last 3 iOS versions"
],
複製代碼
插件(plugins
): 擴展插件,在webpack
構建流程中的特定時機注入擴展邏輯來改變構建結果或作你想要作的事情,就相似vue
生命週期鉤子同樣,在某種場景,幫你作某些事情。官方已收錄的插件
一種用於打包生成html的插件:HtmlWebpackPlugin
會在打包結束後,自動生成html文件,並把打包生成的js自動引入到這個html文件中。具體配置可查看HtmlWebpackPlugin文檔
//安裝HtmlWebpackPlugin文檔
npm install --save-dev html-webpack-plugin
複製代碼
//webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html' //生成的模板文件
}),
]
}
複製代碼
//public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>html 模板</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
複製代碼
有時候咱們打包老是要手動刪除掉上一次打包的文件,咱們就想有沒有什麼工具能幫助咱們在打包前自動刪除掉dist目錄,CleanWebpackPlugin
就能夠幫咱們解決這個問題,詳細配置
//webpack.config.js
...
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './public/index.html'
}),
]
複製代碼
source-map
能夠解決打包後代碼報錯的地方是打包後的代碼而不是源業務代碼的問題
//index.js
console.log('devtool',test)
複製代碼
這是未設置source-map報錯的打包代碼
devtool: 'source-map'
複製代碼
配置後從新打包久能夠看到報錯的是原業務代碼,可是咱們不建議直接用source-map
,我建議開發環境使用eval-cheap-module-source-map
,生成環境用cheap-module-source-map
,不一樣的配置打包的速度不同,能夠簡單總結,source-map
會生成.map文件來映射,打包速度會很慢,由於還要映射打包文件,inline
能夠不生產.map文件,直接打包在出門文件裏面轉成和base64,cheap
能夠只報行除出錯而不加列出錯,module
可讓第三方loader 插件也生效報錯,eval
能夠直接執行eval函數因此速度最快,具體能夠參考官方文檔配置
有時候咱們修改了打包入口的文件,老是要從新打包編譯打開瀏覽器訪問,有沒有一種配置能讓咱們監聽到入口文件修改,就能自動打包編譯在瀏覽器刷新呢,webpackDevServer
就能夠幫你作到,webpackDevServer
會在本地幫你的項目搭建一個服務器來跑,只要你更新它就能夠幫你從新打包編譯~
首先咱們得安裝webpackDevServer
npm install webpack-dev-server -D
複製代碼
而後配置相關的參數
//webpack.config.js
module.exports = {
...
devServer: {
contentBase: './dist',
port: 9000, //服務端口號
open: true, //首次打包編譯自動打開瀏覽器
proxy: {//反向代理,通常用於解決跨域問題
'/api': 'http://localhost:3000'
}
},
複製代碼
執行npm run start
就能夠打包編譯幫你打開相關的服務了~
//package.json
"scripts": {
"start": "webpack-dev-server"
},
複製代碼
有時候咱們須要作的是,改了該模塊的代碼,瀏覽器不刷新,只更新該的模塊代碼上去,Hot Module Replacement
就能幫咱們實現這個效果。
//webpack.config.js
const webpack = require('webpack')
devServer: {
...
hot: true,//使用 Hot Module Replacement
hotOnly: true, //Hot Module Replacement 出錯的時候,瀏覽器照樣不刷新
},
plugins: [
...
new webpack.HotModuleReplacementPlugin()
]
複製代碼
js模塊代碼更新的話還須要增長,css模塊的話css-loader
已經幫咱們處理了,那像vue的文件修改vue-loader
也已經作了相關的處理
if (module.hot) {
module.hot.accept('./number.js', function() {
// Do something with the updated library module...
});
}
複製代碼