奇貨商城重構——webpack自動化工程

近幾年,前端各類框架工具層出不窮,從兩三年前仍是一個jQuery搞定全站,到以後requirejs/seajs,node,gulp/webpack,Angular/React/Vue,RN/weex的不斷涌現,徹底顛覆了原來的前端開發模式。css

那麼這些框架和工具給咱們到底帶來了什麼好處呢?其實我認爲最核心莫過於這兩點:模塊化開發自動化工程。而本次前端重構所圍繞的核心問題就是自動化工程,將原有的gulp版本的項目利用webpack完全改造,順利消滅了既繁瑣又易錯的人工操做。html

gulp版本的痛點

咱們先來看下奇貨商城以前的開發流程:前端

a1

從上圖能夠看出,咱們奇貨前端開發以前存在的一些痛點:node

  1. 前端在後端項目裏面修改vm文件聯調;webpack

  2. 開發聯調須要上傳靜態資源到測試域名CDN;es6

  3. 不一樣目錄下的資源還須要在CDN上傳網站上一級一級目錄的點開再上傳;web

  4. 上線前須要人工去替換vm文件裏的CDN路徑;express

  5. 上線前還須要人工去上傳靜態資源到正式域名CDN;npm

  6. 開發模式不支持es6轉義,致使低端安卓機沒法在本地進行前端調試;json

  7. gulp-babel不徹底支持es6轉es5,致使部分低端安卓機出現各類莫名其妙的問題;

以上這些痛點,形成的重複性無用功,既浪費精力又着實讓人蛋疼,而通過此次的框架重構,只需一鍵操做,就可完成聯調和發佈的部署。省心省力還不會出錯。

如何利用webpack作自動化

先看一下改版後,奇貨商城的開發流程:

a2

從上圖能夠看到,咱們通過改版後作到了:

  1. vm文件自動生成

  2. 開發聯調直接讀取本地靜態資源

  3. 打包後全部資源在同一級目錄,一次性拖拽上傳(下個版本將實現前靜態資源自動上傳)

  4. 只需一行配置項,自動生成對應的線上CDN路徑

  5. 完美的babel-loader,es6語法也可在低端安卓機上輕鬆本地調試;

下面咱們看看如何實現。

項目結構

下面是部分主要目錄結構:

├── build   (全部的webpack配置項)
│   ├── build.js
│   ├── dev-client.js
│   ├── dev-server.js
│   ├── utils.js    (★入口配置,生成文件配置,vm生成都靠這個文件)
│   ├── webpack.base.conf.js    (基礎配置)
│   ├── webpack.dev.conf.js     (開發模式配置)
│   └── webpack.prod.conf.js    (生成環境配置)
├── config  (node環境變量,入口文件的配置)
│   ├── dev.env.js
│   ├── entry.js    (頁面文件列表)
│   ├── index.js    (★主配置文件)
│   ├── prod.env.js
│   └── skinEntry.js    (皮膚文件列表)
├── dist    (打包後生成的文件夾,已所有轉成vm)
│   ├── goods
│   │   ├── detail.vm
│   ├── index.vm
│   └── static  (打包後-靜態資源文件)
│       ├── 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  (業務組件)
    │   ├── js
    │   └── less
    ├── layout  (公共頁面)
    │   ├── footer.html
    │   └── header.html
    └── lib     (第三方組件)

以上是咱們奇商城的前端目錄結構。

webpack的一些必用的loader和plugin,例如less-loader, style-loader, file-loader, html-loader, 還有UglifyJsPlugin, ExtractTextPlugin, OptimizeCSSPlugin等等,在這裏就不詳細展開了。

咱們重點說說如下幾點核心:

node腳本調用webpack

經過node腳原本調用webpack,而不是直接在命令行啓動webpack,會有這麼幾個用處:

  • 經過node啓express作本地mock數據;

  • 開發環境和生產環境的公共配置項,經過webpack-merge模塊作抽離,方便維護;

  • 能夠設置node環境變量,以區分不一樣環境中的打包配置,這點在後面還有一個大招;

HtmlWebpackPlugin

這貨能夠說是整個構建過程裏,核心中的核心了。

自動生成vm、開發環境調用本地資源,以及皮膚文件的管理都有這個插件的功。部分代碼:

new HtmlWebpackPlugin({
  filename: process.env.NODE_ENV === 'production' ? path + name + '.vm' : path + name + '.html',
  template: template,
  inject: false,
  chunks: [pathBuild + name, 'vendor', 'manifest']
})

經過判斷node環境變量,決定生成vm仍是本地html;

CommonsChunkPlugin

經過這個插件實現了js模塊打包,公共模塊提取,客戶端緩存&增量發佈,皮膚文件生成。部分代碼:

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, '')
})

// 避免修改業務代碼致使vendor的md5改變,保留文件緩存
new webpack.optimize.CommonsChunkPlugin({
  name: 'manifest',
  chunks: ['vendor']
})

自動化部署

自動化部署是在打包服務器經過腳本實現的,先經過npm命令打包前端工程,而後將代碼copy到後端工程中,最後打包後端項目,再發布。

a3

遇到的困難

公共文件的引入

webpack官方文檔並無如何引入公共html文件的說明,這一點是在翻了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},就像這樣:

b1

這時候webpack打包竟然就報錯了,報錯了:

b2

緣由排查

出現這個問題的緣由應該是因爲HtmlWebpackPlugin這個插件引用的模版默認是ejs,當不使用全局html-loader的時候,模板文件實際上是以ejs解析的,而${cssUrl}在ejs中也識別爲一個變量,固然就報錯了。

解決方法

這過程當中,整個週末都在想這個問題,甚至已經開始考慮用gulp+webpack的方案了。。

又翻了不少資料,忽然想到既然是ejs模板,能夠嘗試了一些ejs去寫,而不是非要把這個模板以html的方式loader進來,而後就有了以下方法:

<link href="<%= '${cssUrl}' %>" rel="stylesheet">

這時候就被識別爲一個字符串了!成功解決。

進一步探索,巧用node環境變量

上面的方法解決的其實也是挺醜的,由於本地開發的時候須要引用本地文件的,上線的時候又得傻乎乎地去一個個地方去替換:

<!-- <link href="<%= skinCss %>" rel="stylesheet">   -->
<link href="/skins/pay/pay.css" rel="stylesheet">

而後立刻試了下,在模板文件中用ejs去讀node環境變量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

到這裏,咱們奇貨商城已經實現了前端工程自動化,不再用一遍又一遍地去vm裏修改路徑,人工去記着改了哪些文件,要上傳哪些靜態資源。更加不用擔憂漏傳什麼資源文件而致使線上bug辣。:)

相關文章
相關標籤/搜索