其中build文件夾中的webpack.prod.conf.js是我們主要注意的文件,我們在該文件中動(dòng)態(tài)設(shè)置不需要被打包的模塊并構(gòu)建出合適的鏈接。
確定需要使用CDN的模塊
在webpack項(xiàng)目中,所引入的第三方資源會(huì)被統(tǒng)一打包進(jìn)vender文件中,我們通過webpack的externals屬性可以設(shè)置打包時(shí)排除該模塊,詳情說明見外部擴(kuò)展(externals)。
在前面的步驟中,我們創(chuàng)建的項(xiàng)目包括vue、vue-router,在正式開發(fā)在還會(huì)有ui庫,如element-ui,為了方便演示,我們再安裝element-ui和axios兩個(gè)模塊,并實(shí)現(xiàn)在構(gòu)建是把這是個(gè)模塊以cdn的形式引入。
| 所需模塊 |
|---|
| vue |
| vue-router |
| element-ui |
| axios |
$ npm install element-ui axios -S
注意安裝時(shí)記得-S,它的作用是安裝完后在package.json項(xiàng)目文件中插入記錄,后續(xù)操作需要讀取已安裝模塊
確定CDN資源URI
對于cdn,我們可以自己搭建,也可以使用專業(yè)的cdn服務(wù)商,這里使用免費(fèi)的cdn bootcdn。選用免費(fèi)cdn有很多好處,但畢竟有隱患,那就是服務(wù)有可能會(huì)奔潰。
bootcdn https://www.bootcdn.cn
依次搜索出前面模塊,結(jié)果如下
| 模塊 | 版本 | js | css |
|---|---|---|---|
| vue | 2.5.2 | https://cdn.bootcss.com/vue/2.5.2/vue.min.js | - |
| vue-router | 3.0.1 | https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js | - |
| element-ui | 2.6.3 | https://cdn.bootcss.com/element-ui/2.6.1/index.js | https://cdn.bootcss.com/element-ui/2.6.1/theme-chalk/index.css |
| axios | 0.18.0 | https://cdn.bootcss.com/axios/0.18.0/axios.min.js | - |
按照規(guī)律,得出cdn資源路徑規(guī)則為
https://cdn.bootcss.com + 模塊名 + 版本號(hào) + 具體路徑
其他cdn服務(wù)商同理
打包前的處理
build/utils.js添加讀取事件
使用cdn其實(shí)也就是在webpack熱啟動(dòng)和打包項(xiàng)目的時(shí)候動(dòng)態(tài)插入script和style鏈接,為了方便維護(hù),我們通過在build/utils.js文件上添加幾個(gè)方法,將來在webpack.dev.conf.js和webpack.prod.conf.js上可以使用。
如果沒有build/utils.js,可以在其他文件上添加,只要在后續(xù)步驟中能操作到就行
1.添加cdn根地址
// build/utils.js 國內(nèi)免費(fèi)cdn鏡像源
exports.cdnBaseHttp = 'https://cdn.bootcss.com';
2.添加cdn模塊 按照需要?jiǎng)h改
// build/utils.js external配置
exports.externalConfig = [
{ name: 'vue', scope: 'Vue', js: 'vue.min.js' },
{ name: 'vue-router', scope: 'VueRouter', js: 'vue-router.min.js' },
{ name: 'axios', scope: 'axios', js: 'axios.min.js' },
{ name: 'element-ui', scope: 'ELEMENT', js: 'index.js', css: 'theme-chalk/index.css' },
];
name: 模塊名稱,與package.json同名
scope: 模塊作用域命名
js:js地址
css: css地址
這里特別注意scope,它是webpack配置的external參數(shù)下的信息,比如vue的作用域命名是Vue,vue-router的作用域命名是VueRouter,element-ui的作用域命名是ELEMENT,同理,jq的作用域命名是JQuery,具體做法是先引入該資源,然后在控制臺(tái)依次輸入近似的值,一個(gè)個(gè)匹配(目前沒找到更好的做法)。
3.添加獲取版本號(hào)方法
// build/utils.js 獲取模塊版本號(hào)
exports.getModulesVersion = () => {
let mvs = {};
let regexp = /^npm_package_.{0,3}dependencies_/gi;
for (let m in process.env) { // 從node內(nèi)置參數(shù)中讀取,也可直接import 項(xiàng)目文件進(jìn)來
if (regexp.test(m)) { // 匹配模塊
// 獲取到模塊版本號(hào)
mvs[m.replace(regexp, '').replace(/_/g, '-')] = process.env[m].replace(/(~|\^)/g, '');
}
}
return mvs;
};
4.導(dǎo)出不需要被打包的cdn模塊配置重點(diǎn)
// build/utils.js
exports.getExternalModules = config => {
let externals = {}; // 結(jié)果
let dependencieModules = this.getModulesVersion(); // 獲取全部的模塊和版本號(hào)
config = config || this.externalConfig; // 默認(rèn)使用utils下的配置
config.forEach(item => { // 遍歷配置
if (item.name in dependencieModules) {
let version = dependencieModules[item.name];
// 拼接css 和 js 完整鏈接
item.css = item.css && [this.cdnBaseHttp, item.name, version, item.css].join('/');
item.js = item.js && [this.cdnBaseHttp, item.name, version, item.js].join('/');
externals[item.name] = item.scope; // 為打包時(shí)準(zhǔn)備
} else {
throw new Error('相關(guān)依賴未安裝,請先執(zhí)行npm install ' + item.name);
}
});
return externals;
};
webpack.dev.conf.js添加cdn配置
在webpack熱啟動(dòng)本地調(diào)試的時(shí)候,我們可以使用cdn。
1.獲取cdn配置
// build/webpack.dev.conf.js 大概在15行
const externalConfig = JSON.parse(JSON.stringify(utils.externalConfig)); // 讀取配置
utils.getExternalModules(externalConfig); // 獲取到合適的路徑(引用類型,自動(dòng)改變)
// const devWebpackConfig = merge .......
在build/webpack.dev.conf.js中,默認(rèn)已經(jīng)引入了utils.js,所以可以直接調(diào)用相關(guān)方法,如果是自定義的文件,記得引入。
2.HtmlWebpackPlugin插件中導(dǎo)出cdn
緊接著我們在該文件下找到devWebpackConfig下的plugins下的HtmlWebpackPlugin插件,它的作用是動(dòng)態(tài)構(gòu)建html頁面,原始配置如下:
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
}),
// 代表處理根目錄下的index.html文件
我們可以往里面添加點(diǎn)自定義屬性,方便在index.html中調(diào)用。,修改如下:
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true,
cdnConfig: externalConfig, // cdn配置
onlyCss: true, //dev下只加載css
}),
其中cdnConfig和onlyCss自定義屬性,在html上通過htmlWebpackPlugin.options可以讀取到。
更多html-webpack-plugin配置情況官網(wǎng),這里暫時(shí)不需要更多。
webpack.prod.conf.js添加cdn配置和忽略模塊
在打包的時(shí)候,我們使用cdn,配置和前面dev的差不多,只不過需要做多一步。
1.獲取cdn配置
// build/webpack.prod.conf.js 大概在15行
const externalConfig = JSON.parse(JSON.stringify(utils.externalConfig)); // 讀取配置
const externalModules = utils.getExternalModules(externalConfig); // 獲取到合適路徑和忽略模塊
// const webpackConfig = merge(baseWebpackConfig....
注意此處的externalModules,后面用到,也就是比dev多的步驟。
2.webpck配置加多個(gè)屬性externals
externals代表構(gòu)建時(shí)不需要被處理的模塊,也就是前面說的scope需要注意的地方。
// build/webpack.prod.conf.js
const webpackConfig = merge(baseWebpackConfig, {
externals: externalModules, // 構(gòu)建時(shí)忽略的資源
// 其他屬性
}
3.HtmlWebpackPlugin插件中導(dǎo)出cdn
和dev一樣,我們修改webpackConfig下的plugins下的HtmlWebpackPlugin插件配置 (這里的默認(rèn)配置比dev的多,主要是css壓縮和js壓縮相關(guān))
new HtmlWebpackPlugin({
// 其他默認(rèn)配置
cdnConfig: externalConfig, // cdn配置
onlyCss: false, //加載css
}),
加入和dev一樣的兩個(gè)配置,不過需要把onlyCss改為true,因?yàn)槲覀兿M虬鼤r(shí)不單單使用css。
index.html插入相關(guān)鏈接
webpack配置已經(jīng)完成,在html-webpack-plugin中已經(jīng)添加了相關(guān)參數(shù),我們再在頁面上可以直接使用,使用語法是ejs,和asp.net,jsp,php類似。
<!DOCTYPE html>
<html>
<head>
<!-- 其他標(biāo)簽 -->
<% htmlWebpackPlugin.options.cdnConfig.forEach(function(item){ if(item.css){ %>
<link href="<%= item.css %>" rel="stylesheet" />
<% }}) %>
</head>
<body>
<!-- 其他標(biāo)簽 -->
<% htmlWebpackPlugin.options.cdnConfig.forEach(function(item){ if(item.js && !htmlWebpackPlugin.options.onlyCss){ %>
<script type="text/javascript" src="<%= item.js %>"></script>
<% }}) %>
<!-- built files will be auto injected -->
</body>
</html>
```
通過<% %> 和 htmlWebpackPlugin.options 用js遍歷插入link標(biāo)簽和script標(biāo)簽。
ps: 修改了webpack配置,需要重啟項(xiàng)目才會(huì)生效