構建多頁面應用——靜態資源的處理

在之前的系列文章中,我已經介紹瞭如何用webpack實現多頁面應用的js,html,css的處理。今天就主要介紹如何處理靜態資源,在web開發中最常見的靜態資源就是圖片。

圖片的引用方式

而因爲在web中,圖片有兩種主要的引入方式,第一種是<img src="...">,第二種是backgorund-image: url(...)。前者在html中使用,後者在css中使用。

web開發中的圖片處理

圖片常見的類型有jp(e)g,png,gif,包括現在普遍使用的svg以及webp。svg作爲矢量圖形,有一定的使用場景,而webp作爲未來web開發的趨勢,有一定的使用場景,比如:愛奇藝中的輪播圖(carousel)中就是用webp,但其他的見到的不多。

現在,web開發中使用最多的還是jpg和png,處理他們,在構建工具中使用url-loaderfile-loader就好了,其中file-loader負責圖片的拷貝和輸出,並會給圖片名添加一個hash值。

說到這裏,很多人會想到字體圖標。以前處理web頁面中的圖標(icon),使用圖片來處理,這樣會帶來一個性能問題就是http請求的增多,這樣會造成服務器的負載壓力,同時會帶來用戶體驗的問題,因爲會出現頁面的局部空白和頁面重繪的問題,當然一種解決方案使雪碧圖(sprite),但是如果圖片過大怎麼解決,如何對圖片進行分解(大變小的問題),圖片的拼接比較困難,最要命的是在引用雪碧圖時要進行計算,除此之外就是如何對雪碧圖的組成圖片進行自定義的刪減,而使用字體圖標這些問題,都會得到一定程度的解決,當然在構建工具中可以使用webpack-spritesmith這個插件來處理組成雪碧圖的圖片。

當然,有些特殊的情況,需要使用base64,這裏使用url-loader即可。

將圖片處理爲base64有使用場景,將圖片轉換爲雪碧圖亦有使用場景,單獨的圖片處理也有使用場景(這些使用場景的圖片大小從左到右依次增大)。

這些場景在一個web項目中都會涉及到。

雖然使用字體圖標可以替代雪碧圖,因爲字體圖標有更小的尺寸,更自由的操作手法(如:圖標顏色的自定義),但是一個DIY的web項目還是有些圖標還是需要雪碧圖。

但是,這裏有一個問題,如何在一個項目中同時使用base64,雪碧圖,字體圖標,單獨的圖片。

在構建中如何使用多種圖片處理方式

在構建多頁面應用中,如何解決呢?

字體圖標

處理字體圖標很簡單,如:iconfont(阿里巴巴字體圖標庫),就像引用css那麼簡單。

base64

base64的處理,使用url-loader

雪碧圖

雪碧圖的處理,可使用webpack-spritesmith這個插件

單獨的圖片

使用file-loader,它負責拷貝url-loader的處理結果,並輸出。

上面就是我們常見的圖片處理,如果要處理svg可以參考svg-url-loader,如果要處理webp可以參考webp-loader

如何對圖片進行優化

對圖片進行優化,會帶來良好的用戶體驗。

熟悉圖片優化的都知道漸進式(progressive),可參考nuwen.net

jp(e)g可以進行連續性處理,這樣可保證圖片數據請求回來多少,就渲染多少,是自上而下的渲染,也是有模糊到清晰的狀態。

png可以進行交叉處理,這樣也可保證圖片數據請求回來多少,就渲染多少,它是整體的顯示,而且是又模糊狀態到清晰的狀態。

gif圖片一般使用小圖,如果是大圖會記號瀏覽器性能,還不如使用視頻,或者用css動畫來代替。我個人整理了一個css 動畫集,有需要的可以看一下。

在構建多頁面應用中,會使用到image-webpack-loader來做優化處理。

其中,配置項options中的mozjpeg 處理jp(e)g圖片,pngquant處理png圖片,gifsicle處理gif圖片,webp處理webp圖片。

多頁面應用中的圖片處理

首先,看一下多頁面應用中的目錄結構圖:

./src
│  aboutUs.js
│  contactUs.js
│  css.js
│  index.js
│  recruitment.js
│  
├─assets
│  │  favicon.jpg
│  │  
│  ├─css
│  │  │  index.scss
│  │  │  
│  │  ├─commons
│  │  │  ├─container
│  │  │  │      index.scss
│  │  │  │      
│  │  │  ├─footer
│  │  │  │      index.scss
│  │  │  │      
│  │  │  └─header
│  │  │          index.scss
│  │  │          
│  │  ├─productus
│  │  │      index.scss
│  │  │      productus-sprite.scss
│  │  │      
│  │  └─utils
│  │          btn.scss
│  │          form.scss
│  │          inital.scss
│  │          list.scss
│  │          modeal.scss
│  │          normalize.scss
│  │          pagination.scss
│  │          popover.scss
│  │          table.scss
│  │          text.scss
│  │          tooltip.scss
│  │          
│  └─imgs
│      ├─base64
│      │      fe.jpg
│      │      
│      ├─other
│      │      float.jpg
│      │      productus-sprite.png
│      │      
│      └─sprites
│          └─productus
│                  product-us_01.png
│                  product-us_02.png
│                  product-us_03.png
│                  product-us_04.png
│                  product-us_05.png
│                  product-us_06.png
│                  product-us_07.png
│                  product-us_08.png
│                  product-us_09.png
│                  product-us_10.png
│                  product-us_11.png
│                  product-us_12.png
│                  
├─pages
│  │  recruitment.pug
│  │  template.pug
│  │  
│  └─components
│      ├─commons
│      │  ├─container
│      │  │      index.pug
│      │  │      
│      │  ├─footer
│      │  │      index.pug
│      │  │      
│      │  └─header
│      │          index.pug
│      │          
│      └─productus
│              index.pug
│              
└─utils
        load.js

跟以前的實例代碼相比,這次的文件目錄結構變化較大,這裏將要處理的所有文件模塊都放在了src目錄下。

可能有人會問,爲什麼要要將目錄分的這麼細,下面我就說一下爲什麼這麼分。

  • aboutUs.js, contactUs.js, index.js, recruitment.js是四個路由頁面,要用到的js代碼,css.js處理各個路由頁面公用的css代碼;
  • 靜態資源目錄(assets)下,存放web項目常用的靜態資源;
  • 靜態資源目錄下的css目錄統一存放整個web項目所用到的css樣式。其中commons存放公用的css模塊,每個公用模塊有創建一個目錄存放該公用模塊可以使用到的css模塊(提醒,不要分的過於細),而其他的如productus存放產品模塊代碼,根據開發的需要可以創建其他的模塊目錄,目錄結構類似於commons中的header模塊,其中utils放置自己總結的工具模塊代碼,如tableform等。然後,指定style-loader,css-loader等樣式相關的loader處理css樣式文件,這樣可以減少遍歷,縮短構建時間。
  • 靜態資源目錄下的imgs目錄,用來存放整個項目中,用到的圖片。在這裏,分爲base64spriteohter等,爲什麼要這樣分?如果分的話file-loader這個webapck的loader會複製並導出imgs目下所有的圖片,者在構建中並不是我們需要的,這樣會增加構建的時間。如果讓url-loader處理base64目錄下的圖片,file-loader處理ohter目錄下的圖片,webpack-spritesmith處理sprite目錄下的圖片,並將生成的圖片放到ohter目錄下,用file-loader進行二次處理。這樣做,webpack處理更精確,可以減少不必要的遍歷,極大地減少構建的時間,同樣方便對圖片的管理,特別是對於需要改變sprite的圖片的管理。
  • imgs目錄下創建base64目錄,sprite目錄,使用file-loader指定處理ohter目錄,是爲了避免file-loader將所有的圖片都拷貝一份並導出到dist輸出目錄中,因爲與base64相關的文件已經在css樣式文件中了,再拷貝一份,已經沒有意義,而sprite相關的文件會被webpack-spritesmith插件先處理生成一個文件,所以再拷貝它們也沒有意義,還會讓構建速度更慢。
  • page目錄下,放置所有的html代碼塊(這裏使用pug編譯器生成相應的html代碼塊),它的目錄分類和css相類似,它們是一一對應的關係。

注意:iamge-webpack-loader,要先對所有的圖片進行優化處理,然後再用其他loader處理。loader的執行順序,如果你是style-loader!css-loader!sass-loader"使用,它是從右到左方向先後執行,如果你是在配置文件中的rules: [...]數組中,它也是從右到左的方向執行,如果你將所有的loader規則有回車符號隔開,那麼它就是自下而上的執行。

一類特殊的圖片引用

針對<img src="...">的圖片使用,ul-loader是不會處理html中的img引用,現在處理這樣情況的loader或插件,也並沒有一個比較出名的。現在通用的做法就是將圖片拷貝一份到生成目錄中,copy-webpack-plugin。參考配置代碼如下:

new CopyWebpackPlugin([
  {
  from: path.resolve(__dirname, 'assets/imgs/other/'),
  to: path.resolve(__dirname, 'dist/assets/imgs/other/'),
  ignore: ['.*']
  }
]),

但這樣,會帶來另一個問題就是圖片的優化問題,如何使用 image-webpack-loader?本來的目的是對項目中使用到的所有的圖片進行優化,而現在只能對base64sprite目錄下的圖片進行優化處理。不過,不要慌,可以通過創建一個新的npm腳本命令(本是裏使用的是npm run img)來對圖片進行壓縮處理,新建了一個目錄static用來保存優化前的圖片,ohter用來保存優化後的圖片。

首先,需要安裝imagemin, imagemin-mozjpeg, imagemin-optipng, imagemin-gifsicle :

yarn add imagemin imagemin-mozjpeg imagemin-optipng imagemin-gifsicle --dev

然後,在項目的根目錄添加一個優化圖片的文件optzing-img.js,代碼如下:

const path = require('path')
const imagemin = require('imagemin');
const imageminMozjpeg = require('imagemin-mozjpeg');
const imageminOptipng = require('imagemin-optipng');
const imageminGifsicle = require('imagemin-gifsicle');

(async () => {
    await imagemin(
    [
      path.resolve(__dirname, 'src/assets/imgs/static/*.jpg'),
      path.resolve(__dirname, 'src/assets/imgs/static/*.png'),
      path.resolve(__dirname, 'src/assets/imgs/static/*.gif')
    ],
    path.resolve(__dirname, 'src/assets/imgs/other/'),
    {
      use: [
        imageminMozjpeg(),
        imageminOptipng(),
        imageminGifsicle()
      ]
    }
  );

    console.log('圖片優化完成!');
})();

最後,在package.json文件中添加如下的npm命令:

...
"scripts": {
    ...
    "img": "node optzing-img.js"
  },
...

在控制輸入npm run img,然後按下回車鍵就可以得到你所需要的。

注:既然重新定義了圖片優化的npm腳本命令,那麼,是否需要去掉之前在webpack.config.js中的image-webpakc-loader,當然不需要。主要有兩個原因,一個是sprite雪碧圖它是用幾張小圖片合成了一張大圖片,這張合成的圖片還需要優化;另一個是因爲本項目對於存放圖片的目錄進行了細化。

字體

在web開發中,自定義的字體也是比較常見的,在webpack中它的處理和圖片類似,都是使用的 url-loaderfile-loader。參考代碼如下:

...
{
  include: path.resolve(__dirname, 'assets/fonts/'),
  test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
  use: [{
    loader: 'url-loader',
    options: {
      limit: 10000,
      name:  isDev ? '[name].[ext]' : '[name].[hash].[ext]',
      outputPath: 'assets/fonts/'
    }
  }]
},
...

新創建了一個fonts目錄又來存放項目開發過程中使用的字體。

源代碼

webpack4.x multi-page

此後,webpack構建多頁面應用系列文章的源代碼,都在這個github項目中,webpack3.x multi-page不再維護。

構建多頁面應用系列文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章