本文主要從webpack4.x入手,會對平時經常使用的Webpack配置一一講解,各個功能點都有對應的詳細例子,因此本文也比較長,但若是你能動手跟着本文中的例子完整寫一次,相信你會以爲Webpack也不過如此。 css
簡單來講,它其實就是一個模塊打包器。 html
若是像之前開發時一個html文件可能會引用十幾個js文件,並且順序還不能亂,由於它們存在依賴關係,同時對於ES6+等新的語法,less, sass等CSS預處理都不能很好的解決……,此時就須要一個處理這些問題的工具。vue
Webpack就是爲處理這些問題而生的,它就是把你的項目當成一個總體,經過一個入口主文件(如:index.js),從這個文件開始找到你的項目全部的依賴文件並處理它們,最後打包成一個(或多個)瀏覽器可識別的JavaScript文件。node
首先新建一個空文件夾,用於建立項目,在終端中進入文件夾,以下我在桌面建了一個名爲webpack-project的文件夾,使用終端進入文件夾後(若是對命令行不太熟悉,可參考個人博客:命令行不會?看這裏),使用npm init
命令建立一個package.json文件。react
npm init
複製代碼
輸入這個命令後,終端會問你一系列諸如項目名稱,項目描述,做者等信息,不過若是你不打算髮布這個模塊,直接一路回車就好。(也可使用npm init -y
這個命令來一次生成package.json文件,這樣終端不會詢問你問題)。 webpack
若是你想一步到位的話,就把全局webpack
、webpack-cli
和本地項目webpack
、webpack-cli
全都先裝了,由於後面一些模塊會用到。安裝webapck
時把webpack-cli
也裝上是由於在webpack4.x
版本後webpack模塊把一些功能分到了webpack-cli
模塊,因此二者都須要安裝,安裝方法以下:git
npm install webpack webpack-cli --global //這是安裝全局webpack及webpack-cli模塊
npm install webpack webpack-cli --save-dev //這是安裝本地項目模塊
複製代碼
上述命令可採用簡寫,
install
可簡寫爲i
,--global
可簡寫爲-g
,--save-dev
可簡寫爲-D
(這個命令是用於把配置添加到package.json的開發環境配置列表中,後面會提到),--save
可簡寫爲-S
,同時國內咱們能夠採用cnpm,配置方法可去這裏查看,這樣安裝速度會相對較快。以下:github
cnpm i webpack -g //這是安裝全局webpack命令
cnpm i webpack webpack-cli -D //這是安裝本地項目模塊
複製代碼
在webpack-project
文件夾中新建兩個文件夾,分別爲src文件夾和dist文件夾,接下來再建立三個文件:web
index.html
--放在dist文件夾中;hello.js
--放在src文件夾中;index.js
--放在src文件夾中;此時,項目結構以下:正則表達式
咱們在index.html
中寫下html
代碼,它的做用是爲了引入咱們打包後的js文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webpack Project</title>
</head>
<body>
<div id='root'></div>
<script src="bundle.js"></script> <!--這是打包以後的js文件,咱們暫時命名爲bundle.js-->
</body>
</html>
複製代碼
咱們在hello.js
中導出一個模塊:
// hello.js
module.exports = function() {
let hello = document.createElement('div');
hello.innerHTML = "Long time no see!";
return hello;
};
複製代碼
而後在index.js
中引入這個模塊(hello.js
):
//index.js
const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());
複製代碼
上述操做就至關於咱們把hello.js
模塊合併到了index.js
模塊,以後咱們打包時就只需把index.js
模塊打包成bundle.js
,而後供index.html
引用便可,這就是最簡單的webpack打包原理。
在終端中使用以下命令進行打包:
// webpack全局安裝的狀況下
webpack src/index.js --output dist/bundle.js
// --output可簡寫爲-o
複製代碼
上述就至關於把src
文件夾下的index.js
文件打包到dist
文件下的bundle.js
,這時就生成了bundle.js
供index.html
文件引用。
結果以下:
能夠看出webpack同時編譯了index.js
和hello.js
,如今打開index.html
,能夠看到以下結果:
沒錯,咱們已經成功使用webpack進行打包,原來webpack也不過如此嘛!可是,每次都在終端中輸入這麼長的命令,感受好煩啊,還好有懶人方法,讓咱們看看。
其實webpack是有不少功能的,也是很方便的,咱們能夠在當前項目的根目錄下新建一個配置文件webpack.config.js
,咱們寫下以下簡單配置代碼,目前只涉及入口配置(至關於咱們的index.js
,從它開始打包)和出口配置(至關於咱們打包生成的bundle.js
)。
// webpack.config.js
module.exports = {
entry: __dirname + "/src/index.js", // 入口文件
output: {
path: __dirname + "/dist", //打包後的文件存放的地方
filename: "bundle.js" //打包後輸出文件的文件名
}
}
複製代碼
注:
__dirname
是node.js中的一個全局變量,它指向當前執行腳本所在的目錄,即C:\Users\sjt\DeskTop\webpack-project(這是我當前的目錄)
但平時咱們看到的腳手架配置也比較喜歡採用node.js的path
模塊來處理絕對路徑,因此咱們也能夠採用以下的寫法,和上述的效果是同樣的:
// 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
的功能是拼接路徑片斷。
有了這個配置文件,咱們只需在終端中運行webpack
命令就可進行打包,這條命令會自動引用webpack.config.js文件中的配置選項,示例以下:
搞定,是否是這樣更方便了,感受沒那麼low了,但還能不能更便捷智能呢?那必須的!
咱們如今只在終端中使用webpack
命令來進行打包,要是之後在打包的同時還有更多的操做呢,那不是還得寫上更多的命令?因此咱們得想辦法把這些命令都集成起來,這時候以前的package.json
文件就派上用場了。 如今的package.json文件大概就是以下這樣:
{
"name": "webpack-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1" //咱們要修改的是這裏,JSON文件不支持註釋,引用時請清除
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.23.1",
"webpack-cli": "^3.1.2"
}
}
複製代碼
修改以下:
{
"name": "webpack-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack", //改爲這樣,注意使用時把註釋刪掉
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.23.1",
"webpack-cli": "^3.1.2"
}
}
複製代碼
注:package.json中的script會按你設置的命令名稱來執行對應的命令。
這樣咱們就能夠在終端中直接執行npm start
命令來進行打包,start
命令比較特殊,能夠直接npm
加上start
就能夠執行,若是咱們想起其餘的名稱,如build
時,就須要使用npm run
加上build
,即npm run build
命令。 如今咱們執行npm start
命令:
OK,搞定,是否是很簡單,但webpack的功能遠不止於此,下面咱們繼續。
如今咱們是經過打開本地文件來查看頁面的,看起來總感受比較low,看別人用vue,react框架時都是運行在本地服務器上的,那咱們能不能也那樣呢?那必須的!
Webpack提供了一個可選的本地開發服務器,這個本地服務器基於node.js構建,它是一個單獨的組件,在webpack中進行配置以前須要單獨安裝它做爲項目依賴:
cnpm i webpack-dev-server -D
複製代碼
devServer做爲webpack配置選項中的一項,如下是它的一些配置選項:
contentBase
:設置服務器所讀取文件的目錄,當前咱們設置爲"./dist"port
:設置端口號,若是省略,默認爲8080
inline
:設置爲true
,當源文件改變時會自動刷新頁面historyApiFallback
:設置爲true
,全部的跳轉將指向index.html
如今咱們把這些配置加到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: "./dist", // 本地服務器所加載文件的目錄
port: "8088", // 設置端口號爲8088
inline: true, // 文件修改後實時刷新
historyApiFallback: true, //不跳轉
}
}
複製代碼
咱們繼續在package.json
文件中添加啓動命令:
{
"name": "webpack-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server --open"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.23.1",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.10"
}
}
複製代碼
咱們把start
命令名稱改成了build
,這樣比較語義化,平時的腳手架也多數採用這個名稱,咱們用dev
(development的縮寫,意指開發環境)來啓動本地服務器,webpack-dev-server
就是啓動服務器的命令,--open
是用於啓動完服務器後自動打開瀏覽器,這時候咱們自定義命令方式的便捷性就體現出來了,能夠多個命令集成在一塊兒運行,即咱們定義了一個dev
命令名稱就能夠同時運行了webpack-dev-server
和--open
兩個命令。
如今在終端輸入npm run dev
運行服務器:
這樣咱們便可在http://localhost:8088/中查看頁面(退出服務器,可以使用ctrl+c
後,再按y
確認,便可退出服務器運行)
做爲開發,代碼調試固然少不了,那麼問題來了,通過打包後的文件,你是不容易找到出錯的地方的,Source Map
就是用來解決這個問題的。
經過以下配置,咱們會在打包時生成對應於打包文件的.map
文件,使得編譯後的代碼可讀性更高,更易於調試。
// 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: "./dist", // 本地服務器所加載文件的目錄
port: "8088", // 設置端口號爲8088
inline: true, // 文件修改後實時刷新
historyApiFallback: true, //不跳轉
},
devtool: 'source-map' // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
}
複製代碼
配置好後,咱們再次運行npm run build
進行打包,這時咱們會發如今dist
文件夾中多出了一個bundle.js.map
文件以下:
devtool: 'source-map'
配置項的做用。
loaders是webpack最強大的功能之一,經過不一樣的loader,webpack有能力調用外部的腳本或工具,實現對不一樣格式的文件的處理,例如把scss
轉爲css
,將ES6六、ES7等語法轉化爲當前瀏覽器能識別的語法,將JSX轉化爲js等多項功能。
Loaders須要單獨安裝而且須要在webpack.config.js
中的modules
配置項下進行配置,Loaders的配置包括如下幾方面:
test
:一個用以匹配loaders所處理文件的拓展名的正則表達式(必須)loader
:loader的名稱(必須)include/exclude
:手動添加必須處理的文件(文件夾)或屏蔽不須要處理的文件(文件夾)(可選);options
:爲loaders提供額外的設置選項(可選)
若是咱們要加載一個css文件,須要安裝配置style-loader
和css-loader
:
cnpm i 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: "./dist", // 本地服務器所加載文件的目錄
port: "8088", // 設置端口號爲8088
inline: true, // 文件修改後實時刷新
historyApiFallback: true, //不跳轉
},
devtool: 'source-map', // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
module: {
rules: [
{
test: /\.css$/, // 正則匹配以.css結尾的文件
use: ['style-loader', 'css-loader'] // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
}
]
}
}
複製代碼
咱們在src文件夾下新建css
文件夾,該文件夾內新建style.css
文件:
/* style.css */
body {
background: gray;
}
複製代碼
在index.js
中引用它:
//index.js
import './css/style.css'; //導入css
const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());
複製代碼
這時咱們運行npm run dev
,會發現頁面背景變成了灰色。
若是是要編譯sass文件呢?
cnpm i sass-loader node-sass -D // 由於sass-loader依賴於node-sass,因此還要安裝node-sass
複製代碼
增長sass的rules:
// 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: "./dist", // 本地服務器所加載文件的目錄
port: "8088", // 設置端口號爲8088
inline: true, // 文件修改後實時刷新
historyApiFallback: true, //不跳轉
},
devtool: 'source-map', // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
module: {
rules: [
{
test: /\.css$/, // 正則匹配以.css結尾的文件
use: ['style-loader', 'css-loader'] // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
},
{
test: /\.(scss|sass)$/, // 正則匹配以.scss和.sass結尾的文件
use: ['style-loader', 'css-loader', 'sass-loader'] // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
}
]
}
}
複製代碼
在css文件夾中新建blue.scss
文件:
/* blue.scss */
$blue: blue;
body{
color: $blue;
}
複製代碼
在index.js
中引入blue.scss
:
//index.js
import './css/style.css'; // 導入css
import './css/blue.scss'; // 導入scss
const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());
複製代碼
這時npm run dev
從新啓動服務器,應該會出現以下結果:
還有諸如圖片loader、字體loader等就不一一列出來了,感興趣的可前往webpack官網查看,都是同樣的套路。
Babel實際上是一個編譯JavaScript的平臺,它能夠編譯代碼幫你達到如下目的:
Babel實際上是幾個模塊化的包,其核心功能位於稱爲babel-core
的npm包中,webpack能夠把其不一樣的包整合在一塊兒使用,對於每個你須要的功能或拓展,你都須要安裝單獨的包(用得最多的是解析ES6的babel-preset-env
包和解析JSX的babel-preset-react
包)。
cnpm i babel-core babel-loader babel-preset-env babel-preset-react -D
// babel-preset-env的env表示是對當前環境的預處理,而不是像之前使用babel-preset-es2015只能針對某個環境
複製代碼
// 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: "./dist", // 本地服務器所加載文件的目錄
port: "8088", // 設置端口號爲8088
inline: true, // 文件修改後實時刷新
historyApiFallback: true, //不跳轉
},
devtool: 'source-map', // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
module: {
rules: [
{
test: /\.css$/, // 正則匹配以.css結尾的文件
use: ['style-loader', 'css-loader'] // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
},
{
test: /\.(scss|sass)$/, // 正則匹配以.scss和.sass結尾的文件
use: ['style-loader', 'css-loader', 'sass-loader'] // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
},
{ // jsx配置
test: /(\.jsx|\.js)$/,
use: { // 注意use選擇若是有多項配置,可寫成這種對象形式
loader: "babel-loader",
options: {
presets: [
"env", "react"
]
}
},
exclude: /node_modules/
}
]
}
}
複製代碼
如今咱們已經能夠支持ES6及JSX的語法了,咱們用react來試試,但使用react還得先安裝兩個模塊react
和react-dom
。
cnpm i react react-dom -S
複製代碼
接下來咱們把hello.js
文件修改一下:
// hello.js
import React, {Component} from 'react'; // 這兩個模塊必須引入
let name = 'Alan';
export default class Hello extends Component{
render() {
return (
<div> {name} </div>
);
}
}
複製代碼
修改index.js
文件:
//index.js
import './css/style.css'; // 導入css
import './css/blue.scss'; // 導入scss
import React from 'react';
import {render} from 'react-dom';
import Hello from './hello'; // 可省略.js後綴名
render(<Hello />, document.getElementById('root')); 複製代碼
此時運行npm run dev
後你可能會發現以下結果:
這是由於官方默認babel-loader | babel
對應的版本須要一致: 即babel-loader
須要搭配最新版本babel
,詳細可參考這篇博客。
兩種解決方案:
cnpm i babel-loader@7 babel-core babel-preset-env -D
複製代碼
cnpm i babel-loader @babel/core @babel/preset-env webpack -D
複製代碼
我這裏採起的是第一個方案,回退後,再此運行npm run dev
,獲得以下結果:
到這裏了是否是感受很爽,不就是配置嘛,想要使用什麼就配置什麼。
雖然babel徹底能夠在webpack.config.js
中進行配置,但如今不是都提倡模塊化嘛,也許以後babel膨脹了,增長了更多的配置項呢? 那咱們不如把它提取出來,把它放到根目錄下的.babelrc
文件下(webpack會自動調用.babelrc
裏的babel配置選項)。
咱們在項目根目錄下新建.babelrc
文件:
// 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: "./dist", // 本地服務器所加載文件的目錄
port: "8088", // 設置端口號爲8088
inline: true, // 文件修改後實時刷新
historyApiFallback: true, //不跳轉
},
devtool: 'source-map', // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
module: {
rules: [
{
test: /\.css$/, // 正則匹配以.css結尾的文件
use: ['style-loader', 'css-loader'] // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
},
{
test: /\.(scss|sass)$/, // 正則匹配以.scss和.sass結尾的文件
use: ['style-loader', 'css-loader', 'sass-loader'] // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
},
{ // jsx配置
test: /(\.jsx|\.js)$/,
use: { // 注意use選擇若是有多項配置,可寫成這種對象形式
loader: "babel-loader"
},
exclude: /node_modules/ // 排除匹配node_modules模塊
}
]
}
}
複製代碼
// .babelrc 使用時把註釋刪掉,該文件不能添加註釋
{
"presets": ["env", "react"]
}
複製代碼
此時不出問題的話應該一切運行正常,接下來讓咱們進入強大的插件模塊。
插件(Plugins)是用來拓展Webpack功能的,它們會在整個構建過程當中生效,執行相關的任務。 Loaders和Plugins經常被弄混,可是他們實際上是徹底不一樣的東西,能夠這麼來講,loaders是在打包構建過程當中用來處理源文件的(JSX,Scss,Less..),一次處理一個,插件並不直接操做單個文件,它直接對整個構建過程其做用。
使用某個插件,須要經過npm
進行安裝,而後在webpack.config.js
配置文件的plugins
(是一個數組)配置項中添加該插件的實例,下面咱們先來使用一個簡單的版權聲明插件。
// webpack.config.js
const webpack = require('webpack'); // 這個插件不須要安裝,是基於webpack的,須要引入webpack模塊
module.exports = {
...
module: {
rules: [
{
test: /\.css$/, // 正則匹配以.css結尾的文件
use: ['style-loader', 'css-loader'] // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
},
{
test: /\.(scss|sass)$/, // 正則匹配以.scss和.sass結尾的文件
use: ['style-loader', 'css-loader', 'sass-loader'] // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
},
{ // jsx配置
test: /(\.jsx|\.js)$/,
use: { // 注意use選擇若是有多項配置,可寫成這種對象形式
loader: "babel-loader"
},
exclude: /node_modules/ // 排除匹配node_modules模塊
}
]
},
plugins: [
new webpack.BannerPlugin('版權全部,翻版必究') // new一個插件的實例
]
}
複製代碼
運行npm run build
打包後咱們看到bundle.js
文件顯示以下:
到目前爲止咱們都是使用一開始建好的index.html
文件,並且也是手動引入bundle.js
,要是之後咱們引入不止一個js文件,並且更改js文件名的話,也得手動更改index.html
中的js文件名,因此能不能自動生成index.html
且自動引用打包後的js呢?HtmlWebpackPlugin
插件就是用來解決這個問題的:
首先安裝該插件
cnpm i html-webpack-plugin -D
複製代碼
而後咱們對項目結構進行一些更改:
dist
整個文件夾刪除;src
文件夾下新建一個index.template.html
(名稱自定義)文件模板(固然這個是可選的,由於就算不設置模板,HtmlWebpackPlugin
插件也會生成默認html
文件,這裏咱們設置模塊會讓咱們的開發更加靈活),以下:<!-- index.template.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Here is Template</title>
</head>
<body>
<div id='root'>
</div>
</body>
</html>
複製代碼
webpack.config.js
中咱們引入了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: "./dist", // 本地服務器所加載文件的目錄
port: "8088", // 設置端口號爲8088
inline: true, // 文件修改後實時刷新
historyApiFallback: true, //不跳轉
},
devtool: 'source-map', // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
module: {
rules: [
{
test: /\.css$/, // 正則匹配以.css結尾的文件
use: ['style-loader', 'css-loader'] // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
},
{
test: /\.(scss|sass)$/, // 正則匹配以.scss和.sass結尾的文件
use: ['style-loader', 'css-loader', 'sass-loader'] // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
},
{ // jsx配置
test: /(\.jsx|\.js)$/,
use: { // 注意use選擇若是有多項配置,可寫成這種對象形式
loader: "babel-loader"
},
exclude: /node_modules/ // 排除匹配node_modules模塊
}
]
},
plugins: [
new webpack.BannerPlugin('版權全部,翻版必究'), // new一個插件的實例
new HtmlWebpackPlugin({
template: path.join(__dirname, "/src/index.template.html")// new一個這個插件的實例,並傳入相關的參數
})
]
}
複製代碼
而後咱們使用npm run build
進行打包,你會發現,dist
文件夾和html
文件都會自動生成,以下:
爲何會自動生成dist
文件夾呢?由於咱們在output
出口配置項中定義了出口文件所在的位置爲dist
文件夾,且出口文件名爲bundle.js
,因此HtmlWebpackPlugin
會自動幫你在index.html
中引用名爲bundle.js
文件,若是你在webpack.config.js
文件中更改了出口文件名,index.html
中也會自動更改該文件名,這樣之後修改起來是否是方便多了?
/dist
文件夾(CleanWebpackPlugin)你可能已經注意到,在咱們刪掉/dist
文件夾以前,因爲前面的代碼示例遺留,致使咱們的/dist
文件夾比較雜亂。webpack
會生成文件,而後將這些文件放置在/dist
文件夾中,可是webpack
沒法追蹤到哪些文件是實際在項目中用到的。
一般,在每次構建前清理/dist
文件夾,是比較推薦的作法,所以只會生成用到的文件,這時候就用到CleanWebpackPlugin
插件了。
cnpm i clean-webpack-plugin -D
複製代碼
// webpack.config.js
...
const CleanWebpackPlugin = require('clean-webpack-plugin'); // 引入CleanWebpackPlugin插件
module.exports = {
...
plugins: [
new webpack.BannerPlugin('版權全部,翻版必究'), // new一個插件的實例
new HtmlWebpackPlugin({
template: path.join(__dirname, "/src/index.template.html")// new一個這個插件的實例,並傳入相關的參數
}),
new CleanWebpackPlugin(['dist']), // 所要清理的文件夾名稱
]
}
複製代碼
插件的使用方法都是同樣的,首先引入,而後new一個實例,實例可傳入參數。
如今咱們運行npm run build
後就會發現,webpack會先將/dist
文件夾刪除,而後再生產新的/dist
文件夾。
HotModuleReplacementPlugin
(HMR)是一個很實用的插件,能夠在咱們修改代碼後自動刷新預覽效果。 方法:
devServer
配置項中添加hot: true
參數。HotModuleReplacementPlugin
是webpack
模塊自帶的,因此引入webpack
後,在plugins
配置項中直接使用便可。// webpack.config.js
...
const webpack = require('webpack'); // 這個插件不須要安裝,是基於webpack的,須要引入webpack模塊
module.exports = {
...
devServer: {
contentBase: "./dist", // 本地服務器所加載文件的目錄
port: "8088", // 設置端口號爲8088
inline: true, // 文件修改後實時刷新
historyApiFallback: true, //不跳轉
hot: true // 熱更新
},
...
plugins: [
new webpack.BannerPlugin('版權全部,翻版必究'), // new一個插件的實例
new HtmlWebpackPlugin({
template: path.join(__dirname, "/src/index.template.html")// new一個這個插件的實例,並傳入相關的參數
}),
new CleanWebpackPlugin(['dist']), // 傳入所要清理的文件夾名稱
new webpack.HotModuleReplacementPlugin() // 熱更新插件
]
}
複製代碼
此時咱們從新啓動項目npm run dev
後,修改hello.js
的內容,會發現瀏覽器預覽效果會自動刷新(也許反應會比較慢,由於咱們使用了source-map
和其餘配置的影響,後面代碼分離的時候咱們再處理)。
在當前的開發環境都是提倡模塊化,webpack天然不例外,咱們前面的webpack.config.js
配置文件,其實也沒配置多少東西就這麼多了,要是之後增長了更多配置,豈不是看得眼花繚亂,因此最好的方法就是把它拆分,方便管理:
1. 咱們在根目錄下新建三個文件,分別爲webpack.common.js
、webpack.dev.js
、webpack.prod.js
,分別表明公共配置文件、開發環境配置文件、生產環境(指項目上線時的環境)配置文件。
2. 安裝一個合併模塊插件:
cnpm i 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結尾的文件
use: ['style-loader', 'css-loader'] // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
},
{
test: /\.(scss|sass)$/, // 正則匹配以.scss和.sass結尾的文件
use: ['style-loader', 'css-loader', 'sass-loader'] // 須要用的loader,必定是這個順序,由於調用loader是從右往左編譯的
},
{ // jsx配置
test: /(\.jsx|\.js)$/,
use: { // 注意use選擇若是有多項配置,可寫成這種對象形式
loader: "babel-loader"
},
exclude: /node_modules/ // 排除匹配node_modules模塊
}
]
},
plugins: [
new webpack.BannerPlugin('版權全部,翻版必究'), // new一個插件的實例
new HtmlWebpackPlugin({
template: path.join(__dirname, "/src/index.template.html")// new一個這個插件的實例,並傳入相關的參數
}),
new webpack.HotModuleReplacementPlugin()
]
}
複製代碼
// webpack.dev.js
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: "./dist", // 本地服務器所加載文件的目錄
port: "8088", // 設置端口號爲8088
inline: true, // 文件修改後實時刷新
historyApiFallback: true, //不跳轉
hot: 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', // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
plugins: [
new CleanWebpackPlugin(['dist']), // 所要清理的文件夾名稱
]
})
複製代碼
此時咱們的項目目錄以下:
4. 設置package.json
的scripts
命令:
{
"name": "webpack-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack --config webpack.prod.js",
"dev": "webpack-dev-server --open --config webpack.dev.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"clean-webpack-plugin": "^0.1.19",
"css-loader": "^1.0.0",
"html-webpack-plugin": "^3.2.0",
"node-sass": "^4.9.4",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"webpack": "^4.23.1",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.10",
"webpack-merge": "^4.1.4"
},
"dependencies":{
"react": "^16.6.0",
"react-dom": "^16.6.0"
}
}
複製代碼
咱們把build
命令改成了webpack --config webpack.prod.js
,意思是把打包配置指向webpack.prod.js
配置文件,而以前咱們只須要使用一個webpack
命令爲何就能夠運行了?由於webpack
命令是默認指向webpack.config.js
這個文件名稱了,如今咱們把文件名稱改了,因此就須要自定義指向新的文件,dev
命令中的指令也同理。
而後咱們運行npm run build
和npm run dev
,效果應該和咱們分離代碼前是同樣的。
注:說道
package.json
文件,順便就多提幾句,由於也許有些朋友可能對咱們安裝模塊時加的-D
、-S
或-g
命令存在一些疑惑,由於不知道何時加什麼尾綴。 其實這個package.json
文件是用於咱們安裝依賴的,能夠把它當成一份依賴安裝說明表,就是若是咱們把項目上傳或者發給其餘的開發同事,確定不會把/node_modules
文件夾也發送過去,由於這太大了,不現實也不必。 開發同事只須要有這份package.json
文件,而後npm install
就能夠把咱們所須要的依賴都安裝下來,但前提是package.json
文件上有記錄,這就是安裝模塊時加上-D
,-S
命令的緣由。-D
的全稱是--save-dev
指開發環境時須要用到的依賴,會記錄在package.json
文件中的devDependencies
選項中,而-S
是--save
是指生產環境也就是上線環境中須要用到的依賴,會記錄在package.json
文件中的dependencies
選項中,-g
的全稱是--global
指安裝全局命令,就是咱們在本電腦的任何項目中都能使用到的命令,好比安裝cnpm
這個淘寶鏡像命令就會用到-g
命令。 因此咱們在安裝模塊時必定不要忘了加上對應的尾綴命令,讓咱們的模塊有跡可循,不然其餘的開發同事接手你的項目的話,會不會下班後(放學後)在門口等你就不知道了。
扯遠了,但願不要嫌棄,也是想講得更詳細嘛!
到目前爲止咱們都是一個入口文件和一個出口文件,要是我不止一個入口文件呢?下面咱們來試試:
在webpack.common.js
中的entry
入口有三種寫法,分別爲字符串、數組和對象,平時咱們用得比較多的是對象,因此咱們把它改成對象的寫法,首先咱們在src
文件夾下新建two.js
文件,名稱任意。由於有多個入口,因此確定得多個出口來進行一一對應了,因此entry
和output
配置以下:
// webpack.common.js
...
module.exports = {
entry: {
index: path.join(__dirname, "/src/index.js"),
two: path.join(__dirname, "/src/two.js")
},
output: {
path: path.join( __dirname, "/dist"), //打包後的文件存放的地方
filename: "[name].js" //打包後輸出文件的文件名
},
...
}
複製代碼
// two.js
function two() {
let element = document.createElement('div');
element.innerHTML = '我是第二個入口文件';
return element;
}
document.getElementById('root').appendChild(two());
複製代碼
而後咱們運行npm run build
打包後發現/dist
文件夾下會多出two.js
文件,同時index.html
也會自動將two.js
引入,而後咱們運行npm run dev
顯示以下:
1.增長css前綴
平時咱們寫css時,一些屬性須要手動加上前綴,好比-webkit-border-radius: 10px;
,在webpack中咱們能不能讓它自動加上呢?那是必須的,首先確定得安裝模塊了:
cnpm i postcss-loader autoprefixer -D
複製代碼
安裝好這兩個模塊後,在項目根目錄下新建postcss.config.js
文件:
// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer') // 引用autoprefixer模塊
]
}
複製代碼
在style.css
中增長如下樣式:
/* style.css */
body {
background: #999;
}
#root div{
width: 200px;
margin-top: 50px;
transform: rotate(45deg); /* 這個屬性會產生前綴 */
}
複製代碼
修改webpack.common.js
文件中的css-loader
配置:
...
module.exports = {
...
module: {
rules: [
{
test: /\.css$/, // 正則匹配以.css結尾的文件
use: [
{loader: 'style-loader'}, // 這裏採用的是對象配置loader的寫法
{loader: 'css-loader'},
{loader: 'postcss-loader'} // 使用postcss-loader
]
},
...
]
},
...
}
複製代碼
而後咱們運行npm run dev
後css樣式中會自動添加前綴,顯示以下:
雖然webpack的理念是把css、js全都打包到一個文件裏,但要是咱們想把css分離出來該怎麼作呢?
cnpm i extract-text-webpack-plugin@next -D // 加上@next是爲了安裝最新的,不然會出錯
複製代碼
安裝完以上插件後在webpack.common.js
文件中引入並使用該插件:
// webpack.common.js
...
const ExtractTextPlugin = require('extract-text-webpack-plugin') //引入分離插件
module.exports = {
...
module: {
rules: [
{
test: /\.css$/, // 正則匹配以.css結尾的文件
use: ExtractTextPlugin.extract({ // 這裏咱們須要調用分離插件內的extract方法
fallback: 'style-loader', // 至關於回滾,經postcss-loader和css-loader處理過的css最終再通過style-loader處理
use: ['css-loader', 'postcss-loader']
})
},
...
]
},
plugins: [
...
new ExtractTextPlugin('css/index.css') // 將css分離到/dist文件夾下的css文件夾中的index.css
]
}
複製代碼
運行npm run build
後會發現/dist
文件夾內多出了/css
文件夾及index.css
文件。
經讀友提示(感謝熱心讀友):如今
webpack4.x
版本分離css方法可採用mini-css-extract-plugin
插件,具體使用方式可參考這裏,套路同樣的哈。
3.消除冗餘css
有時候咱們css寫得多了,可能會不自覺的寫重複了一些樣式,這就形成了多餘的代碼,上線前又忘了檢查,對於這方面,咱們應該儘可能去優化它,webpack就有這個功能。
cnpm i purifycss-webpack purify-css glob -D
安裝完上述三個模塊後,由於正常來講是在生產環境中優化代碼,因此咱們應該是在webpack.prod.js
文件中進行配置,引入clean-webpack-plugin
及glob
插件並使用它們:
// webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const CleanWebpackPlugin = require('clean-webpack-plugin'); // 引入CleanWebpackPlugin插件
const path = require('path');
const PurifyCssWebpack = require('purifycss-webpack'); // 引入PurifyCssWebpack插件
const glob = require('glob'); // 引入glob模塊,用於掃描所有html文件中所引用的css
module.exports = merge(common, { // 將webpack.common.js合併到當前文件
devtool: 'source-map', // 會生成對於調試的完整的.map文件,但同時也會減慢打包速度
plugins: [
new CleanWebpackPlugin(['dist']), // 所要清理的文件夾名稱
new PurifyCssWebpack({
paths: glob.sync(path.join(__dirname, 'src/*.html')) // 同步掃描全部html文件中所引用的css
})
]
})
複製代碼
咱們在style.css
文件中增長一些多餘的代碼試試:
/* style.css */
body {
background: #999;
}
#root div{
width: 200px;
margin-top: 50px;
transform: rotate(45deg); /* 這個屬性會產生前綴 */
}
.a{ /* 冗餘css */
color: black;
}
.b{ /* 冗餘css */
width: 50px;
height: 50px;
background: yellow;
}
複製代碼
而後咱們運行npm run build
後發現打包後的index.css
中是沒有多餘的.a
和.b
代碼的:
/* index.css */
body {
background: #999;
}
#root div {
width: 200px;
margin-top: 50px;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
/* 這個屬性會產生前綴 */
}
/*# sourceMappingURL=index.css.map*/
複製代碼
4.處理圖片
到目前爲止咱們還沒講到圖片的問題,若是要使用圖片,咱們得安裝兩個loader:
// 雖然咱們只需使用url-loader,但url-loader是依賴於file-loader的,因此也要安裝
cnpm i url-loader file-loader -D
複製代碼
而後在webpack.common.js
中配置url-loader
:
// webpack.common.js
...
module.exports = {
...
module: {
rules: [
{
test: /\.css$/, // 正則匹配以.css結尾的文件
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'postcss-loader']
})
},
{
test: /\.(png|jpg|svg|gif)$/, // 正則匹配圖片格式名
use: [
{
loader: 'url-loader' // 使用url-loader
}
]
},
...
]
},
...
}
複製代碼
咱們修改一下style.css
,把背景改成圖片背景:
/* style.css */
body {
background: url(../images/coffee.png) top right repeat-y; /* 設爲圖片背景 */
}
#root div{
width: 200px;
margin-top: 50px;
transform: rotate(45deg); /* 這個屬性會產生前綴 */
}
.a{
color: black;
}
.b{
width: 50px;
height: 50px;
background: yellow;
}
複製代碼
運行npm run dev
後顯示以下:
可是背景圖片變成了base64
,由於webpack會自動優化圖片,減小發送請求,可是若是我想把它變成路徑的該怎麼作?
咱們能夠把webpack.common.js
的loader配置更改一下,增長options
選項:
// webpack.common.js
...
module.exports = {
...
module: {
rules: [
{
test: /\.css$/, // 正則匹配以.css結尾的文件
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'postcss-loader']
})
},
{
test: /\.(png|jpg|svg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 1000 // 限制只有小於1kb的圖片才轉爲base64,例子圖片爲1.47kb,因此不會被轉化
}
}
]
},
...
]
},
...
}
複製代碼
而後咱們運行npm run build
後,再運行npm run dev
,額,圖片是沒有轉成base64了,可是圖片怎麼不顯示了?
問題就出在路徑上,咱們以前圖片的路徑是在../images
文件夾下,可是打包出來後沒有這個路徑了,圖片直接和文件同級了,因此咱們須要在webpack.common.js
中給它設置一個文件夾:
// webpack.common.js
...
module.exports = {
...
module: {
rules: [
...
{
test: /\.(png|jpg|svg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 1000, // 限制只有小於1kb的圖片才轉爲base64,例子圖片爲1.47kb,因此不會被轉化
outputPath: 'images' // 設置打包後圖片存放的文件夾名稱
}
}
]
},
...
]
},
...
}
複製代碼
繼續npm run build
打包再npm run dev
運行,個人天!圖片仍是不顯示! 調試工具上看圖片路徑有images
文件夾了,可是個人../
呢?
這又涉及到配置路徑的問題上了,咱們還須要在css-loader中給背景圖片設置一個公共路徑publicPath: '../'
,以下:
// webpack.common.js
...
module.exports = {
...
module: {
rules: [
{
test: /\.css$/, // 正則匹配以.css結尾的文件
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'postcss-loader'],
publicPath: '../' // 給背景圖片設置一個公共路徑
})
},
{
test: /\.(png|jpg|svg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 1000, // 限制只有小於1kb的圖片才轉爲base64,例子圖片爲1.47kb,因此不會被轉化
outputPath: 'images' // 設置打包後圖片存放的文件夾名稱
}
}
]
},
...
]
},
...
}
複製代碼
如今再npm run build
打包再npm run dev
啓動,OK!沒毛病!
是否是很熱鬧?到如今咱們不知不覺中也同時解決了圖片分離的問題,偷偷高興一下吧!
在webpack4.x版本中當你打包時會自動把js壓縮了,並且npm run dev
運行服務器時,當你修改代碼時,熱更新很慢,這是由於你修改後webpack又自動爲你打包,這就致使了在開發環境中效率很慢,因此咱們須要把開發環境和生產環境區分開來,這時就體現出咱們代碼分離的便捷性了,webpack.dev.js
表明開發環境的配置,webpack.prod.js
表明生產環境的配置,這時咱們只要在package.json
文件中配置對應環境的命令便可:
{
...
"scripts": {
"build": "webpack --config webpack.prod.js --mode production",
"dev": "webpack-dev-server --open --config webpack.dev.js --mode development"
},
...
}
}
複製代碼
--mode production
表示打包時是生產環境,會自行將js進行壓縮,而--mode development
表示當前是開發環境,不須要進行壓縮。這同時也解決了以前一直遺留的警告問題:
好了,到如今咱們基本把webapck經常使用的功能都走了一遍,寫得有點長,感謝你能仔細的看到這裏,但願能對你有所幫助,若是有發現不對的地方,也請多多指教。其實webpack還有不少功能,這裏也沒講述徹底,但相信你如今對webpack也有了必定的瞭解,更多的webpack探索必定難不倒你!喜歡的話就關注一波吧,後續會持續更新……
完整代碼請前往github,若是能有所幫助,但願能給個star(偷笑)