
近幾年,前端各種框架工具層出不窮,從兩三年前還是一個jQuery搞定全站,到之后 requirejs/seajs,node,gulp/webpack,Angular/React/Vue,RN/weex的不斷涌現(xiàn),完全顛覆了原來的前端開發(fā)模式。
那么這些框架和工具給我們到底帶來了什么好處呢?其實我認為最核心莫過于這兩點:模塊化開發(fā)、自動化工程。而本次前端重構所圍繞的核心問題就是自動化工程,將原有的 gulp版本的項目利用webpack徹底改造,順利消滅了既繁瑣又易錯的人工操作。
gulp 版本的痛點
我們先來看下奇貨商城之前的開發(fā)流程:
從上圖可以看出,我們奇貨前端開發(fā)之前存在的一些痛點:
前端在后端項目里面修改 vm文件聯(lián)調(diào);
開發(fā)聯(lián)調(diào)需要上傳靜態(tài)資源到測試域名 CDN;
不同目錄下的資源還需要在 CDN上傳網(wǎng)站上一級一級目錄的點開再上傳;
上線前需要人工去替換 vm 文件里的 CDN路徑;
上線前還需要人工去上傳靜態(tài)資源到正式域名 CDN;
開發(fā)模式不支持 es6 轉(zhuǎn)義,導致低端安卓機無法在本地進行前端調(diào)試;
gulp-babel不完全支持es6轉(zhuǎn)es5,導致部分低端安卓機出現(xiàn)各種莫名其妙的問題;
以上這些痛點,造成的重復性無用功,既浪費精力又著實讓人蛋疼,而經(jīng)過這次的框架重構,只需一鍵操作,就可完成聯(lián)調(diào)和發(fā)布的部署。省心省力還不會出錯。
如何利用 webpack 做自動化
先看一下改版后,奇貨商城的開發(fā)流程:
從上圖可以看到,我們經(jīng)過改版后做到了:
**vm **文件自動生成
開發(fā)聯(lián)調(diào)直接讀取本地靜態(tài)資源
打包后所有資源在同一級目錄,一次性拖拽上傳(下個版本將實現(xiàn)前靜態(tài)資源自動上傳)
只需一行配置項,自動生成對應的線上 **CDN **路徑
完美的 babel-loader,**es6 **語法也可在低端安卓機上輕松本地調(diào)試;
下面我們看看如何實現(xiàn)。
項目結構
下面是部分主要目錄結構:
├── build (所有的webpack配置項)
│ ├── build.js
│ ├── dev-client.js
│ ├── dev-server.js
│ ├── utils.js (★入口配置,生成文件配置,vm生成都靠這個文件)
│ ├── webpack.base.conf.js (基礎配置)
│ ├── webpack.dev.conf.js (開發(fā)模式配置)
│ └── webpack.prod.conf.js (生成環(huán)境配置)
├── config (node環(huán)境變量,入口文件的配置)
│ ├── dev.env.js
│ ├── entry.js (頁面文件列表)
│ ├── index.js (★主配置文件)
│ ├── prod.env.js
│ └── skinEntry.js (皮膚文件列表)
├── dist (打包后生成的文件夾,已全部轉(zhuǎn)成vm)
│ ├── goods
│ │ ├── detail.vm
│ ├── index.vm
│ └── static (打包后-靜態(tài)資源文件)
│ ├── css
│ ├── js
│ └── skins (打包后皮膚文件夾)
│ ├── default
│ │ ├── default.1184b4d7.js
│ │ ├── default.f07ae9df.css
│ │ └── default.html
│ ├── huotu
│ └── pay
├── mock
├── package.json
├── routes
├── src (源文件)
│ ├── js
│ │ ├── components
│ │ ├── goods
│ │ │ ├── detail.js
│ │ │ └── skins
│ │ │ ├── default.js
│ │ │ ├── huotu.js
│ │ ├── index.js
│ ├── less
│ │ ├── components
│ │ ├── goods
│ │ │ ├── detail.less
│ │ │ └── skins
│ │ │ ├── default.less
│ │ │ ├── huotu.less
│ ├── index.less
│ └── pages
│ ├── components
│ ├── goods
│ │ ├── detail.html
│ │ └── skins
│ │ ├── default.html
│ │ ├── huotu.html
│ └── index.html
├── static
│ └── images
└── unit (公共庫)
├── common (業(yè)務組件)
│ ├── js
│ └── less
├── layout (公共頁面)
│ ├── footer.html
│ └── header.html
└── lib (第三方組件)
以上是我們奇商城的前端目錄結構。
webpack的一些必用的loader和plugin,
例如
less-loader, style-loader, file-loader, html-loader, 還有UglifyJsPlugin, ExtractTextPlugin, OptimizeCSSPlugin
等等,在這里就不詳細展開了。
我們重點說說以下幾點核心:
node 腳本調(diào)用 webpack
通過node腳本來調(diào)用webpack,而不是直接在命令行啟動webpack,會有這么幾個用處:
通過node啟 express做本地mock數(shù)據(jù);
開發(fā)環(huán)境和生產(chǎn)環(huán)境的公共配置項,通過
webpack-merge模塊做抽離,方便維護;可以設置node環(huán)境變量,以區(qū)分不同環(huán)境中的打包配置,這點在后面還有一個大招;這貨可以說是整個構建過程里,核心中的核心了。
自動生成 vm、開發(fā)環(huán)境調(diào)用本地資源,以及皮膚文件的管理都有這個插件的功。部分代碼:
new HtmlWebpackPlugin({
filename: process.env.NODE_ENV === 'production' ? path + name + '.vm' : path + name + '.html',
template: template,
inject: false,
chunks: [pathBuild + name, 'vendor', 'manifest']
})
通過判斷node環(huán)境變量,決定生成vm還是本地html;通過這個插件實現(xiàn)了 js 模塊打包,公共模塊提取,客戶端緩存&增量發(fā)布,皮膚文件生成。部分代碼:
for (let i = 0; i < entry.length; i++) {
let item = entry[i]
let path = item.path
let name = item.name
let pathBuild = path.replace(/\//g, '-');
result[pathBuild + name] = './src/js/' + path + name + '.js'
}
for (let i = 0; i < skinEntry.length; i++) {
let item = skinEntry[i]
let path = item.path
let name = item.name
if (process.env.NODE_ENV === 'production') {
result['../skins/' + path + name] = './src/js/goods/skins/' + name + '.js'
} else {
result['skins/' + path + name] = './src/js/goods/skins/' + name + '.js'
}
}
Object.assign(result, {
vendor: ['@unit/common/js/base', '@unit/common/js/util']
})
// 公共文件提取
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor', // 注意不要.js后綴
chunks: utils.computeChunks(entryConfig, '')
})
// 避免修改業(yè)務代碼導致vendor的md5改變,保留文件緩存
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
chunks: ['vendor']
})
自動化部署
自動化部署是在打包服務器通過腳本實現(xiàn)的,先通過npm命令打包前端工程,然后將代碼copy到后端工程中,最后打包后端項目,再發(fā)布。
遇到的困難
公共文件的引入
webpack官方文檔并沒有如何引入公共html文件的說明,這一點是在翻了
N多資料后才發(fā)現(xiàn)的,最終的方案是:
去掉 webpack.config.js 文件中配置的全局html-loader,這樣html模版文件就不會被html-loader解析,我們可以使用ejs 語法嵌入其他 html 頁面和圖片資源。因為沒了全局的html-loader解析html文件,使用ejs語法嵌入的資源返回的是 ejs 代碼,還需要使用html-loader來解析成html代碼。
(html-loader!)表示引用html-loader這個加載器來解析
<%= require('html-loader!../layout/header.html') %>
但是這樣將全局html-loader去掉后,又碰到了下面的問題。
jsp 變量的引入
vm中有時需要直接引用后端的變量,如${cssUrl},就像這樣:
這時候webpack打包居然就報錯了,報錯了:
原因排查
出現(xiàn)這個問題的原因應該是由于HtmlWebpackPlugin這個插件引用的模版默認是 ejs,當不使用全局html-loader的時候,模板文件其實是以 ejs 解析的,而${cssUrl}在 ejs 中也識別為一個變量,當然就報錯了。
解決方法
這過程中,整個周末都在想這個問題,甚至已經(jīng)開始考慮用gulp+webpack的方案了。。又翻了很多資料,突然想到既然是ejs模板,可以嘗試了一些 ejs 去寫,而不是非要把這個模板以html的方式 loader 進來,然后就有了如下方法:
<link href="<%= '${cssUrl}' %>" rel="stylesheet">
這時候就被識別為一個字符串了!成功解決。
進一步探索,巧用 node 環(huán)境變量
上面的方法解決的其實也是挺丑的,因為本地開發(fā)的時候需要引用本地文件的,上線的時候又得傻乎乎地去一個個地方去替換:
<!-- <link href="<%= skinCss %>" rel="stylesheet"> -->
<link href="/skins/pay/pay.css" rel="stylesheet">
然后馬上試了下,在模板文件中用 ejs 去讀node 環(huán)境變量
process.env.NODE_ENV,果然能取到值,就有了下面這個相對完美的方案:
<%
if (process.env.NODE_ENV === 'production') {
skinCss = '${cssUrl}';
} else {
skinCss = '/skins/pay/pay.css';
}
%>
<link href="<%= skinCss %>" rel="stylesheet">
其中production就是利用 **node **啟動 **webpack **時配置的,在這里派上了大用場。
End
到這里,我們奇貨商城已經(jīng)實現(xiàn)了前端工程自動化,再也不用一遍又一遍地去 vm 里修改路徑,人工去記著改了哪些文件,要上傳哪些靜態(tài)資源。更加不用擔心漏傳什么資源文件而導致線上bug辣。
作者:Woodk
出處:http://www.cnblogs.com/woodk/p/7459467.html
技術交流QQ群 :238757010
關注微信公眾號Reboot51后臺回復 python、自動化即可獲得相應課程的試聽資料
運維自動化班 6 期報名開始
12月17日(周日)