webpack基礎配置

1. webpack是什麼?

一個現代的javascript應用程序的靜態模塊打包器。當webpack處理應用程序時,會根據入口文件等引入的地方造成一個依賴關係圖,其中包括應用程序須要的每一個模塊,而後將這些模塊打包成一個或多個bundle。

官方圖形象的描述了不一樣的格式資源經過webpack最終打包成經常使用的瀏覽器可識別的資源。javascript

2. webpack的做用?

2.1 模塊化能簡單引入並使用某個被依賴的模塊的功能
2.2 處理打包javascript的文件
2.3 能夠將ES6轉化爲瀏覽器可識別和兼容的語法
2.4 經過loader轉化後可打包處理非javascript資源文件
2.5 能提取共用的模塊化功能打包後產生獨立的一個或多個文件
2.6 搭建開發環境開啓本地服務器、監視文件改動,熱替換
2.7 合理的長效緩存等...css

3. webpack主要的配置?

3.1 entry

用來告訴webpack應該使用哪一個模塊,來做爲構建依賴圖的起點html

module.exports = {
  // 單個入口文件時,若是未命名則默認打包後名爲main.js
  entry: './src/index.js'
  // 打包生成 app.js
  entry:{
     app : './src/index.js'
  }  
  // 多入口路徑文件 打包後會生成app.js和main.js
  entry:{
     app: './src/index.js',
     main: './src/main.js'
  }
}

3.2 output

用於指示webpack打包後文件存放的路徑和文件命名,設置靜態資源前綴路徑等vue

const path = require('path');
 module.exports = {
   entry:{
     app:'./src/index.js'
   },
   output:{
     path: path.resolve(__dirname,'dist'),
     filename: 'js/[name].[contenthash:8].js',
     /* 
     * 能夠理解爲配置的基礎路徑
     * 打包後以前的源碼資源會根據這個配置生成新的訪問路徑
     * 好比'/app.js' => https://cfile.xiaoman.cn/app.js
     */
     publicPath:'https://cfile.xiaoman.cn'
   }
   /* 這個module部分會描述**
   module: {
     rules: [
      {
        test: /\.vue$/,
        use: [{
        loader:'vue-loader'
        }]
      }
    ]
  }

示例demo的目錄結構和其餘文件
image.png
image.png
image.pngjava

根據上面配置如今全部的資源所有打包在app.js這個文件中,詳情以下圖
image.pngnode

隨着不停的迭代項目會愈來愈大,若是將所有文件打包到同一個文件意味着用戶僅瀏覽一個頁面要下載全部頁面的資源(針對單頁面應用),這種方式大大的下降首屏渲染速度。那麼減小首次資源加載和沒必要要資源的加載是必不可少的。
則動態按需加載或者共用部分不要重複打包到每一個使用到的文件中,經過將公用部分資源分離彷佛能夠解決這個問題。jquery

const path = require('path');
module.exports = {
  entry:{
    app:'./src/index.js'
  },
  output:{
    path: path.resolve(__dirname,'dist'),
    filename: 'js/[name].[contenthash:8].js',
    chunkFilename: 'js/[name].[contenthash:8].js',
    publicPath:'https://cfile.xiaoman.cn'
  },
  /* 這個module部分會描述**/
  module: {
    rules: [
      {
       test: /\.vue$/,
       use: [{
       loader:'vue-loader'
       }]
     }
    ]
 }

根據上面的配置,在其餘文件異步加載某個文件,打包狀況以下,將異步加載的文件單獨造成一個文件。這時候app.js的字節數下降了,而且只有用戶須要加載這個資源纔會當即被加載
1.非命名式動態加載文件
以上配置便可完成
更改後的index.js文件(將路由改成動態加載)
image.pngwebpack

能夠看到打包後單獨生成了0.2711cda5.js文件
image.pnges6

2.命名式動態加載文件
這種比較清晰知道文件的來源,可是每一個文件這樣命名較爲麻煩web

/*
*  在map.json文件中我隨便導出了一些json數據,僅供打包演示
*  如下是util.js文件的代碼
*/
export const getMap = ()=> import(
  /* webpackChunkName: "async-database-map"*/  
  './map.json').then(module => { return module.default  
})

這是目錄結構和Add.vue文件的代碼
image.png

打包以後就多了一個async-database-map.3a0b70bc.js文件
image.png

3.3 module

如今在Add.vue中加上樣式。開始打包...
image.png
發現webpack會報錯,大概意思說它不認識css,須要使用loader轉化一下從而讓wepback能夠處理css

那麼咱們知道了module主要是把每一個須要轉化的類型的模塊使用loader進行特有的處理。好比webpack自己不支持處理非javacript類型的文件。則能夠經過vue-loader、css-loader轉化爲webpack能夠處理的文件。這樣就能夠寫vue、css文件。固然字體、圖片等均可以,須要在moudle中進行另外配置便可。

/*
* webpack.config.js 文件,其餘配置同上
* 僅展現module配置
*/
module.exports = {
  module: {
    // 不須要解析的文件名或路徑
    noParse: \[/iconfont.js/\],
    // 解析文件的規則
    rules:[
      {  
        test: /\.vue$/,  
        use: ['vue-loader'] 
      },
      {
        test:'/\.js$/', // 被解析的文件類型
        ues: [
         /*
         * 將es6語法能夠轉話成瀏覽器可識別的es5語法
         * 在根目錄下添加 babel.config.js並配置如何轉換便可
         * 若是用了babel-loader webpack默認會使用
         * babel.config.js做爲babel-loader的配置選項
         * 固然還有其餘方式好比.babelrc,詳見[babel官網](https://www.babeljs.cn/docs/configuration)
         */ 
         'babel-loader',
         'thread-loader'
        ],
        include: [ 
          /* 需包括的非當前webpack配置所在的根目錄下的路徑
          *  並不是一個大項目的根目錄,由於一個項目下可能有多個 
          *  子項目
          */
          resolve('common/js')
        ],
        // 不包括的需解析轉換的目錄或文件
        exclude: [
         /node_modules\/(?!amumu)/
        ]
      },
      {  
        test: /\.css$/,  
        use: ['style-loader','css-loader']  
      }
    ]
  }
}

配置以後打包成功,而且es6也被babel-loader轉成瀏覽器可識別的es5語法
image.png

3.4 resolve

模塊解析,主要用於幫忙找到文件的依賴模塊的絕對路徑,使其可以被正確解析。

/*
* webpack.config.js 文件,其餘配置同上
* 僅展現resovle配置
*/
module.exports = {
  resolve:{
    /*
     * 表示js、vue、json後綴的文件再被引入到其餘文件時可省略後綴
     * 名也能夠找到,如不寫在引入時不能省略後綴名
     */
    extensions:['.js', '.vue', '.json'],
    /*
     * 別名,import中的@crm在打包時將自動換成配置的別名路徑
     */
    alias: {
     '@root':resolveRoot('./'),
     '@crm':resolveRoot('crm/src'),
    }
  }  
}

3.5 externals

因爲某些包不須要打包到bundle中,而是在運行時再去從外部獲取這些擴展的資源。好比經過cdn路徑獲取jQuery,就無需將jQuery打包到bundle中了。

// 在index.html中 經過cdn去引入jQuery
<script
  src="https://code.jquery.com/jquery-3.1.0.js"
  crossorigin="anonymous">
</script>
// webpack.config.js
 module.configs = {
   exterals: {
     jquery: jQuery
   }
 }

上面的則表示在模塊經過import依賴jquery時,jQuery會被當作全局變量去替換被使用到的地方。
固然在咱們現有的系統中,並未經過cdn的形式去直接引入exterals中配置的外部依賴。而是經過gulp將某些配置的基礎依賴包打包在vendor.base.js(見gulpfile.js)中,並直接在index.html中引入。

3.6 optimization

webpack 4新的項目打包優化配置屬性,有些屬性是用來替代webpack.
optimize.xx下的某些插件。

/*
* webpack.config.js 文件,其餘配置同上
* 僅展現optimization配置
*/
module.exports = {
  optimization: {
    /*
    * 告訴webpack使用TerserPlugin或者在minimizer屬性中的插件
    * 來最小化 要輸出的bundle
    * 默認只處理 js 文件
    */
    minimize: false
    /* 配置經過那個插件和如何最小化bundle */
    minimizer:[new TerserPlugin()]
    /* 
    * 將運行時每一個模塊之間的映射關係單獨分離成一個文件 
    * 主要記錄了在運行時模塊之間的運行和映射關係
    * [關於runtime與manifest官方解析](https://www.webpackjs.com/concepts/manifest/)
    */
    runtimeChunk:{
      name: 'runtime'
    },
    /*
    * 分離打包模塊
    * 將知足test的文件單獨分離成一個文件或多個文件,不與入口文件
    * app.js 打包在一塊兒
    */
    splitChunks: {
      cacheGroups: {
        vendor: {
          chunks:'initial',
          name:'vendor',
          priority:0,
          test: /\/node\_modules\//,
          enforce:true,
        },
        styles:{  
          name: 'styles',  
          test: /\.css$/,  
          chunks: 'all',  
          enforce: true,  
        }
      }
    }

image.png
這裏就清晰的記錄了,node_modules模塊和styles與app.js分離,自身單獨生成文件,又大大的減小了app.js的字節數,加快首屏渲染時間。
若是沒有分離,則樣式更新或者vue中的其餘部分更新都會使得整個文件的緩存失效。分離後css和邏輯js部分徹底獨立,更改css不會使js部分失效,同理亦然。這樣更好的利用了緩存機制,減小沒必要要未更新部分的下載。

3.7 plugins

用於自定義webpack的構建過程。webpack有內置插件,能夠經過webpack.[plugin-name]使用這些插件。固然npm社區有更多的插件。

// webpack.config.js 文件,其餘配置同上
module.exports = {
   plguins: [
    /*
    * 在全局中定義某些變量,使得在項目任意處可訪問自定義的變量
    */
    new webpack.DefinePlugin({
        'process.env':{
            PROJECT_NAME:'"crm"',
            NODE_ENV:'"production"',
            ENV:'"production"'
        },
    }),
   ]
}

3.8 webpack-dev-server

開發時建立本地服務器,可經過配置的網址和端口號進行訪問。經過webpack-dev-server生成的bundle運行於項目根目錄,這個文件並無存到物理磁盤上,而是在內存中。當咱們更改某些文件內容時會自動編譯出最新的結果,可以更快的看到效果,加快開發速率。
安裝 webpack-dev-server 依賴, 在package.json中配置start腳本
image.png

// webpack.config.js 文件,其餘配置同上
  module.exports = {
    devServer:{
      host: 'http://localhost:8081',
      port: 8001,
      // 開啓熱更新
      hot: true, 
      /*
      * 將dist目錄的文件做爲可訪問文件。通常想要提供靜態文件時才須要
      */
      contentBase: './dist',  
      /*
       * 綁定特定的基礎目錄
       * 好比訪問某個bundle.js
       * 其完整路徑應是 http://localhost:8081/assets/bundle.js
       */
      publicPath: '/assets', 
    }
  }

運行後輸出以下信息,直接在瀏覽器便可訪問到
image.png

4 常見的插件的做用

1.VueLoaderPlugin
專門用於對vue文件的打包,雖然在module中對vue文件使用了vue-loader, 還須要手動在插件處調用才行。能夠本身定義參數,但最新的參數中已經廢棄了在vue插件中定義css樣式的加載器。

module.exports = {
   plguins: [
    new webpack.DefinePlugin({'同上省略'}),
    new VueLoaderPlugin(),
   ]
}

image.png

2.AssetsPlugin
生成webpack-assets.json文件,記錄用到css和js的文件名,用來作緩存破壞。
能夠自定義配置生成路徑、文件名或只記錄入口文件css和js文件名、元信息等

module.exports = {
  plugins:[
    new AssetsPlugin({
        /* 是否輸入到配置的編譯路徑處 */
       useCompilerPath:true,
        /* 輸出文件中只包含入口文件及其關聯塊用到的css和js文件名 */
       entrypoints:true,
       /* 爲true時只在webpack-dev-server運行時在內存中生成,不寫入磁盤 */
       keepInMemory:process.env.NODE_ENV!=='production',
       metadata: {
       build: {
          buildTime:format(newDate(), 'yyyy-MM-dd HH:mm:ss'),
        },
      },
    })
  ]
}

3.webpack.DefinePlugin
上面描述了(詳見3.4),用來定義全局變量,使得在項目中任意文件可以使用這個變量名

4.webpack.NamedModulesPlugin
給入口文件和已命名的異步打包文件以外的chunk進行命名。好比路由中動態加載的文件。

constroutes = [
  { path:'add', component: ()=>import('./Add.vue')},
]

若是不使用這個插件webpack會自動命名爲 [id].[contenthash:8]
(根據上面的chunkFilename的配置會生成hash後綴).

另外根據本身的需求命名文件

module.exports = {
   plugins: [
      new webpack.NamedChunksPlugin(chunk => {
        if (chunk.name) {
          returnchunk.name
        }
        consthash = require('hash-sum')    
        const joinedHash=hash(Array.from(chunk.modulesIterable, m =>m.id).join('_'))
        return `chunk-`+joinedHash
     })
   ]
 }

image.png
可與3.2中生成的bunlde進行對比可發現以0.2711cda5.js命名的文件變爲
chunk-226123c6.eef091ca.js了

5.HtmlWebpackPlugin
由於項目都有一個入口的html文件,這個文件會負責引入並加載js/img等等靜態資源從而顯示到瀏覽器。但咱們打包的js名是由文件名+hash組成。若是在html中引入的js名能自動根據編譯結果進行變化就省事很多。這個插件正好解決此問題。

module.exports = {
    plugins: [
       /* 默認會輸出到配置的output路徑下 */
       new HtmlWebpackPlugin({
         // 生成的文件名
         filename:'index.html',
         // 以哪一個文件爲模板來進入js等打包文件的修改和注入
         template:'index.html',
       })
    ]
  }

打包的bundle截圖
image.png

生成的index.html自動引入了webpack打包的入口js文件,無需每次打包完成後手動去引入
image.png

6.inlineManifestWebpackPlugin
這個插件的做用是把單獨打包的關於記錄manifest信息的js文件(默認名:runtime.js)的內容之內聯方式放在入口html(默認名:index.html)中。由於index.html每次打包都會變更,記錄manifest的文件每次也會變更則這個文件的緩存是沒用的。若是記錄manifest的文件的內容內聯到index.html就能夠減小一個請求。更好的進行長效緩存。

module.exports = {
  optimization:{
    runtimeChunk:{
      name:'runtime',
    }
  },
  plugins:[
     new HtmlWebpackPlugin({
       filename:'index.html',
       template:'index.html',
     }),
    /* 
    * 1.首先要把記錄manifest信息的文件單獨打包
    * 2. 搭配HtmlWebpackPlugin一塊兒使用,並在後面配置
    * 3. 傳入的名字與 optimization中配置的分離manifest文件名一致,默認是runtime
    */
    new InlineManifestWebpackPlugin('runtime')
  ] 
}

image.png

7.CopyWebpackPlugin
顧名思義,拷貝文件。 將某些文件從一個地方拷貝到另外的地方。

module.exports = {
   plguins: [
    new webpack.DefinePlugin({'同上省略'}),
    new VueLoaderPlugin(),
    new CopyWebpackPlugin([
      {
        /* 具體根據本身的項目目錄 */
        from:'../static',
        to:'./dist/static',
        ignore: ['.*'],
      },
    ]),
   ]
}

8.MiniCssExtractPlugin
將樣式從內嵌style的文件中分離出來並造成單獨的文件。好比xx.vue文件中有些樣式,就能把這部分樣式抽出來。這樣作,若是xx.vue中的非樣式部分修改,不會使得樣式文件的緩存實現。各自獨立,不會相互影響,可減小請求http資源的數量。
image.png

這裏配置遇到一個坑,在mode設置爲'development'分離徹底沒問題,改成'production',分離以後的css文件就不見了。通過調查該插件有把css分離出來,只是webpack在tree sharking時認爲無反作用就將生成的css搖了。

由於最新生成的package.json中包含了 sideEffects: false這個屬性,即認爲是無反作用的。

生產環境webpack默認sideEffects:true 讀取packages.json的標識
來判斷模塊或包有/無反作用

則webpack在打包檢查被分離css文件時並未找到以import和export導入導出的形式,認爲無反作用直接搖掉。

webpack關於tree sharking部分定義以下:
image.png

9.OptimizeCSSAssetsWebpackPlugin
壓縮css,能夠跟上比較,字節從63 bytes -> 44 bytes,明顯減小。
image.png

10.FriendlyErrorsWebpackPlugin
能夠識別某些類的網頁包錯誤,並對它們進行清理、聚合和排序,以提供更好的開發人員體驗。

image.png

在未配置前打包時會輸入某些包時的輸入信息,大可能是無用的信息,咱們但願控制檯能輸入有效且乾淨的信息。

module.exports = {
  devServer:{
    contentBase:'./dist',
    hot:true, // 是否啓用熱更新
    /*
    * 啓動以後,只會在控制檯打印初始信息,不會打印其餘信息。意味關閉了錯誤和其餘提示
    * 千萬要配置這個屬性,不然FriendlyErrorsPlugin不起做用
    */
    quiet: true, 
  },
  plugins:{
    /*
    * 因此須要使用這個插件在編輯成功/報錯是進行提示
    */
    new FriendlyErrorsPlugin({
      compilationSuccessInfo: {
         messages: [`good job compile succ`],
        },
      onErrors:function(){
        console.log(`compile faild, check you project`)
      },
      clearConsole:true,
    }),
  }
}

編譯成功時清空了控制檯,運行compilationSuccessInfo,執行自定義內容。
image.png

編譯失敗時明確告訴是個文件哪一行編譯錯誤,方便快速定位。
image.png

插件的世界多而廣,只簡單介紹幾種項目中用到的比較多的插件。可根據項目的須要在npm社區中尋找合適的插件。

5 結語

這裏描述了一個基本的webpack使用以及webpack基本屬性。實際項目遠遠不止這些配置,也不會如此簡單。可能涉及到長效緩存、本地開發優化、首屏加載速度、文件名變動等等一系列的問題。加油吧!

6 參考

webpack官方文檔
手摸手,帶你用合理的姿式使用webpack4(下)
帶你用合理的姿式使用webpack4(上)
處理打包文件體積過大的問題
webpack4---生產環境css樣式丟失問題

相關文章
相關標籤/搜索