webpack簡單使用與優化

1、使用

關於webpack的使用,在這借鑑官方文檔,作一些簡單總結。css

webpack4之後會有默認的配置體系,簡單來講項目根目錄下默認會有個webpack.config.js的配置文件,其中入口文件爲src/index.js,打包出口文件夾爲dist,通常來講咱們平時項目也是按這個規則來的。html

會有首先看一下一個簡單的webpack基本配置結構node

{
    entry:'./src/index.js',
    output:{
        path: __dirname+'/dist',
        filename: '[name].js'
    },
    mode:'production',
    module:{},
    plugins:[]
}

1.1 入口

entry,指定入口文件,地址是以項目爲根節點的相對路徑的字符(以單頁面爲例,只有一個入口)react

1.2 輸出

output,指定輸出,參數是一個對象,其中path指定輸出的文件夾,地址須要使用絕對路徑webpack

1.3 模式

mode,通常是兩個值'developement'和'production',做用用來標記對應開發和生產環境,同時webpack會根據值不一樣進行一些默認配置,好比production會壓縮代碼;同時,在開發或者打包過程當中,會在Node的模塊process中把值存進變量process.env.NODE_ENV中供使用。web

1.4 模塊

module,由於webpack是基於Node,自己能運行識別js文件,因此其它模塊,如css、圖片等經過配置module中的loader來加載這些文件,同時藉助loader也能夠編譯處理ES六、TS等上層語法、模板。json

1.5 插件

plugins,如文檔所說,插件是webpack的支柱功能,目的在於解決loader沒法實現的事。好比常見的UglifyJsPlugin壓縮代碼、CopyWebpackPlugin複製靜態資源、HtmlWebpackPlugin使用html模板等等瀏覽器

2、優化

前面介紹的webpack的簡單使用,官方文檔很詳細就一帶而過了。接下來總結下webpack優化相關的東西,優化這部分,我把它分爲三個部分:代碼風格、構建效率、加載性能

2.1 代碼風格

開發單頁面時,都會配置一套開發環境和一套打包操做,而後你有一個朋友就是我係列通常會有三個配置文件;緩存

  • webpack.base.js:基礎配置
  • webpack.dev.js:開發環境配置
  • webpack.pro.js:打包配置

webpack.base.js公共配置部分,而後生產和打包配置經過merge的方式擴展。服務器

package.json的scripts中配置命令行;

  • "dev":"webpack --config webpack.dev.js",
  • "build":"webpack --config webpack.pro.js"

這樣的確也很清晰,也沒問題。可是後來我使用React腳手架create-react-app,看了一下配置文件以爲更加合理。

首先它並無在命令行中使用webpack,這樣就省去安裝webpack-cli

create-react-app中的package.json的scripts中配置命令行;

  • "start": "node scripts/start.js",
  • "build": "node scripts/build.js"

項目中寫好腳本直接使用node執行,配置文件比較多就模擬主要內容

// webpack.config.js
module.exports = function(webpackEnv){
    const isEnvDevelopment = webpackEnv === 'development';
    const isEnvProduction = webpackEnv === 'production';
    return {
        // ...
        mode: webpackEnv,
        // ...
        plugins:[
            new pulicPlugin(),
            isEnvDevelopment && new Plugin1(),
            isEnvProduction && new Plugin2(),
        ].filter(Boolean)
        // ....
    }
}

webpack.config.js導出一個函數,指定環境做爲參數,而後經過參數進行定製,返回一個webpack的配置參數,start.jsbuild.js主體內容也大概能夠猜測到了

// start.js
const configFactory = require('webpack.config');
const webpack = require('webpack');
const config = configFactory('development');
const complier = webpack(config);
return new Promise((resolve, reject) => {
    compiler.run((err, stats) => {
      let messages;
      if (err) {
        if (!err.message) {
          return reject(err);
        }
        // ....
      }
    // .....
    })

首先經過傳參方式能夠省去安裝webpack-merge,並且原來使用的方式,只執行到webpack編譯,而腳手架還補充了編譯後的統計分析。

2.2 構建效率

  • 一、dllPlugin

dll意思是動態連接庫,在webpack的主要思想就是將一些第三方庫打包分離出來,只要第三方庫不須要升級,每次項目啓動或者打包時就不須要重複打包這些庫了。

具體用法以react項目爲例,在config新增一個webpack.dll.config.js配置

const webpack = require('webpack');
const path = require('path');
module.exports =  {
    entry: {
        vendor:[
            'react',
            'react-dom',
            'react-router-dom'
        ]
    },
    output: {
        path: path.resolve(__dirname,'../dist'),
        filename: '[name].js',
        library: '[name]_[hash]',
    },
    plugins: [
        new webpack.DllPlugin({
            context: __dirname,
            name: '[name]_[hash]', //須要與output.library相同
            path: path.resolve(__dirname, '../dist/[name]-manifest.json'),
        })
    ]
}

package.json增長命令行"build:dll": "webpack --config config/webpack.dll.config.js --mode production"

執行命令後會在dist文件夾下生成vendor-manifest.jsonvendor.js兩個文件,vendor.js也就是分離出的靜態資源,而vendor-manifest.json一個清單文件來描述資源的來源路徑。

分離完成後就是經過DllReferencePlugin供項目使用了,在webpack.config.js中添加配置

new webpack.DllReferencePlugin({
  context: __dirname,
  manifest: require('../dist/vendor-manifest.json'), //指定dll描述的位置
}),

這樣經過dll分離就完成了,這時候還差一步,咱們運行項目發現html並無引入vendor.js,這裏須要手動更新html模板,或者使用插件add-asset-html-webpack-plugin添加。

const AddAsssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
//....
plugins:[
  // ...
  new AddAsssetHtmlWebpackPlugin({
    filepath: path.resolve(__dirname, '../dist/*.js'),
    hash:true, //附加hash到文件名後,防止緩存
    outputPath: 'static/js', //
    publicPath: '/static/js'
  })
  // ....
]

這樣完成後的項目就能夠正常運行了。

  • 二、HappyPack

happypack提高webpack打包速度,本質上happypack是用經過js的多進程來實現打包加速。

const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
// 引入happypack,ThreadPool線程池使用系統核心數
module.exports = {
  // ....
  module: {
    rules: [
      {
        test:/\.js$/,
        use:[
          {
            // loader:'babel-loader',
            // options:{
            //  ....
            // }
            loader:'happypack/loader?id=happyBabel', //在loader中添加觸發happypack,將原來的配置轉移給happypack
          }
        ]
      }
    ]
  },
  plugins:[
    new HappyPack({
      id:'happyBabel', //id與loader中配置的id參數相同
      threadPool: happyThreadPool,
      verbose: false, //是否輸出過程日誌
      loaders:[
        {
          loader:'babel-loader', 
          options:{
            ....
          }
        }
      ]
    })
  ]
}

2.3 加載性能

  • 一、Gzip
HTTP協議上的GZIP編碼是一種用來改進WEB應用程序性能的技術。大流量的WEB站點經常使用GZIP壓縮技術來讓用戶感覺更快的速度。這通常是指WWW服務器中安裝的一個功能,當有人來訪問這個服務器中的網站時,服務器中的這個功能就將網頁內容壓縮後傳輸到來訪的電腦瀏覽器中顯示出來.通常對純文本內容可壓縮到原大小的40%.這樣傳輸就快了,效果就是你點擊網址後會很快的顯示出來.固然這也會增長服務器的負載. 通常服務器中都安裝有這個功能模塊的。

以上是引用的一部分介紹,如今主流的瀏覽器都支持Gzip,簡單來講Gzip在本來打包壓縮後的基礎上再壓縮一次,Gzip的主要原理就是將重複的字符片斷臨時替換來減少文件大小。

如何查看?

瀏覽器客戶端的http請求頭中的有一個Accept-Encoding字段,告訴服務器接受的編碼格式,它的值中包含gzip就說明瀏覽器支持解析Gzip的返回;若是服務器開啓且返回了Gzip的響應,這時響應頭會有個的content-encoding字段,內容爲content-encoding:gzip。也就是響應在服務端壓縮成Gzip,而後客戶端再解壓使用。

如何使用?

create-react-app腳手架爲例,打包完成後控制檯輸出如下一些內容。
build.png
文件大小提示是使用Gzip壓縮後的大小,咱們審查真實文件大小確實是要大很多,並且Gzip文件後綴名是gz。對於靜態資源的Gzip,這裏須要在webpack中增長compression-webpack-plugin的配置。

const CompressWebpackPlugin = require('compression-webpack-plugin');
// 添加到plugins
new CompressWebpackPlugin({
    test: /\.(js|css)$/, // 壓縮js和css
    threshold: 10240, // 達到10k才啓用,資源太小不必,並且解壓也是有損耗
    minRatio: 0.6 //壓縮最小比例下限
    // 其餘參數使用默認
  })

完成配置後,打包後的代碼靜態資源知足以上條件的會多出一個之後綴爲.gz的gzip文件,客戶端配置也就完成了。當支持解析Gzip的客戶端請求服務端的靜態資源就會自動請求.gz的文件。壓縮效率,以antd爲例,完整的普通壓縮包體積會接近2M,但Gzip壓縮後體積會縮小爲不到600KB的大小,結合Gzip的原理,這種複用性強的UI組件壓縮效率確定會比較高。

靜態資源搞定了,服務端也能夠作一些優化。服務端使用Gzip主要是對一些接口返回的數據進行壓縮,以Koa爲例,安裝koa-compress

const compress = require('koa-compress')
const Koa = require('koa')
const app = new Koa()
app.use(compress({
  filter: function (content_type) {
      return /text/i.test(content_type)
  },
  threshold: 2048,
  flush: require('zlib').Z_SYNC_FLUSH
}))

使用demo的默認配置,完成後在接口響應值在知足配置條件下就會被壓縮成Gzip返回給客戶端了。

  • 二、按需加載

單頁面應用中的多路由,爲防止第一次加載頁面加載過多資源,將路由配置成按需加載。

一篇講述代碼分割的文章

react項目爲例,能夠藉助react-loadable,配置路由組件

import Loadable from 'react-loadable';

// 修改前配置,打包之後進入首頁就會加載相應資源
import oldViewComponent from './viewComponent';
<Route path='/view' component={oldViewComponent} />

// 修改後,路由跳轉/view纔會加載
const newViewComponent = Loadable({
    loader: () => import('./viewComponent'),
    loading: Loading //加載完成前顯示,通常用一個loading組件
})
<Route path='/view' component={newViewComponent} />
相關文章
相關標籤/搜索