Webpack 項目優化雜記

本項目源碼均可在 這裏 找到。

之前公司的官網項目靜態文件都是放在靜態服務器中,這其中的弊端就不贅述了。簡單說一下 CDN 的好處:

CDN 可以解決因分佈、帶寬、服務器性能帶來的訪問延遲問題,適用於站點加速、點播、直播等場景。使用戶可就近取得所需內容,解決 Internet 網絡擁擠的狀況,提高用戶訪問網站的響應速度和成功率。控制時延無疑是現代信息科技的重要指標,CDN 的意圖就是儘可能的減少資源在轉發、傳輸、鏈路抖動等情況下順利保障信息的連貫性。CDN 就是扮演者護航者和加速者的角色,更快準狠的觸發信息和觸達每一個用戶,帶來更爲極致的使用體驗。

上傳腳本文件

這裏爲了最小化演示路徑,我們只上傳項目所用的 js 文件。且 CDN 基於七牛雲。

// webpack.config.js
module.exports = {
  // 入口文件
  entry: './src/app.js',
  // 打包出口
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'app.js',
    publicPath: 'http://example.clouddn.com/example/' // CDN 地址
  }
}

output.publicPath 中可以配置我們的 CDN 地址(這裏你要有一個七牛雲的 賬戶 )。之後我們引入 qn-webpack

plugin 配置見該 plugin 源碼即可。這裏有一份示例配置:

// webpack.config.js
const qiniuWebpackPlugin = new QiniuWebpackPlugin({
  accessKey: '七牛雲個人面板 > 密鑰管理 > AK',
  secretKey: '七牛雲個人面板 > 密鑰管理 > SK',
  bucket: '對象存儲空間名',
  path: '空間內保存路徑',
  exclude: /index\.html$/ // 需要排除上傳的文件
});

module.exports = {
  // 加載插件
  plugins: [ qiniuWebpackPlugin ]
};

現在運行:

npm run build # webpack --mode=production

屏幕快照 2018-12-05 下午12.25.54.png

在終端中我們已經可以看到我們的 js 文件被上傳至自己的 CDN 服務中。通過 html-webpack-plugin 我們引入 js 的路徑也相應的替換成了 CDN 地址。

上傳圖片

很多時候,在我們的項目中,需要去:

import url from './your/img/path';

<img src={url}>

圖片掛上 CDN 也是很有必要的,圖片視頻文件之類的東西本身就比較大而且可以算是靜態的內容,從動態服務器上分離出去,可以極大的減小服務的負載。特別像是七牛雲這樣擁有圖片處理引擎的服務商,我們還可以通過 imageView2 來處理上傳至 CDN 的圖片。

說回圖片上傳,首先我們引入 file-loader ,Webpack 配置如下:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(gif|png|jpe?g|svg|webp)$/i,
        use: [
          {
            loader: 'file-loader',
            options: {
              // publicPath: 默認 `__webpack_public_path__` 爲 `output.publicPath`
            }
          }
        ]
      }
    ]
  }
}

file-loader 會指示 Webpack 將所需的對象作爲文件引入並返回其公共 URL 。

再次運行:

npm run build # webpack --mode=production

屏幕快照 2018-12-05 下午2.22.44.png

兩個文件已經被上傳至 CDN ,且路徑被替換。但是, 2.74 MiB 與後面的 [big] 很是鮮豔,下一步我們需要壓縮圖片以減少文件體積。

處理圖片

之前我們可以看看 Chrome 控制檯:

屏幕快照 2018-12-05 下午2.29.06.png

2.7 MiB 的圖片就算放在 CDN ,請求時間也超過了 100ms,那麼我們開始處理圖片。

引入 image-webpack-loader 並開始配置 Webpack :

//webpack.config.js
module.exports = {
  module: {
    rules: [{
      test: /\.(gif|png|jpe?g|svg|webp)$/i,
      use: [
        {
          loader: 'file-loader',
          options: {
            // publicPath: 默認 `__webpack_public_path__` 爲 `output.publicPath`
          }
        }, {
          loader: 'image-webpack-loader',
          options: {
            mozjpeg: {
              progressive: true,
              quality: 65
            },
            optipng: {
              enabled: false,
            },
            pngquant: {
              quality: '65-90',
              speed: 4
            },
            gifsicle: {
              interlaced: false,
            },
            WebP: {
              quality: 75
            }
          }
        },
      ],
    }]
  }
}

詳細配置參見 文檔

再次打包:

屏幕快照 2018-12-05 下午2.51.53.png

發現圖片大小從 2.74 MiB -> 1.16 MiB 雖然還是有點大,但是已經有很明顯的大小變化。再來看看 Chrome 控制檯:

屏幕快照 2018-12-05 下午2.56.18.png

效果很明顯 140 ms -> 43 ms

WebP

在進行這一步之前,需要簡單瞭解一下什麼是 WebP 格式圖片。

目前移動端 Android 4.0 以上、PC 端 Chorme 10+(14 ~ 16 有渲染bug)、Opera 11+ 、Safri 均支持 WebP 格式圖片。 WebP 與 jpg 相比較,編碼速度慢 10 倍,解碼速度慢 1.5 倍,而絕大部分的網絡應用中,圖片都是靜態文件,所以對於用戶使用只需要關心解碼速度即可。但實際上,webp 雖然會增加額外的解碼時間,但是由於減少了文件體積,縮短了加載的時間,實際上文件的渲染速度反而變快了。

搬運知乎上的一張圖片:

9bfba760f53916e6a8a8c2458e0b1c36_hd.jpg

所以我們可以得出結論:WebP 體積大幅減少,圖片質量也有保障,除了兼容性不太好。

對於兼容性,我們可以看這張圖:

屏幕快照 2018-12-05 下午3.05.08.png

考慮到兼容性的問題,我們之後會進行專門處理。現在第一步則是轉化 png & jpg -> Webp

這裏選用 gulp 作爲轉化圖片爲 WebP 的自動化構建工具。Webpack 社區插件看 這裏,我這裏選用 gulp 作爲構建工具也是希望自己能夠控制 WebP 圖片的製作,在本地生成而不是 CI 打包時(時間有點長... 。

//gulpfile.js
const gulp = require('gulp');
const webp = require('gulp-webp'); // 基於 cwebp 的 gulp 插件

// 基於 cwebp 轉化圖片
gulp.task('webp', () =>
  gulp.src('src/img/*.{png,jpg,jpeg}')
    .pipe(webp({ quality: 75 })) // 詳情配置見:https://github.com/imagemin/imagemin-webp#api
    .pipe(gulp.dest('src/img'))
);

// 監聽文件夾變化
gulp.task('watch', () =>
  gulp.watch('src/img/*.{png,jpg,jpeg}', ['webp'])
);

gulp.task('default', () =>
  gulp.start('watch')
);

配置之後我們運行打包可以比較一下:

  • 使用 WebP 格式前

    屏幕快照 2018-12-05 下午3.32.12.png

    屏幕快照 2018-12-05 下午2.56.18.png

  • 使用 WebP 格式後

    屏幕快照 2018-12-05 下午3.31.58.png

    屏幕快照 2018-12-05 下午3.34.34.png

最後我們在項目中使用 WebP 時候,需要判斷一下瀏覽器是否支持 WebP 格式圖片:

const canUseWebp = (() => {
  return document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') > -1;
})()

// 如果可以使用 WebP ,則給頂部元素加上一個 class
if (canUseWebp) {
 document.getElementsByTagName('body')[0].className += ' webp';
}

將這段 js 內聯在 head 標籤中後,我們可以利用 CSS 預處理器來判斷是否需要使用 WebP 格式圖片。

// stylus
bg($url, $type)
  background-image url($url + $type)
  .webp & // 如果擁有 .webp 類名,則使用 WebP 格式圖片
    background-image url($url + '.webp')
// less
.mixin(@url, @type) {
  background-image: url(@{url}.@{type});
  .webps & {
    background-image: url('@{url}.webp');
  }
}
// scss
@mixin bg($url, $type) {
  background-image: url($url + $type);
  @at-root(with: all) .webps & {
    background-image: url($url + '.webp');
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章