在使用webpack
以前咱們要安裝webpack
,安裝命令以下:css
# 建立webpack-demo文件夾
mkdir webpack-demo
# 生成package.json文件,對項目依賴進行管理
yarn init -y
# 安裝webpack相關依賴
yarn add webpack webpack-cli -D
複製代碼
這裏咱們安裝webpack-cli
的緣由是由於它可讓咱們能夠在命令行中運行webpack
,不然webpack
命令將沒法運行html
接下來咱們創建src
目錄來管理咱們的源代碼,並創建main.js
文件,寫入第一行代碼:vue
console.log('Hello webpack!')
複製代碼
接下來咱們在項目根目錄創建webpack
的配置文件webpack.config.js
並進行最簡單的配置:node
const path = require('path');
module.exports = {
mode: 'development',
entry: {
main: './src/main.js'
},
output: {
path: path.resolve(__dirname, './dist'),
filename: 'main.js'
}
}
複製代碼
以後咱們經過package.json
文件中的script
命令來爲webpack
添加快捷命令:
react
配置好後經過命令行執行:yarn build
,會發現根目錄出現了dist
目錄,裏邊有咱們的代碼main.js
。webpack
接下來咱們在根目錄新建index.html
,經過script
標籤將打包後的代碼引入並在瀏覽器中打開:
es6
或者能夠經過node
命令來運行打包文件:
web
到這裏咱們成功打包好了咱們的第一個文件,感受仍是有些小激動的。chrome
在配置以前,咱們先簡單理解下入口和出口的概念:npm
entry
): 整個項目的起始模塊,用來做爲構建其內部依賴圖的開始output
): 告訴webpack
打包後的文件所存放的目錄這裏是一個打包截圖:
瞭解了基本概念以後,咱們來看一下經常使用的相關配置:
entry: {
main: './src/main.js' // 入口文件對應的源代碼位置,key值爲打包生成後的chunkNames
},
output: {
path: path.resolve(__dirname, './dist'), // 打包生成文件存放的位置
// 使用每次構建過程當中,惟一的hash生成
filename: '[name]_[hash].js', // 每一個打包輸出文件的名稱
// publicPath: 'https://cdn.example.com/assets/', // 會在引入的資源前加入該路徑,例:將資源託管到cnd
}
複製代碼
在配置文件中咱們使用了[name]
這種符號,這是webpack
中的佔位符(placeholder
),這裏介紹下配置中使用到的,若是小夥伴們感興趣能夠本身查閱官方文檔:
[name]
: 模塊名稱[hash]
: 模塊標識符的hash
(每次打包都會生成對應惟一的hash
值)在webpack
中,咱們能夠經過mode
選項,來分別爲生成環境打包和開發環境打包使用相應的內置優化:
mode: 'development'
複製代碼
目前咱們的配置是開發環境: development
,當咱們使用production
的時候,webpack
會將我node
中的全局變量改成: process.env.NODE_ENV
的值設置爲production
,而且也會啓用代碼壓縮等功能,有助於幫咱們減小代碼體積,提高用戶體驗。
而當咱們使用development
模式時,webpack
會將process.env.NODE_ENV
的值設置爲development
,並取消代碼壓縮等功能,提高開發體驗
在webpack
中,咱們可使用各類各樣的插件來自定義webpack
構建過程,方便咱們的開發。
這裏咱們介紹2個經常使用的plugin
:
HtmlWebpackPlugin
: 自動建立一個html
文件來幫咱們引入打包文件,這對咱們每次打包都經過hash
值來生成不一樣的打包文件的狀況特別有用CleanWebpackPlugin
: 在打包以前刪除output.path
指定的位置中的文件,保證每次打包都是最新的文件首先咱們來安裝這倆個插件:
yarn add --dev html-webpack-plugin clean-webpack-plugin
複製代碼
接下來咱們再webpack
中使用這2個插件:
// 引入插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html', // 生成html文件的文件名
template: './index.html' // 使用的html模板
}),
new CleanWebpackPlugin()
]
複製代碼
這裏咱們經過爲html-webpack-plugin
來指定根目錄下的index.html
爲模版,生成打包後的index.html
。新生成的index.html
文件與模板文件不一樣之處在於會自動引入打包生成的文件(包括js
文件、css
文件、以及以後配置的dll
文件),這樣即便咱們使用了[hash]
等佔位符,插件也會自動幫咱們動態引入資源,而不用咱們每次手工配置。
使用HtmlWebpackPlugin
指定template
的緣由是由於咱們能夠在模板文件中本身寫一些咱們本身的代碼,好比引入一些css
或者執行一段js
腳本亦或者咱們能夠在模板文件中指定項目的根元素:
<!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>
<!-- 在模板中指定根元素,方便開發時爲其中插入元素 -->
<div id="root"></div>
</body>
</html>
複製代碼
經過使用插件咱們目前解決了以下問題:
loader
下面是對webpack
官網中loader
介紹的一步分摘抄:
loader
用於對模塊的源代碼進行轉換
特性(這裏只列出了部分):
loader
支持鏈式傳遞,鏈中每一個loader
會將轉換應用在已處理過的資源上。一組鏈式的loader
將按照相反的順序執行。鏈中的第一個loader
將其結果(也就是轉換後的資源)傳遞給下一個loader
,依次類推loader
能夠經過options
對象配置- 插件能夠爲
loader
帶來更多特性
我我的以爲其實loader
就是讓webpack
能夠識別各類資源,而後將資源加工處理成瀏覽器能夠識別的、兼容性更好的、性能更好的代碼。
接下來咱們學習如何經過loader
來讓webpack
處理各類資源。
css
要想使用css
文件,咱們首先須要安裝
style-loader和
css-loader`:
yarn add style-loader css-loader -D
複製代碼
在webpack.config.js
進行配置:
module: {
rules: [
{
test: /.css$/,
use: ['style-loader', 'css-loader']
}
]
}
複製代碼
可是平常的項目中,有不少css
屬性須要添加瀏覽器供應商前綴來確保兼容性, 咱們可使用postcss-loader
結合autoprefixer
來實現:
yarn add postcss-loader autoprefixer -D
複製代碼
咱們須要爲css
文件添加postcss-loader
,以後在根目錄新建postcss.config.js
配置autoprefixer
:
// webpack.config.js
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}
]
// postcss.config.js
module.exports = {
plugins: [
// autoprefixer: parse CSS and add vendor prefixes to CSS rule using values from can I use
// 解析CSS並使用Can I use 中的值 將供應商前綴添加到css規則中
require('autoprefixer')
]
};
複製代碼
日常咱們也會用到css
預處理器來方便開發,這裏咱們以sass
爲例:
yarn add sass-loader node-sass -D
複製代碼
在webpack
中添加以下配置:
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'sass-loader'
]
}
複製代碼
這裏要特別注意loader
的順序問題(反向:從下到上,從右到左):
sass-loader
將scss
文件中的語法解析爲css
語法postcss-loader
爲對應的css
屬性添加瀏覽器供應商前綴css-loader
根據import
語法將全部的css
文件整合到一塊兒css-loader
整合的css
文件經過style
標籤插入到html
中,實現樣式的更改若是順序書寫錯誤,會致使程序沒法正常運行
最終效果以下:
學習了上面的知識之後,咱們再瞭解幾個css-loader
的經常使用配置:
{
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
// 開啓css模塊化
modules: true,
// 在css-loader前應用的loader的數量:確保在使用import語法前先通過sass-loader和postcss-loader的處理
importLoaders: 2
}
},
'postcss-loader',
'sass-loader'
]
}
複製代碼
要想在項目中使用圖片和字體圖標,咱們須要使用到file-loader
:
yarn add file-loader -D
複製代碼
須要在webpack.config.js
中添加以下配置:
{
test: /\.(png|svg|jpg|gif)$/,
use: ['file-loader']
}
複製代碼
配置成功後webpack
會將咱們引入的資源轉換爲路徑字符串,webpack
會經過字符串幫咱們找到文件的位置。
接下來咱們嘗試一下file-loader
的幾個配置項:
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
// placeholders:
// [ext]: 資源擴展名
// [name]: 資源的基本名稱
// [hash]: 內容hash值
// [path]: 資源相對於context的路徑
// 默認值: [hash].[ext]
name: '[name]_[hash:8].[ext]',
// 打包文件存放到出口目錄下的images文件中
outputPath: 'images/'
}
}
]
}
複製代碼
打包文件以下:
這裏也可使用url-loader
來處理靜態資源,url-loader
的工做方式和file-loader
相同,可是它會在文件小於限制大小(單位byte
)的時候,返回base64
字符串,當大於限制大小時會返回和file-loader
同樣的地址字符串。
這在處理一些小文件的時候不用再進行資源請求,能夠提升性能
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
// placeholders:
// [ext]: 資源擴展名,默認file.extname
// [name]: 資源的基本名稱,默認file.basename
// [hash]: 內容hash值,默認md5
// [path]: 資源相對於context的路徑,默認值file.dirname
// 默認值: [hash].[ext]
name: '[name]_[hash:8].[ext]',
// 打包文件存放到出口目錄下的images文件中
outputPath: 'images/',
limit: 8192, // 單位byte,文件小於8kb時返回base64文件,大於這個限制會返回地址
}
}
]
}
複製代碼
咱們能夠看一下打包區別:
source map
配置當咱們使用webpack
進行代碼打包之後,若是代碼出現錯誤,會很難追蹤到錯誤和警告在源代碼中的原始位置。爲了更容易地追蹤錯誤和警告,JavaScript
提供了SourceMap
功能,能夠將編譯後的代碼映射回原始源代碼。
webpack
中配置以下:
// 此選項控制是否生成,以及如何生成source map
devtool: 'none',
複製代碼
可選項以下:
這裏簡單介紹下各配置項中單詞的一些含義,方便理解:
cheap
: 不會生成列映射,只會映射行數eval
: 每一個模塊都使用eval
執行,而且都有//@sourceURL
module
:會處理來自loader
的source map
inline
: source map
轉換爲DataUrl
後添加到bundle
中平常的開發中咱們會進行以下配置:
cheap-module-eval-source-map
cheap-module-source-map
(生產環境使用source map
,方便定位錯誤)在開發環境和生產環境中,咱們要用到的webpack
配置是不一樣的,這裏咱們在根目錄下分別新建webpack.dev.js
和webpack.prod.js
來分別存放開發環境和生產環境所須要的配置,經過原有的webpack.config.js
來存放公共的配置文件。
接下來咱們使用webpack-merge
來進行配置文件的合併工做:
yarn add webpack-merge -D
複製代碼
爲了方便統一管理,咱們根目錄下新建config
文件夾,而後將配置文件放到裏邊,如今的配置文件是這樣的:
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const absPath = (dir) => path.resolve(__dirname, dir)
module.exports = {
entry: {
main: absPath('../src/main.js') // 入口文件對應的源代碼位置,key值爲打包生成後的chunkNames
},
output: {
path: absPath('../dist'), // 打包生成文件存放的位置
// 使用每次構建過程當中,惟一的hash生成
filename: '[name]_[hash].js', // 每一個打包輸出文件的名稱
// publicPath: 'https://cdn.example.com/assets/', // 會在引入的資源前加入該路徑,例:將資源託管到cnd
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
]
},
{
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
// 開啓css模塊化
modules: true,
// 在css-loader前應用的loader的數量:確保在使用import語法前先通過sass-loader和postcss-loader的處理
importLoaders: 2
}
},
'postcss-loader',
'sass-loader'
]
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
// placeholders:
// [ext]: 資源擴展名,默認file.extname
// [name]: 資源的基本名稱,默認file.basename
// [hash]: 內容hash值,默認md5
// [path]: 資源相對於context的路徑,默認值file.dirname
// 默認值: [hash].[ext]
name: '[name]_[hash:8].[ext]',
// 打包文件存放到出口目錄下的images文件中
outputPath: 'images/',
limit: 8192, // 單位byte,文件小於8kb時返回base64文件,大於這個限制會返回地址
}
}
]
}
]
},
plugins: [
// 自動引入打包後的文件到html中:
// 對於每次打包都會從新經過hash值來生成文件名的狀況特別適用
// 也能夠經過template來生成一個咱們本身定義的html模板,而後幫咱們把打包後生成的文件引入
new HtmlWebpackPlugin({
filename: 'index.html', // 生成html文件的文件名
template: absPath('../index.html') // 使用的html模板
})
]
};
// webpack.dev.js
const merge = require('webpack-merge');
const baseConfig = require('./webpack.config')
module.exports = merge(baseConfig, {
mode: 'development',
devtool: 'cheap-module-eval-source-map'
})
// webpack.prod.js
const merge = require('webpack-merge');
const baseConfig = require('./webpack.config')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = merge(baseConfig, {
mode: 'production',
devtool: 'cheap-module-source-map',
plugins: [
new CleanWebpackPlugin()
]
})
複製代碼
而後咱們在package.json
中分別爲開發環境和生產環境添加打包快捷命令:
"scripts": {
"build": "webpack --config ./config/webpack.prod.js",
"start": "webpack --config ./config/webpack.dev.js"
},
複製代碼
如今目錄結構是這樣的:
接下來咱們就能夠直接使用yarn build
和yarn start
命令來分別爲開發環境和生產環境進行打包了
webpackDevServer
方便開發和調試如今咱們在每次修改代碼後,都須要再次從新打包文件,並在瀏覽器中打開生成的html
文件。並且因爲是本地打開html
文件,諸如請求代理、局域網內預覽等功能便沒法使用。
webpack-dev-server
爲咱們提供了一個簡單的web
服務器,可以實時從新加載(live reloading
)修改後的代碼,而且也提供瞭如proxy
等功能來支持咱們跨域請求接口,極大的方便了開發。
yarn add webpack-dev-server -D
複製代碼
以後咱們在開發環境的webpack
配置中添加以下配置:
devServer: {
// 告訴服務器從哪裏提供內容。只有在你想要提供靜態文件時才須要
contentBase: absPath('../dist'),
// 是否在啓動服務時自動打開瀏覽器
open: true,
// 端口在沒有設置的時候默認爲8080
port: 3000
}
複製代碼
固然,這樣更改以後咱們要經過webpack-dev-server
來執行開發時的配置文件:
// "start": "webpack --config ./config/webpack.dev.js"
"start": "webpack-dev-server --config ./config/webpack.dev.js"
複製代碼
執行yarn start
:
當咱們在開發過程當中修改一些代碼的時候,咱們可能只是想讓咱們當前修改的內容生效,而不是形成整個頁面的刷新,致使咱們每次修改都要從新以前的步驟來驗證咱們的代碼。
在webpack
中咱們經過以下配置來實現模塊熱替換的功能(hot module replacement
),固然這個功能只在開發環境開啓:
// webpack.dev.js
devServer: {
// 告訴服務器從哪裏提供內容。只有在你想要提供靜態文件時才須要
contentBase: absPath('../dist'),
// 是否在啓動服務時自動打開瀏覽器
open: true,
// 端口在沒有設置的時候默認爲8080
port: 3000,
// 啓用webpack的模塊熱替換特性
hot: true
},
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
]
複製代碼
和以前配置的區別有2個:
devServer
添加配置項: hot: true
webpack
的插件: webpack.NameModulesPlugin
和webpack.HotModuleReplacementPlugin
而後這樣只能實現css
文件的熱更新,對於js
的熱更新,咱們還須要添加一下代碼:
// accept接收2個參數: 1. dependencies: 一個字符串或字符串數組 2. callback: 模塊更新後觸發的函數
if (module.hot) {
module.hot.accept('./utils/printSomething', () => {
console.log('update module');
})
}
複製代碼
頁面中的效果以下:
你們可能會發現咱們在寫css
和相似於vue
和react
框架代碼的時候,並無本身手寫module.hot.accept
方法,這是框架和css
的loader
已經幫咱們進行了自動處理,咱們只須要關注代碼的書寫便可。
babel
轉義es6
語法在平常工做中,咱們會使用不少es6
裏的新語法,這些語法在咱們目前使用的chrome
新版瀏覽其中通常均可以很好的支持,可是在一些國產瀏覽器或者低版本瀏覽器中可能會出現兼容性問題。
這裏咱們須要經過babel-loader
來進行語法的轉義:
yarn add babel-loader @babel/core @babel/preset-env -D
複製代碼
在webpack
中進行配置:
// webpack.config.js 中添加一個loader配置項
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
},
複製代碼
項目根目錄創建.babelrc
文件,並加入以下代碼:
{
"presets": [
"@babel/preset-env"
]
}
複製代碼
這樣配置以後,項目中的Promise
,map
等語法依舊不會進行轉換,這裏咱們使用@babel/polyfill
來轉換這些語法:
yarn add @babel/polyfill -D
複製代碼
.babelrc
進行以下配置:
{
"presets": [
[
"@babel/env",
{
// 爲低版本引入babel/polyfill
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
},
// 使用useBuiltIns要指定corejs
"corejs": "2",
// 只對項目中用到的`esnext`語法進行polyfill處理
"useBuiltIns": "usage",
}
]
]
}
複製代碼
這樣配置以後就能夠經過babel
來轉換esnext
的一些語法,而且能夠兼容低版本和國產瀏覽器。因爲使用到了useBuiltIns:usage
,只會對咱們使用到的新語法進行轉換,減小了polyfill
的體積
react
開發環境在學習完以上內容之後,咱們須要搭建一個支持react
框架語法的webpack
配置。
首先咱們安裝相應的依賴:
yarn add react react-dom
yarn add @babel/preset-react @babel/plugin-proposal-class-properties -D
複製代碼
.babelrc
中配置以下:
{
"presets": [
[
"@babel/env",
{
// 爲低版本引入babel/polyfill
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
},
// 使用useBuiltIns要指定corejs
"corejs": "2",
// 只對項目中用到的`esnext`語法進行polyfill處理
"useBuiltIns": "usage",
}
],
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-proposal-class-properties"
]
}
複製代碼
以後咱們在入口文件main.js
中寫入以下代碼:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
state = {
number: 10
}
render() {
return (
<div> hello Webapck React <h2>{this.state.number}</h2> </div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root')) 複製代碼
執行yarn start
命令:
到這裏爲止,咱們已經能夠搭建一個簡單的react
開發環境,以後咱們能夠繼續學習一些webpack
的高級概念。