在前一篇文章中介紹了一些webpack配置經常使用的loader和插件,而且完成了一個適合大多數場景的基礎配置文件;本文將繼續介紹webpack的配置,相對於上一篇文章,本文更加着重開發效率和個性化需求的配置javascript
前一篇文章中配置都只適用於單頁應用的打包,可是在實際的工做也還會涉及到多頁應用的項目開發;css
webpack中多頁應用與單頁應用打包的不一樣之處主要體如今如下幾點:html
配置前端
<!-- webpack.config.js -->
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: {
main: './main.js',
miniApp: './miniApp.js'
},
output: {
filename: [name].[hsah].js,
path: path.resolve(__dirname, 'dist')
},
plugin: [
new HtmlWebpackPlugin({
template: './index.html',
filename: 'main.html',
chunks: ['main']
}),
new HtmlWebpackPlugin({
template: './index.html',
filename: 'miniApp.html',
chunks: ['miniApp']
})
]
// 省略其餘代碼
}
複製代碼
[name]
: 構建包的名稱,它來源於entry中的key
值,在單頁應用中默認爲mainvue
chunks
: 用於指定須要引入到html中的js文件;與它相反,還有一個excludeChunks
參數,它用於指定不須要引入的js文件java
平常的開發中會遇到一些直接使用的文件,這些文件多是js,也多是css或者是圖片,它們不須要通過打包,能夠直接將他們拷貝到webpack構建目錄;可是手動拷貝不只麻煩而且容易出錯(好比,修改以後忘記拷貝)node
copy-webpack-plugin
插件用於將指定文件/文件夾複製到構建目錄;經過這個插件能夠將靜態文件直接複製到輸出目錄中webpack
安裝git
npm install copy-webpack-plugin -D
複製代碼
配置github
<!-- webpack.config.js -->
const path = require('path')
const copyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
plugin: [
new copyWebpackPlugin([
{
from: path.resolve(__dirname, './public'),
to: './dist'
}
])
]
}
複製代碼
from
: 指定須要複製的文件夾
to
: 指定複製後的文件夾
更多copyWebpackPlugin
配置,能夠參考官方文檔
在開發的過程當中常常會經過控制檯查看錯誤信息;可是打包以後的錯誤信息位置將會以構建後的js爲基準,這樣的錯誤信息由於沒有定位到源碼的錯誤位置,對調試很不友好
這個問題能夠經過對devTool
配置sourceMap
來解決;sourceMap是一個源碼映射文件,有多種格式可選,這裏只列舉幾個有表明性的,更多sourceMap格式能夠查看官方文檔
source-map
: 原始源代碼,會單獨生成源碼文件,能夠提示錯誤信息的列和行eval-source-map
: 原始源代碼,不會產生單獨的文件,可是能夠顯示行和列cheap-module-source-map
: 轉換後的代碼,生成單獨的文件,能夠提示行但不能提示列cheap-module-eval-source-map
: 原始源代碼,集成在打包後的文件中,不會生成獨立的文件,能夠提示行,但不能提示列綜合實際使用狀況和構建速度考慮,開發環境中通常使用cheap-module-eval-source-map
<!-- webpack.config.js -->
module.exports = {
// 省略其餘代碼
devtool: 'cheap-module-eval-source-map'
}
複製代碼
跨域是先後端接口交互時一個很常見的問題,解決跨域發方式也有不少,這裏主要介紹如何經過webpack的配置來解決跨域問題;
先經過server.js
在本地建立一個node服務, 啓動本地的3000端口做爲後端服務
<!-- server.js -->
const express = require('express')
const app = express()
app.get('/api/user', (req, res) => {
res.json({name: '阿白Smile'})
})
app.listen(3000)
複製代碼
使用命令行工具執行node server.js
命令啓動node服務,而後使用client.js
向後端服務發送請求
<!-- client.js -->
const xhr = new XMLHtttpRequest()
xhr.open('GET', '/api/user', true)
xhr.onload = function() {
console.log(xhr.response)
}
xhr.send()
複製代碼
接下來就經過webpack爲前端服務配置一個代理,將前端的服務代理到後端的服務上,這樣可讓前端和服務端在同一個服務下,以此來解決跨域問題
<!-- webpack.config.js -->
module.exports = {
devServer: {
prot: 3000,
host: 'localhost'
proxy: {
'/api': 'http://localhost:3000'
}
}
}
複製代碼
經過以上的配置,當client.js
訪問/api/user
時,請求會被代理到http://localhost:3000/api/user
若是不想每次都在接口路徑前加/api
,或者後端的接口沒有/api
這一層路徑,那麼能夠經過pathRewrite
重寫路徑,將/api
重寫爲空
<!-- webpack.config.js -->
module.exports = {
devServer: {
prot: 3000,
host: 'localhost'
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: {'/api': ''}
}
}
}
}
複製代碼
經過服務端啓動webpack,能夠將前端和服務端啓動在同一個服務上;當先後端都在同一個服務的時候天然就不存在跨域問題了
服務端啓動webpack須要使用到webpack-dev-middleware
中間件
安裝
npm install webpack-dev-middleware -D
複製代碼
配置
<!-- server.js -->
const express = require('express')
const webpack = require('webpack')
const middle = require('webpack-dev-middleware')
const app = express()
let config = require('./webpack.config.js')
let compiler = webpack(config)
app.use(middle(compiler))
app.get('/user', (req, res) => {
res.json({name: '阿白Smile'})
})
app.listen(3000)
複製代碼
使用這種方式的前提是可以操做服務器,不過既然均可以操做服務器了,那麼還能夠經過設置header
來實現跨域
webpack-dev-server
提供了一個before鉤子
;它的第一個參數暴露了webpack-dev-server
內部的express
服務,經過這個服務,能夠完成一些數據的mock
<!-- webpack.config.js -->
module.exports = {
devServer: {
before(app) {
app.get('/name', (req, res) => {
res.json('阿白Smile')
})
}
}
}
複製代碼
mocker-api
是一個爲 REST API 建立mock的webpack-dev-server
中間件。在後端服務尚未完成的時候,能夠經過這個中間件進行mock數據
安裝
npm install mocker-api -D
複製代碼
配置
建立一個mock.js
進行接口和數據mock
<!-- mock.js -->
module.exports = {
'GET /userInfo/:id': (req, res) => {
const { id } = req.params;
// 省略查詢
return res.json({
id,
name: '阿白smile'
});
}
}
複製代碼
配置到devServer
的before鉤子
中
<!-- webpack.config.js -->
const path = require('path')
const apiMocker = require('mocker-api');
const mockApi = path.resolve('./mock.js')
module.exports = {
devServer: {
before(app) {
mockApi(app, mockApi)
}
}
}
複製代碼
修改client.js
文件,訪問/userInfo
接口
<!-- client.js -->
const xhr = new XMLHtttpRequest()
xhr.open('GET', '/userInfo/1', true)
xhr.onload = function() {
console.log(xhr.response)
}
xhr.send()
複製代碼
使用npm run dev
命令執行腳本以後,在瀏覽器中打開,此時瀏覽器會打印出{"id":"1","name":"阿白smile"}
webpack啓動後會從入口文件開始找出全部依賴的模塊;resolve
的做用就是告訴webpack如何尋找這些模塊所對應的文件
resolve.alias
用於配置別名,經過別名能夠把原來的導入路徑映射到一個新的路徑, 它可讓模塊的引入更加簡單;vue中經常爲src
文件夾設置一個別名爲@
文件結構
|--src
|--|--assets
|--|--|--main.css
複製代碼
配置
<!-- webpack.config.js -->
module.exports = {
// 省略其餘代碼
resolve: {
alias: {
'@': './src'
}
}
}
複製代碼
使用
import '@/assets/main.css'
複製代碼
使用別名以後,別名會直接映射到別名所指定的路徑;在使用相關模塊時,可使用更加簡單的方式書寫模塊路徑
resolve.modules
用於指定webpack解析模塊時從哪些目錄下搜索模塊,默認狀況下,webpack只從node_modules
中搜索;
若是有一個模塊是本身編寫的文件,那麼它就不須要到node_modules
中取查找;好比, 咱們常常會在src/components
中編寫一些組件,爲了不每次引用組件都寫很長的路徑,就能夠經過modules
配置來簡化如下路徑
文件結構
|--src
|--|--components
|--|--|--navMenu.vue
複製代碼
配置
<!-- webpack.config.js -->
module.exports = {
// 省略其餘代碼
resolve: {
modules: ['./src/components', 'node_modules']
}
}
複製代碼
使用
import navMenu from 'navMenu'
複製代碼
webpack在搜索模塊時,會根據modules
的配置從左到右開始搜索;因此,在使用navMenu
時,會先從./src/components
中查找,若是沒有找到,則會去node_modules
中查找;
在模塊導入語句中沒有帶文件後綴時,webpack會自動帶上嘗試後綴去訪問(默認帶.js
); resolve.extensions
用於配置嘗試訪問的後綴列表,列表的優先級爲從左到右
配置
<!-- webpack.config.js -->
module.exports = {
// 省略其餘代碼
resolve: {
extensions: ['.js', '.vue', '.json']
}
}
複製代碼
使用
import index from './index'
複製代碼
上面的代碼結合配置以後,會先嚐試使用.js
做爲後綴去訪問index,若是不存在則嘗試使用.vue
做爲後綴去訪問;以此類推,若是配置的後綴都嘗試了尚未找到文件,則會報錯,文件未找到
在實際開發中,開發環境和線上生產環境每每會使用不一樣的服務和域名,而且不一樣的環境應該是能夠自動切換的,這就須要在代碼中使用環境變量來區分不一樣的環境,而後自動切換相應的服務和域名
webpack經過內置的DefinePlugin
插件能夠提供了一個區分環境的全局變量
DefinePlugin
的鍵值都是一個標誌符或者多個用 .
鏈接起來的標誌符
若是value
是一個字符串,它會被看成一個代碼片斷來使用
若是value
不是字符串,它會被轉化爲字符串(包括函數)
若是value
是一個對象,它全部的 key
會被一樣的方式定義(即全局能夠直接訪問對象的key,value
則會應用DefinePlugin
的鍵值規則)
若是在一個 key
前面加了 typeof
,它會被定義爲 typeof
調用
module.exports = { plugin: { new webpack.DefinePlugin({ DEV: JSON.stringify('dev') }) } }
if (DEV) { // 開發 BASE_URL: '' // 開發服務 } else { // 生產 BASE_URL: '' // 生產服務 }
經過環境變量能夠進行環境的區分,可是代碼中若是涉及到多處須要區分且每次都須要手動去配置,這不只加大了工做量並且還容易出錯;
通常的解決方案是將不一樣環境的配置分開到不一樣的文件,而後使用webpack-merge
將其與基礎配置進行合併
webpack.base.config.js
: 基礎配置文件 webpack.pro.config.js
: 線上生產環境配置文件 webpack.dev.config.js
: 開發環境配置文件
webpack-merge
是一個函數,它提供了合併功能,接收一個或多個對象/數組,用於對象的合併與數組的鏈接,返回合併後的對象;在合併對象時,若是同一個key出現屢次,則後面的覆蓋前面的
安裝
npm install webpack-merge
複製代碼
配置
const merge = require('webpack-merge')
let result = merge({name: '阿白smile', age: 18}, {age: 24, location: '北京'})
// 合併後的結果
// {name: '阿白smile', age: 24, location: '北京'}
複製代碼
webpack.base.config.js
是基礎的webpack配置,各個環境能夠通用,因此webpack.base.config.js
須要做爲merge的第一個參數
開發環境的配置
<!-- webpack.dev.config.js -->
const baseWebpackConfig = require('./webpack.base.config')
const merge = require('webpack-merge')
let devWebpackConfig = merge(baseWebpackConfig, {
mode: 'development',
devServer: {
// 省略其餘代碼
}
})
moudle.export = devWebpackConfig
複製代碼
生產環境的配置
<!-- webpack.pro.config.js -->
const baseWebpackConfig = require('./webpack.base.config')
const merge = require('webpack-merge')
let proWebpackConfig = merge(baseWebpackConfig, {
mode: 'production',
// 省略其餘代碼
})
moudle.export = proWebpackConfig
複製代碼
配置命令腳本
<!-- package.json -->
"scripts": {
"dev": "webpack-dev-server --config=webpack.dev.config.js"
"build": "webpack --config=webpack.pro.config.js"
}
複製代碼
熱更新主要應用在開發環境,當代碼更改以後頁面上只更新被修改的部分,不須要刷新頁面,對開發和調試很是有利;
webpack中的熱更新的配置主要依賴devServer.hot
以及webpack的內置插件HotModuleReplacementPlugin
<!-- webpack.config.js -->
module.exports = {
devServer: {
hot: true // 啓用熱更新
},
plugin: [
new webpack.HotModuleReplacementPlugin() // 熱更新插件
]
}
複製代碼
以上的代碼配置以後在瀏覽器查看,會發現仍是會刷新整個頁面;這個時候還須要在入口文件中作如下配置
if (module.hot) {
module.hot.accept()
}
複製代碼
module.hot
用於通知webpack此模塊能夠用於熱更新,更多信息能夠參考官方文檔
經過本篇文章中介紹的webpack配置,能夠知足更加個性化的開發需求以及更高效率的開發
本文所對應的配置源碼已提交到個人github
END