日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

webpack配置指南

發(fā)布時(shí)間:2024/8/22 编程问答 32 如意码农
生活随笔 收集整理的這篇文章主要介紹了 webpack配置指南 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Webpack已經(jīng)出來很久了,相關(guān)的文章也有很多,然而比較完整的例子卻不是很多,讓很多新手不知如何下腳,下腳了又遍地坑

說實(shí)話,官方文檔是蠻亂的,而且有些還是錯(cuò)的錯(cuò)的。。很多配置問題只有爬過坑才知道

本文首先介紹Webpack(3)的一些基礎(chǔ)知識(shí),然后以一個(gè)已經(jīng)完成的小Demo,逐一介紹如何在項(xiàng)目中進(jìn)行配置

該Demo主要包含編譯Sass/ES6,提取(多個(gè))CSS文件,提取公共文件,模塊熱更新替換,開發(fā)與線上環(huán)境區(qū)分,使用jQuery插件的方式、頁(yè)面資源引入路徑自動(dòng)生成(可指定生成位置),熱更新編譯模版文件自動(dòng)生成webpack服務(wù)器中的資源路徑,編寫一個(gè)簡(jiǎn)單的插件,異步加載模塊 等基礎(chǔ)功能

應(yīng)該能幫助大家更好地在項(xiàng)目中使用Webpack3來管理前端資源

本文比較啰嗦,可以直接看第四部分Webpack3配置在Demo中的應(yīng)用,或者直接去Fork這個(gè)Demo邊看邊玩

Webpack已升級(jí)為v4版本,優(yōu)化之后性能提升好幾倍,請(qǐng)移步這個(gè) webpack4項(xiàng)目配置Demo,以及 這篇升級(jí)優(yōu)化點(diǎn)

首先,學(xué)習(xí)Webpack,還是推薦去看官方文檔,還是挺全面的,包括中文的和英文的,以及GitHub上關(guān)于webpack的項(xiàng)目issues,還有就是一些完整了例子,最后就是得自己練手配置,才能在過程中掌握好這枯燥的配置。

  • 1. 為什么要用Webpack
  • 2. 什么是Webpack
  • 3. Webpack的基礎(chǔ)配置
    • 1. webpack的配置方式主要有三種

      • 1. 通過cli命令行傳入?yún)?shù)
      • 2. 通過在一個(gè)配置文件設(shè)置相應(yīng)配置,導(dǎo)出使用
      • 3. 通過使用NodeJS的API配置
    • 2. 常見的幾個(gè)配置屬性
      • 1. context  絕對(duì)路徑
      • 2. entry  模塊入口文件設(shè)置
      • 3. resolve 處理資源的查找引用方式
      • 4. output 設(shè)置文件的輸出
      • 5. devtool指定sourceMap的配置
      • 6. module指定模塊如何被加載
      • 7.  plugins設(shè)置webpack配置過程中所用到的插件
  • 4. Webpack3配置在Demo中的應(yīng)用
    • 1. 搭建個(gè)服務(wù)器
    • 2. 設(shè)置基礎(chǔ)項(xiàng)目目錄
    • 3. 開發(fā)和生產(chǎn)環(huán)境的Webpack配置文件區(qū)分
    • 4. 設(shè)置公共模塊
    • 5. 編譯ES6成ES5
    • 6. 編譯Sass成CSS,嵌入到頁(yè)面<style>標(biāo)簽中,或?qū)⑵涮崛〕觯ǘ鄠€(gè))CSS文件來用<link>引入
    • 7. jQuery插件的引入方式
    • 8. HtmlWebpackPlugin將頁(yè)面模板編譯成最終的頁(yè)面文件,包含JS及CSS資源的引用
    • 9. 使用url-loader和file-loader和html-loader來處理圖片、字體等文件的資源引入路徑問題
    • 10. 模塊熱更新替換的正確姿勢(shì)
    • 11. 壓縮模塊代碼
    • 12. 異步加載模塊
    • 13. 其他配置
    • 14. 自定義HtmlWebpackPlugin插件編譯模版文件生成的JS/CSS插入位置
    • 15. 熱更新編譯模版文件自動(dòng)生成webpack服務(wù)器中的資源路徑
    • 16. 其他常見問題整理

一 、為什么要用Webpack

首先,得知道為什么要用webpack

前端本可以直接HTML、CSS、Javascript就上了,不過如果要處理文件依賴、文件合并壓縮、資源管理、使用新技術(shù)改善生活的時(shí)候,就得利用工具來輔助了。

以往有常見的模塊化工具RequireJS,SeaJS等,構(gòu)建工具Grunt、Gulp等,新的技術(shù)Sass、React、ES6、Vue等,要在項(xiàng)目中使用這些東西,不用工具的話就略麻煩了。

其實(shí)簡(jiǎn)單地說要聚焦兩點(diǎn):模塊化以及自動(dòng)構(gòu)建。

模塊化可以使用RequireJS來處理依賴,使用Gulp來進(jìn)行構(gòu)建;也可以使用ES6新特性來處理模塊化依賴,使用webpack來構(gòu)建

兩種方式都狠不錯(cuò),但潮流所驅(qū),后者變得愈來愈強(qiáng)大,當(dāng)然也不是說后者就替代了前者,只是大部分情況下,后者更好

二、什么是Webpack

如其名,Web+Pack 即web的打包,主要用于web項(xiàng)目中打包資源進(jìn)行自動(dòng)構(gòu)建。

Webpack將所有資源視為JS的模塊來進(jìn)行構(gòu)建,所以對(duì)于CSS,Image等非JS類型的文件,Webpack會(huì)使用相應(yīng)的加載器來加載成其可識(shí)別的JS模塊資源

通過配置一些信息,就能將資源進(jìn)行打包構(gòu)建,更好地實(shí)現(xiàn)前端的工程化

三、Webpack的基礎(chǔ)配置

可以認(rèn)為Webpack的配置是4+n模式,四個(gè)基本的 entry(入口設(shè)置)output(輸出設(shè)置)loader(加載器設(shè)置)、plugin(插件設(shè)置),然后加上一些特殊功能的配置。

使用Webpack首先需要安裝好NodeJS

node -v
npm -v

確保已經(jīng)可以使用node,使用NPM包管理工具來安裝相應(yīng)依賴包(網(wǎng)絡(luò)環(huán)境差可以使用淘寶鏡像CNPM來安裝)

npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm -v

全局安裝好webpack包

npm i -g webpack
webpack -v

1. webpack的配置方式主要有三種

1. 通過cli命令行傳入?yún)?shù) 

webpack ./src.js -o ./dest.js --watch --color 

2. 通過在一個(gè)配置文件設(shè)置相應(yīng)配置,導(dǎo)出使用

// ./webpack.config.js文件
module.exports = {
   context: ...
entry: { },
output: { }
}; // 命令行調(diào)用(不指定文件時(shí)默認(rèn)查找webpack.config.js)
webpack [--config webpack.config.js]

3. 通過使用NodeJS的API配置

這個(gè)和第二點(diǎn)有點(diǎn)類似,區(qū)別主要是第二種基本都是使用{key: value}的形式配置的,API則主要是一些調(diào)用

另外,某些插件的在這兩種方式的配置上也有一些區(qū)別

最常用的是第二種,其次第三種,第一種不太建議單獨(dú)使用(因?yàn)橄鄬?duì)麻煩,功能相對(duì)簡(jiǎn)單)

2. 常見的幾個(gè)配置屬性

1. context  絕對(duì)路徑

一般當(dāng)做入口文件(包括但不限于JS、HTML模板等文件)的上下文位置,

默認(rèn)使用當(dāng)前目錄,不過建議還是填上一個(gè)

// 上下文位置
context: path.resolve(__dirname, 'static')

2. entry  模塊入口文件設(shè)置

可以接受字符串表示一個(gè)入口文件,不過一般來說是多頁(yè)應(yīng)用多,就設(shè)置成每頁(yè)一個(gè)入口文件得了

比如home對(duì)應(yīng)于一個(gè)./src/js/home模塊,這里的key會(huì)被設(shè)置成webpack的一個(gè)chunk,即最終webpack會(huì)又三個(gè)chunkname:home | detail | common

也可以對(duì)應(yīng)于多個(gè)模塊,用數(shù)組形式指定,比如這里把jquery設(shè)置在common的chunk中

也可以設(shè)置成匿名函數(shù),用于動(dòng)態(tài)添加的模塊

// 文件入口配置
entry: {
home: './src/js/home',
detail: './src/js/detail',
// 提取jquery入公共文件
common: ['jquery']
},

3. resolve 處理資源的查找引用方式

如上方其實(shí)是省略了后JS綴,又比如想在項(xiàng)目中引入util.js 可以省略后綴

import {showMsg} from './components/util';
// 處理相關(guān)文件的檢索及引用方式
resolve: {
extensions: ['.js', '.jsx', '.json'],
modules: ['node_modules'],
alias: { }
},

4. output 設(shè)置文件的輸出

最基礎(chǔ)的就是這三個(gè)了

path指定輸出目錄,要注意的是這個(gè)目錄影響范圍是比較大,與該chunk相關(guān)的資源生成路徑是會(huì)基于這個(gè)路徑的

filename指定生成的文件名,可以使用[name] [id]來指定相應(yīng)chunk的名稱,如上的home和detail,用[hash]來指定本次webpack編譯的標(biāo)記來防緩存,不過建議是使用[chunkhash]來依據(jù)每個(gè)chunk單獨(dú)來設(shè)置,這樣不改變的chunk就不會(huì)變了

hash放在?號(hào)之后的好處是,不會(huì)生成新的文件(只是文件內(nèi)容被更改了),同時(shí)hash會(huì)附在引用該資源的URL后(如script標(biāo)簽中的引用)

publicPath指定所引用資源的目錄,如在html中的引用方式,建議設(shè)置一個(gè)

// 文件輸出配置
output: {
// 輸出所在目錄
path: path.resolve(__dirname, 'static/dist/js'),
filename: '[name].js?[chunkhash:8]'// 設(shè)置文件引用主路徑
publicPath: '/public/static/dist/js/'
}

5.devtool指定sourceMap的配置

如果開啟了,就可以在瀏覽器開發(fā)者工具查看源文件

// 啟用sourceMap
devtool: 'cheap-module-source-map',

比如這里就是對(duì)應(yīng)的一個(gè)source Map,建議在開發(fā)環(huán)境下開啟,幫助調(diào)試每個(gè)模塊的代碼

這個(gè)配置的選項(xiàng)是滿多的,而且還可以各種組合,按照自己的選擇來吧

6. module指定模塊如何被加載

通過設(shè)置一些規(guī)則,使用相應(yīng)的loader來加載

主要就是配置module的rules規(guī)則組,通過use字段指定loader,如果只有一個(gè)loader,可以直接用字符串,loader要設(shè)置options的就換成數(shù)組的方式吧

或者使用多個(gè)loader的時(shí)候,也用數(shù)組的形式,規(guī)則不要用{ }留空,在windows下雖然正常,但在Mac下會(huì)報(bào)錯(cuò)提示找不到loader

多個(gè)loader遵循從右到左的pipe 的方式,如下 eslint-loader是先于babel-loader執(zhí)行的

通過exclude或include等屬性再確定規(guī)則的匹配位置

// 模塊的處理配置,匹配規(guī)則對(duì)應(yīng)文件,使用相應(yīng)loader配置成可識(shí)別的模塊
module: {
rules: [{
test: /\.css$/,
use: 'css-loader'
}, {
test: /\.jsx?$/,
// 編譯js或jsx文件,使用babel-loader轉(zhuǎn)換es6為es5
exclude: /node_modules/,
use: [{
loader: 'babel-loader',
options: { }
}, {
loader: 'eslint-loader'
}]
}

7.  plugins設(shè)置webpack配置過程中所用到的插件

比如下方為使用webpack自帶的提取公共JS模塊的插件

// 插件配置
plugins: [
// 提取公共模塊文件
new webpack.optimize.CommonsChunkPlugin({
chunks: ['home', 'detail'],
filename: '[name].js',
name: 'common'
}),
new ... ]

這就是webpack最基礎(chǔ)的東西了,看起來內(nèi)容很少,當(dāng)然還有其他很多,但復(fù)雜的地方在于如何真正去使用這些配置

四、Webpack配置在Demo中的應(yīng)用

下面以一個(gè)相對(duì)完整的基礎(chǔ)Demo著手,介紹一下幾個(gè)基本功能該如何配置

Demo項(xiàng)目地址   建議拿來練練

1. 搭建個(gè)服務(wù)器

既然是Demo,至少就得有一個(gè)服務(wù)器,用node來搭建一個(gè)簡(jiǎn)單的服務(wù)器,處理各種資源的請(qǐng)求返回

新建一個(gè)服務(wù)器文件server.js,以及頁(yè)面文件目錄views,其他資源文件目錄public

服務(wù)器文件很簡(jiǎn)單,請(qǐng)求什么就返回什么,外加了一個(gè)gzip的功能

let http = require('http'),
fs = require('fs'),
path = require('path'),
url = require('url'),
zlib = require('zlib'); http.createServer((req, res) => {
let {pathname} = url.parse(req.url),
acceptEncoding = req.headers['accept-encoding'] || '',
referer = req.headers['Referer'] || '',
raw; console.log('Request: ', req.url); try {
raw = fs.createReadStream(path.resolve(__dirname, pathname.replace(/^\//, ''))); raw.on('error', (err) => {
console.log(err); if (err.code === 'ENOENT') {
res.writeHeader(404, {'content-type': 'text/html;charset="utf-8"'});
res.write('<h1>404錯(cuò)誤</h1><p>你要找的頁(yè)面不存在</p>');
res.end();
}
}); if (acceptEncoding.match(/\bgzip\b/)) {
res.writeHead(200, { 'Content-Encoding': 'gzip' });
raw.pipe(zlib.createGzip()).pipe(res);
} else if (acceptEncoding.match(/\bdeflate\b/)) {
res.writeHead(200, { 'Content-Encoding': 'deflate' });
raw.pipe(zlib.createDeflate()).pipe(res);
} else {
res.writeHead(200, {});
raw.pipe(res);
}
} catch (e) {
console.log(e);
}
}).listen(8088); console.log('服務(wù)器開啟成功', 'localhost:8088/');

2. 設(shè)置基礎(chǔ)項(xiàng)目目錄

頁(yè)面文件假設(shè)采用每一類一個(gè)目錄,目錄下的tpl為源文件,另外一個(gè)為生成的目標(biāo)頁(yè)面文件

/public目錄下,基本配置文件就放在根目錄下,JS,CSS,Image等資源文件就放在/public/static目錄下

我們要利用package.json文件來管理編譯構(gòu)建的包依賴,以及設(shè)置快捷的腳本啟動(dòng)方式,所以,先在/public目錄下執(zhí)行 npm init 吧

public/static/dist目錄用來放置編譯后的文件目錄,最終頁(yè)面引用的將是這里的資源

public/static/imgs目錄用來放置圖片源文件,有些圖片會(huì)生成到dist中

public/static/libs目錄主要用來放置第三方文件,也包括那些很少改動(dòng)的文件

public/static/src 用來放置js和css的源文件,相應(yīng)根目錄下暴露一個(gè)文件出來,公共文件放到相應(yīng)子目錄下(如js/components和scss/util)

 

最后文件結(jié)構(gòu)看起來是這樣的,那就可以開干了

3. 開發(fā)和生產(chǎn)環(huán)境的Webpack配置文件區(qū)分

首先在項(xiàng)目目錄下安裝webpack吧

npm i webpack --save-dev

用Webpack來構(gòu)建,在開發(fā)環(huán)境和生產(chǎn)環(huán)境的配置還是有一些區(qū)別的,構(gòu)建是耗時(shí)的,比如在開發(fā)環(huán)境下就不需要壓縮文件、計(jì)算文件hash、提取css文件、清理文件目錄這些輔助功能了,而可以引入熱更新替換來加快開發(fā)時(shí)的模塊更新效率。

所以建議區(qū)分一下兩個(gè)環(huán)境,同時(shí)將兩者的共同部分提取出來便于維護(hù)

NODE_ENV是nodejs在執(zhí)行時(shí)的環(huán)境變量,webpack在運(yùn)行構(gòu)建期間也可以訪問這個(gè)變量,所以我們可以在dev和prod下配置相應(yīng)的環(huán)境變量

這個(gè)配置寫在package.json里的scripts字段就好了,比如

"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build:dev": "export NODE_ENV=development && webpack-dev-server --config webpack.config.dev.js",
"build:prod": "export NODE_ENV=production && webpack --config webpack.config.prod.js --watch "
},

這樣一來,我們就可以直接用 npm run build:prod來執(zhí)行生產(chǎn)環(huán)境的配置命令(設(shè)置了production的環(huán)境變量,使用prod.js)

直接用npm run build:dev來執(zhí)行開發(fā)環(huán)境的配置命令(設(shè)置了development的環(huán)境變量,使用dev.js,這里還使用了devServer,后面說)

注意這里是Unix系統(tǒng)配置環(huán)境變量的寫法,在windows下,記得改成 SET NODE_ENV=development&& webpack-dev-server.......(&&前不要空格)

然后就可以在common.js配置文件中獲取環(huán)境變量

// 是否生產(chǎn)環(huán)境
isProduction = process.env.NODE_ENV === 'production',

然后可以在plugins中定義一個(gè)變量提供個(gè)編譯中的模塊文件使用

// 插件配置
plugins: [
// 定義變量,此處定義NODE_ENV環(huán)境變量,提供給生成的模塊內(nèi)部使用
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
}
}),

這樣一來,我們可以在home.js中判斷是否為開發(fā)環(huán)境來引入一些文件

// 開發(fā)環(huán)境時(shí),引入頁(yè)面文件,方便改變頁(yè)面文件后及時(shí)模塊熱更新
if (process.env.NODE_ENV === 'development') {
require('../../../../views/home/home.html');
}

然后我們使用webpack-merge工具來合并公共配置文件和開發(fā)|生產(chǎn)配置文件

npm i webpack-merge --save-dev

merge = require('webpack-merge')

commonConfig = require('./webpack.config.common.js')

/**
* 生產(chǎn)環(huán)境Webpack打包配置,整合公共部分
* @type {[type]}
*/
module.exports = merge(commonConfig, {
// 生產(chǎn)環(huán)境不開啟sourceMap
devtool: false, // 文件輸出配置
output: {
// 設(shè)置文件引用主路徑
publicPath: '/public/static/dist/js/'
}, // 模塊的處理配置

4. 設(shè)置公共模塊

公共模塊其實(shí)可以分為JS和CSS兩部分(如果有提取CSS文件的話)

在公共文件的plugin中加入

// 提取公共模塊文件
new webpack.optimize.CommonsChunkPlugin({
chunks: ['home', 'detail'],
// 開發(fā)環(huán)境下需要使用熱更新替換,而此時(shí)common用chunkhash會(huì)出錯(cuò),可以直接不用hash
filename: '[name].js' + (isProduction ? '?[chunkhash:8]' : ''),
name: 'common'
}),

設(shè)置公共文件的提取源模塊chunks,以及最終的公共文件模塊名

公共模塊的文件的提取規(guī)則是chunks中的模塊公共部分,如果沒有公共的就不會(huì)提取,所以最好是在entry中就指定common模塊初始包含的第三方模塊,如jquery,react等

 // 文件入口配置
entry: {
home: './src/js/home',
detail: './src/js/detail',
// 提取jquery入公共文件
common: ['jquery']
},

5. 編譯ES6成ES5

要講ES6轉(zhuǎn)換為ES5,當(dāng)然首用babel了,先安裝loader及相關(guān)的包

npm i babel-core babel-loader babel-preset-env babel-polyfill babel-plugin-transform-runtime --save-dev

-env包主要用來配置語(yǔ)法支持度

-polyfill用來支持一些ES6拓展的但babel轉(zhuǎn)換不了的方法(Array.from Generator等)

-runtime用來防止重復(fù)的ES6編譯文件所需生成(可以減小文件大小)

然后在/public根目錄下新建 .babelrc文件,寫入配置

{
"presets": [
"env"
],
"plugins": ["transform-runtime"]
}

然后在common.js的配置文件中新增一條loader配置就行了,注意使用exclude排除掉不需要轉(zhuǎn)換的目錄,否則可能會(huì)出錯(cuò)哦

{
test: /\.jsx?$/,
// 編譯js或jsx文件,使用babel-loader轉(zhuǎn)換es6為es5
exclude: /node_modules/,
use: [{
loader: 'babel-loader',
options: { }
}]
}

6. 編譯Sass成CSS,嵌入到頁(yè)面<style>標(biāo)簽中,或?qū)⑵涮崛〕觯ǘ鄠€(gè))CSS文件來用<link>引入

sass的編譯node-sass需要python2.7的環(huán)境,先確定已經(jīng)安裝并設(shè)置了環(huán)境變量

npm i sass-loader node-sass style-loader css-loader --save-dev

類似的,設(shè)置一下loader規(guī)則

不過這里要設(shè)置成使用提取CSS文件的插件設(shè)置了,因?yàn)樗膁isable屬性可以快速切換是否提取CSS(這里設(shè)置成生產(chǎn)環(huán)境才提取)

好好看這個(gè)栗子,其實(shí)分三步:設(shè)置(new)兩個(gè)實(shí)例,loader匹配css和sass兩種文件規(guī)則,在插件中引入這兩個(gè)實(shí)例

提取多個(gè)CSS文件其實(shí)是比較麻煩的,但也不是不可以,方法就是設(shè)置多個(gè)實(shí)例和對(duì)應(yīng)的幾個(gè)loader規(guī)則

這里把引入的sass當(dāng)做是自己寫的文件,提取成一個(gè)文件[name].css,把引入的css當(dāng)做是第三方的文件,提取成一個(gè)[name]_vendor.css,既做到了合并,也做到了拆分,目前還沒想到更好的方案

上面提到過,output的path設(shè)置成了/public/static/dist/js ,所以這里的filename 生成是基于上面的路徑,可以用../來更換生成的css目錄

[contenthash]是css文件內(nèi)容的hash,在引用它的地方有體現(xiàn)

fallback表示不可提取時(shí)的代替方案,即上述所說的使用style-loader嵌入到<style>標(biāo)簽中

npm i extract-text-webpack-plugin --save-dev

ExtractTextWebpackPlugin = require('extract-text-webpack-plugin')

/ 對(duì)import 引入css(如第三方css)的提取
cssExtractor = new ExtractTextWebpackPlugin({
// 開發(fā)環(huán)境下不需要提取,禁用
disable: !isProduction,
filename: '../css/[name]_vendor.css?[contenthash:8]',
allChunks: true
}) // 對(duì)import 引入sass(如自己寫的sass)的提取
sassExtractor = new ExtractTextWebpackPlugin({
// 開發(fā)環(huán)境下不需要提取,禁用
disable: !isProduction,
filename: '../css/[name].css?[contenthash:8]',
allChunks: true
}); // 插件配置
plugins: [
// 從模塊中提取CSS文件的配置
cssExtractor,
sassExtractor
] module: {
rules: [{
test: /\.css$/,
// 提取CSS文件
use: cssExtractor.extract({
// 如果配置成不提取,則此類文件使用style-loader插入到<head>標(biāo)簽中
fallback: 'style-loader',
use: [{
loader: 'css-loader',
options: {
// url: false,
minimize: true
}
},
// 'postcss-loader'
]
})
}, {
test: /\.scss$/,
// 編譯Sass文件 提取CSS文件
use: sassExtractor.extract({
// 如果配置成不提取,則此類文件使用style-loader插入到<head>標(biāo)簽中
fallback: 'style-loader',
use: [
'css-loader',
// 'postcss-loader',
{
loader: 'sass-loader',
options: {
sourceMap: true,
outputStyle: 'compressed'
}
}
]
})
}

這樣一來,如果在不同文件中引入不同的文件,生成的css可能長(zhǎng)這樣

// ./home.js
import '../../libs/bootstrap-datepicker/datepicker3.css'; import '../../libs/chosen/chosen.1.0.0.css'; import '../../libs/layer/skin/layer.css'; import '../../libs/font-awesome/css/font-awesome.min.css'; import '../scss/detail.scss'; // ./detail.js
import '../../libs/bootstrap-datepicker/datepicker3.css'; import '../../libs/chosen/chosen.1.0.0.css'; import '../../libs/layer/skin/layer.css'; import '../scss/detail.scss';

// ./home.html
<link href="/public/static/dist/js/../css/common_vendor.css?66cb1f48" rel="stylesheet">
<link href="/public/static/dist/js/../css/common.css?618d2a04" rel="stylesheet">
<link href="/public/static/dist/js/../css/home_vendor.css?12a314c8" rel="stylesheet">
<link href="/public/static/dist/js/../css/home.css?c196fc33" rel="stylesheet"> // ./detail.html
<link href="/public/static/dist/js/../css/common_vendor.css?66cb1f48" rel="stylesheet">
<link href="/public/static/dist/js/../css/common.css?618d2a04" rel="stylesheet">

可以看到,公共文件也被提取出來了,利用HtmlWebpackPlugin就能將其置入了

另外,可以看到這里的絕對(duì)路徑,其實(shí)就是因?yàn)樵趏utput中設(shè)置了publicPath為/public/static/dist/js/

當(dāng)然了,也不是說一定得在js中引入這些css資源文件,你可以直接在頁(yè)面中手動(dòng)<link>引入第三方CSS

我這里主要是基于模塊化文件依賴,以及多CSS文件的合并壓縮的考慮才用這種引入方式的

7. jQuery插件的引入方式

目前來說,jQuery及其插件在項(xiàng)目中還是很常用到的,那么就要考慮如何在Webpack中使用它

第一種方法,就是直接頁(yè)面中<script>標(biāo)簽引入了,但這種方式不受模塊化的管理,好像有些不妥

第二種方法,就是直接在模塊中引入所需要的jQuery插件,而jQuery本身由Webpack插件提供,通過ProvidePlugin提供模塊可使用的變量$|jQuery|window.jQuery

不過這種方法好像也有不妥,把所有第三方JS都引入了,可能會(huì)降低編譯效率,生成的文件也可能比較臃腫

npm i jquery --save

//
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
}), ] // ./home.js import '../../libs/bootstrap-datepicker/bootstrap-datepicker.js';
console.log('.header__img length', jQuery('.header__img').length);

第三種辦法,可以在模塊內(nèi)部直接引入jQuery插件,也可以直接在頁(yè)面通過<script>標(biāo)簽引入jQuery插件,而jQuery本身由Webpack的loader導(dǎo)出為全局可用

上述ProvidePlugin定義的變量只能在模塊內(nèi)部使用,我們可以使用expose-loader將jQuery設(shè)置為全局可見

npm i expose-loader --save

// 添加一條規(guī)則
{
test: require.resolve('jquery'),
// 將jQuery插件變量導(dǎo)出至全局,提供外部引用jQuery插件使用
use: [{
loader: 'expose-loader',
options: '$'
}, {
loader: 'expose-loader',
options: 'jQuery'
}]
}

要注意在Webpack3中不能使用webpack.NamedModulesPlugin()來獲取模塊名字,它會(huì)導(dǎo)致expose 出錯(cuò)失效(bug)

不過現(xiàn)在問題又來了,這個(gè)應(yīng)該是屬于HtmlWebpackPlugin的不夠機(jī)智的問題,先說說它怎么用吧

8. HtmlWebpackPlugin將頁(yè)面模板編譯成最終的頁(yè)面文件,包含JS及CSS資源的引用

第一個(gè)重要的功能就是生成對(duì)資源的引入了,第二個(gè)就是幫助我們填入資源的chunkhash值防止瀏覽器緩存

這個(gè)在生產(chǎn)環(huán)境使用就行了,開發(fā)環(huán)境是不需要的

npm i html-webpack-plugin --save-dev

HtmlWebpackPlugin = require('html-webpack-plugin')

plugins: [

 // 設(shè)置編譯文件頁(yè)面文件資源模塊的引入
new HtmlWebpackPlugin({
// 模版源文件
template: '../../views/home/home_tpl.html',
// 編譯后的目標(biāo)文件
filename: '../../../../views/home/home.html',
// 要處理的模塊文件
chunks: ['common', 'home'],
// 插入到<body>標(biāo)簽底部
inject: true
}),
new HtmlWebpackPlugin({
template: '../../views/detail/detail_tpl.html',
filename: '../../../../views/detail/detail.html',
chunks: ['common', 'detail'],
inject: true
}), ]

使用方式是配置成插件的形式,想對(duì)多少個(gè)模板進(jìn)行操作就設(shè)置多少個(gè)實(shí)例

注意template是基于context配置中的上下文的,filename是基于output中的path路徑的

// ./home_tpl.html

    <script src="/public/static/libs/magicsearch/jquery.magicsearch2.js"></script>
</body> // ./home.html <script src=/public/static/libs/magicsearch/jquery.magicsearch2.js></script>
<script type="text/javascript" src="/public/static/dist/js/common.js?cc867232"></script>
<script type="text/javascript" src="/public/static/dist/js/home.js?5d4a7836"></script>
</body>

它會(huì)編譯成這樣,然而,然而,要注意到這里是有問題的

這里有個(gè)jQuery插件,而Webpack使用expose是將jQuery導(dǎo)出到了全局中,我們通過entry設(shè)置把jQuery提取到了公共文件common中

所以正確的做法是common.js文件先于jQuery插件加載

而這個(gè)插件只能做到在<head> 或<body>標(biāo)簽尾部插入,我們只好手動(dòng)挪動(dòng)一下<script>的位置

不過,我們還可以基于這個(gè)插件,再寫一個(gè)插件來實(shí)現(xiàn)自動(dòng)提升公共文件 <script>標(biāo)簽到最開始

HtmlWebpackPlugin運(yùn)行時(shí)有一些事件

    html-webpack-plugin-before-html-generation
html-webpack-plugin-before-html-processing
html-webpack-plugin-alter-asset-tags
html-webpack-plugin-after-html-processing
html-webpack-plugin-after-emit
html-webpack-plugin-alter-chunks

在編譯完成時(shí),正則匹配到<script>標(biāo)簽,找到所設(shè)置的公共模塊(可能設(shè)置了多個(gè)公共模塊),按實(shí)際順序提升這些公共模塊即可

完整代碼如下:

 1 // ./webpack.myPlugin.js
2
3
4 let extend = require('util')._extend;
5
6
7 // HtmlWebpackPlugin 運(yùn)行后調(diào)整公共script文件在html中的位置,主要用于jQuery插件的引入
8 function HtmlOrderCommonScriptPlugin(options) {
9 this.options = extend({
10 commonName: 'common'
11 }, options);
12 }
13
14 HtmlOrderCommonScriptPlugin.prototype.apply = function(compiler) {
15 compiler.plugin('compilation', compilation => {
16 compilation.plugin('html-webpack-plugin-after-html-processing', (htmlPluginData, callback) => {
17 // console.log(htmlPluginData.assets);
18
19 // 組裝數(shù)組,反轉(zhuǎn)保證順序
20 this.options.commonName = [].concat(this.options.commonName).reverse();
21
22 let str = htmlPluginData.html,
23 scripts = [],
24 commonScript,
25 commonIndex,
26 commonJS;
27
28 //獲取編譯后html的腳本標(biāo)簽,同時(shí)在原h(huán)tml中清除
29 str = str.replace(/(<script[^>]*>(\s|\S)*?<\/script>)/gi, ($, $1) => {
30 scripts.push($1);
31 return '';
32 });
33
34 this.options.commonName.forEach(common => {
35 if (htmlPluginData.assets.chunks[common]) {
36 // 找到公共JS標(biāo)簽位置
37 commonIndex = scripts.findIndex(item => {
38 return item.includes(htmlPluginData.assets.chunks[common].entry);
39 });
40
41 // 提升該公共JS標(biāo)簽至頂部
42 if (commonIndex !== -1) {
43 commonScript = scripts[commonIndex];
44 scripts.splice(commonIndex, 1);
45 scripts.unshift(commonScript);
46 }
47 }
48 });
49
50 // 重新插入html中
51 htmlPluginData.html = str.replace('</body>', scripts.join('\r\n') + '\r\n</body>');
52
53 callback(null, htmlPluginData);
54 });
55 });
56 };
57
58
59 module.exports = {
60 HtmlOrderCommonScriptPlugin,
61 };

然后,就可以在配置中通過插件引入了

{HtmlOrderCommonScriptPlugin} = require('./webpack.myPlugin.js');

// HtmlWebpackPlugin 運(yùn)行后調(diào)整公共script文件在html中的位置,主要用于jQuery插件的引入
new HtmlOrderCommonScriptPlugin({
// commonName: 'vendor'
})

親測(cè)還是蠻好用的,可以應(yīng)對(duì)簡(jiǎn)單的需求了

9. 使用url-loader和file-loader和html-loader來處理圖片、字體等文件的資源引入路徑問題

這個(gè)配置開發(fā)環(huán)境和生產(chǎn)環(huán)境是不同的,先看看生產(chǎn)環(huán)境的,主要的特點(diǎn)是有目錄結(jié)構(gòu)的設(shè)置,設(shè)置了一些生成的路徑以及名字信息

開發(fā)環(huán)境因?yàn)槭鞘褂昧薲evServer,不需要控制目錄結(jié)構(gòu)

npm i url-loader file-loader@0.10.0 html-loader --save-dev

這里要注意的是file-loader就不要用0.10版本以上的了,會(huì)出現(xiàn)奇怪的bug,主要是下面設(shè)置的outputPath和publicPath和[path]會(huì)不按套路出牌

導(dǎo)致生成的頁(yè)面引用資源變成奇怪的相對(duì)路徑

rules: [{
test: /\.(png|gif|jpg)$/,
use: {
loader: 'url-loader',
// 處理圖片,當(dāng)大小在范圍之內(nèi)時(shí),圖片轉(zhuǎn)換成Base64編碼,否則將使用file-loader引入
options: {
limit: 8192,
// 設(shè)置生成圖片的路徑名字信息 [path]相對(duì)context,outputPath輸出的路徑,publicPath相應(yīng)引用的路徑
name: '[path][name].[ext]?[hash:8]',
outputPath: '../',
publicPath: '/public/static/dist/',
}
}
}, {
test: /\.(eot|svg|ttf|otf|woff|woff2)\w*/,
use: [{
loader: 'file-loader',
options: {
// 設(shè)置生成字體文件的路徑名字信息 [path]相對(duì)context,outputPath輸出的路徑,publicPath相應(yīng)引用的主路徑
name: '[path][name].[ext]?[hash:8]',
outputPath: '../',
publicPath: '/public/static/dist/',
// 使用文件的相對(duì)路徑,這里先不用這種方式
// useRelativePath: isProduction
}
}],
}, {
test: /\.html$/,
// 處理html源文件,包括html中圖片路徑加載、監(jiān)聽html文件改變重新編譯等
use: [{
loader: 'html-loader',
options: {
minimize: true,
removeComments: false,
collapseWhitespace: false
}
}]
}]

比較生澀難懂,看個(gè)栗子吧

scrat.png是大于8192的,最終頁(yè)面引入會(huì)被替換成絕對(duì)路徑,并且?guī)в衕ash防止緩存,而輸出的圖片所在位置也是用著相應(yīng)的目錄,便于管理

// ./home_tpl.html

        <img class="header__img" src="../../public/static/imgs/kl/scrat.png" width="200" height="200">

// ./home.html

        <img class="header__img" src=/public/static/dist/imgs/kl/scrat.png?8ad54ef5 width=200 height=200>

如果換個(gè)小圖,就會(huì)替換成base64編碼了,在css中的引入也一樣

<img class=header__img src=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAA6CAYAAABrnUYFAAAaVElEQVR4Xu1df3wdVZX/npnkNUlbWn6sFhAELSgibSYtCIhagXXB0vdSsOwu4CKiXbAglk8zAVwloELzwoqIUsFFRXQVaiEvRcAfLD9EfqxtXtqKS239AQoFRUlb+tK+5N2znzNvJp1M34+ZeZOElLn/QPPuPffcc2e+c+75dQlxiyUQSyCWwChJgEaJbkw2lkAsgVg

再來看看開發(fā)環(huán)境的

rules: [{
test: /\.(png|gif|jpg)$/,
// 處理圖片,當(dāng)大小在范圍之內(nèi)時(shí),圖片轉(zhuǎn)換成Base64編碼,否則將使用file-loader引入
use: [{
loader: 'url-loader',
options: {
limit: 8192
}
}]
}, {
test: /\.(eot|svg|ttf|otf|woff|woff2)\w*/,
// 引入文件
use: 'file-loader'
}]

10. 模塊熱更新替換的正確姿勢(shì)

在開發(fā)環(huán)境下,如果做到模塊的熱更新替換,效果肯定是棒棒的。生成環(huán)境就先不用了

在最初的時(shí)候,只是做到了熱更新,并沒有做到熱替換,其實(shí)都是坑在作祟

熱更新,需要一個(gè)配置服務(wù)器,Webpack集成了devServer的nodejs服務(wù)器,配置一下它

// 開發(fā)環(huán)境設(shè)置本地服務(wù)器,實(shí)現(xiàn)熱更新
devServer: {
contentBase: path.resolve(__dirname, 'static'),
// 提供給外部訪問
host: '0.0.0.0',
port: 8188,
// 設(shè)置頁(yè)面引入
inline: true
},

正常的話,啟動(dòng)服務(wù)應(yīng)該就可以了吧

webpack-dev-server --config webpack.config.dev.js

要記住,devServer編譯的模塊是輸出在服務(wù)器上的(默認(rèn)根目錄),不會(huì)影響到本地文件,所以要在頁(yè)面上手動(dòng)設(shè)置一下引用的資源

<script src="http://localhost:8188/common.js"></script>
<script src="http://localhost:8188/home.js"></script>

瀏覽器訪問,改動(dòng)一下home.js文件,這時(shí)應(yīng)該可以看到頁(yè)面自動(dòng)刷新,這就是熱更新了

總結(jié)

以上是生活随笔為你收集整理的webpack配置指南的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。