從零開始學webpack:進階配置

前一篇文章中介紹了一些webpack配置經常使用的loader和插件,而且完成了一個適合大多數場景的基礎配置文件;本文將繼續介紹webpack的配置,相對於上一篇文章,本文更加着重開發效率和個性化需求的配置javascript

多頁應用打包

前一篇文章中配置都只適用於單頁應用的打包,可是在實際的工做也還會涉及到多頁應用的項目開發;css

webpack中多頁應用與單頁應用打包的不一樣之處主要體如今如下幾點:html

  • 多入口,不一樣的頁面使用不一樣的入口文件
  • 多出口,入口和出口相對應,有多個入口也就有了多個出口
  • 多HTML,不一樣的入口能夠輸出不一樣的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配置,能夠參考官方文檔

devTool(配置sourceMap)

在開發的過程當中常常會經過控制檯查看錯誤信息;可是打包以後的錯誤信息位置將會以構建後的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解決跨域問題

跨域是先後端接口交互時一個很常見的問題,解決跨域發方式也有不少,這裏主要介紹如何經過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須要使用到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來實現跨域

mock數據

webpack-dev-server提供了一個before鉤子;它的第一個參數暴露了webpack-dev-server內部的express服務,經過這個服務,能夠完成一些數據的mock

普通數據mock

<!-- webpack.config.js -->
module.exports = {
    devServer: {
        before(app) {
            app.get('/name', (req, res) => {
                res.json('阿白Smile')
            })
        }
    }
}   
複製代碼

使用mockerAPI

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'
        });
    }
}
複製代碼

配置到devServerbefore鉤子

<!-- 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"}

配置resolve屬性

webpack啓動後會從入口文件開始找出全部依賴的模塊;resolve的做用就是告訴webpack如何尋找這些模塊所對應的文件

alias (配置別名)

resolve.alias用於配置別名,經過別名能夠把原來的導入路徑映射到一個新的路徑, 它可讓模塊的引入更加簡單;vue中經常爲src文件夾設置一個別名爲@

文件結構

|--src
|--|--assets
|--|--|--main.css
複製代碼

配置

<!-- webpack.config.js -->
module.exports = {
    // 省略其餘代碼 
    resolve: {
        alias: {
            '@': './src'
        }
    }
}
複製代碼

使用

import '@/assets/main.css'
複製代碼

使用別名以後,別名會直接映射到別名所指定的路徑;在使用相關模塊時,可使用更加簡單的方式書寫模塊路徑

modules

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中查找;

extensions (擴展名配置)

在模塊導入語句中沒有帶文件後綴時,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

相關文章
相關標籤/搜索