webpack實踐總結

  1、Loader寫法及執行順序javascript

從webpack2起,loader的格式以下:css

 module: {
rules: [ {test:
/\.css$/, use: ['style-loader','css-loader']}, ] }
webpack1中的寫法以下:
module: {
        loaders: [
            {
                test: /\.css$/,
                loader: 'style-loaer!css-loader'
            }
        ]
}
無論採用哪一種寫法,須要記住的是loader的執行順序是從右往左

 2、根據模板生成html文件並自動插入腳本和樣式的地址html

使用html-webpack-plugin插件可達到這一目的,而且插入的文件地址會隨着output.publicPath的變動而變動。html-webpack-plugin插件與chunk概念緊密相聯vue

所謂的chunk無非就是代碼段,假設入口爲index.js,在index.js中導入了a.js和b.js,那麼打包出來就會造成一個name爲index的chunk,chunk的內容是這三個文件合起來的內容java

該插件不是webpack自帶的,須要先安裝到開發環境( npm html-webpack-plugin --save-dev),而後在webpack配置文件中有幾個關鍵的配置項:react

const htmlPlugin = require('html-webpack-plugin')
module.exports = {
    plugins:[
      new htmlPlugin({
        filename: '最終輸出的html文件地址',
        template: '模板文件地址',
        hash:false,
        chunks: ['index','common']
      })
    ]
  }

hash:爲true時在html中插入的文件地址格式會變爲xxx.js?a3c9073cfd20130af5e3,生成hash的主要做用是用來處理瀏覽器緩存jquery

chunks:可選屬性,要插入哪些chunk生成的文件,若是未指定,則把全部的chunk造成的文件都插入。webpack

在多頁面中一般每一個頁面都有一個js作爲入口文件,index.html使用index.js,user.html使用user.js,這裏有兩個入口,打包出來會造成兩個chunk,分別爲index和useres6

    entry:{
      index:rootPath+'/src/js/index.js',
      user:rootPath+'/src/js/user.js'
    },
    output: {
      filename: 'js/[name].js',
      path: rootPath+'/dist/'
    },

生成index.html時,咱們顯然不但願把user.js也引用進去,因此這時就必需要指定使用哪些chunkweb

若是要生成多個html,則須要在plugins數組中建立多個htmlPlugin實例

使用ExtractTextPlugin插件提取的css文件會自動添加到html中

指定了chunk時,若是使用CommonsChunkPlugin提取了公共代碼,若是未在配置項中添加該chunk的名字,則生成的html不會插入公共代碼文件

假設在html模板中已經手工插入了一些css和js文件,生成的文件會插入到這些文件以後

<link href="css/bootstrap.min.css" rel="stylesheet">xxxxxxxxx自動插入的連接地址在這裏**********

  3、未打包的樣式和腳本也想自動插入到html中  

在上面例子中咱們把打包生成的文件自動插入到了html中,那麼不參與打包的文件該怎麼處理呢?若是咱們採用手動插入方式,那麼若是在開發環境時不使用cdn,在生產環境要使用cdn時,不是得手工去改html中的連接嗎。

html-webpack-include-assets-plugin插件就是幹這事的,它是對html-webpack-plugin的加強
安裝好後須要使用const htmlIncludeAssetsPlugin = require('html-webpack-include-assets-plugin');導入模塊
而後在webpack.config.js的plugins中添加一個插件實例
      new htmlIncludeAssetsPlugin({
        assets: ['js/lib/jq.js'],
        append: false, //false,添加到html-webpack-plugin生成的連接以前,true爲以後
        hash:true, //是否在生成的文件地址後加hash,默認爲false
        publicPath:'' //自定義publicPath,若是省略或爲空則使用output項中指定的publicPath
      })

 該插件的實例會對全部的html-webpack-plugin實例起做用,也就是在全部生成的html中都插入相應的代碼,若是隻想在指定的html中追加,能夠指定一個file屬性

點我查看配置說明

4、項目使用jquery及插件的最佳實踐

不論是咱們以往的多頁面仍是vue、react這種mvvm框架中,都建議直接按以下方式使用:

1.在html中直接引入jquery及相應的插件,這樣子在代碼中就能夠正常使用jquery以及相應的插件了

2.若是須要在代碼中使用es6的模塊導出功能import $ from 'jquery'或者cmd方式加載var $ = require('jquery'),那麼你須要把$變量暴露給webpack,按以下方式進行配置

externals: {
    jquery: 'window.$'
}

externals的使用場景以下:當咱們想引用一個庫,而後想在代碼中使用cmd或es6的模塊引入,加可是又不想讓webpack打包,這時就可使用該配置項。

此步驟不是必須的,不使用es6 import或cmd require並不影響使用jquery及插件

3.若是是本身編寫的插件,可使用es6的import './jqPlugin/globalJq.js'方式導入,此方式會把globalJq.js的代碼打包進項目的js中

4.會把jquery打包到項目js中的幾種方式

  • 使用expose-loader
    step1:安裝到生產環境:npm install  jQuery expose-loader --save
    step2:在配置文件中添加一個loader
    {
        test: require.resolve('jquery'),
        loader: 'expose?jQuery!expose?$'
     }

     step3,在須要使用jquery的文件中導入模塊便可

    import $ from 'expose?$!jquery'
    import 'jquery-ui' //插件可用  

    此種方式在使用layer.js這個彈層庫時會出錯

  • 使用ProvidePlugin
    該插件是webpack自帶的,不須要單獨安裝
    step1:npm install jQuery --save
    step2:在webpack配置文件的插件選項中添加一個webpack.ProvidPlugin實例(注:webpack是在文件頭聲明的,const webpack=require('webpack'))
  • new webpack.ProvidePlugin({
      $: "jquery",
      jQuery: "jquery"
    }),

    step3:接下來就是直接使用$了,不須要再導入模塊

    import 'jquery-ui' //引入jquery-ui插件
    $('#app').text()

     

    該插件的做用是自動加載模塊。 當 使用未賦值的變量時, module 就會自動被加載,而且 identifier 會被這個 module 輸出的內容所賦值。上面的變量是$,對應的module是咱們安裝的jquery模塊。
    缺點是沒有全局上下文,$變量沒有聲明就可使用,當使用eslint時沒法驗證語法錯誤

5. 爲何推薦直接在html中引入jquery及相應的插件?

採用上面第4條中的作法,在打包時會出現一系列的問題,具體表如今:

1.若是多個文件都使用了jquery,那麼打包時,每一個文件中都會插入jquery庫的代碼,代碼重複,而後你會想到第2條的抽取公共代碼方法

2.使用CommonsChunkPlugin插件把多份代碼中都使用到的代碼提取爲獨立文件,這種作法在每次打包時都會計算哪些是公共代碼,項目大時找包速度很慢。

3.爲了解決打包性能,得改用DllPlugin和DllReferencePlugin插件,打包速度解決了,可是仍是得手動把xxx.dll.js插入到html中。(看不明白沒關係,後續會講到該插件)

看到了嗎,使用第4條中的方法繞來繞去最終仍是得在html中插入提取到的公共代碼或xxx.dll.js,作了一系列的工做,實際上等於脫了褲子放屁

 

5、css相關處理

在webpack中全部的東西都是模塊,css也不例外,只有將css視爲模塊,才能在webpack中進行打包和壓縮,才能自動插入到相應的html中,才能將css轉成js對象方式來使用(vue中有該技術)。

1.css-loader和style-loader

css-loader  主要的做用是把css看成模塊使用,而後就能夠在js中使用 import 'xxx.css'導入樣式表,而後參與打包

style-loader 主要的做用是把js中導入的css模塊插入到html中造成一個<style>xxxx</style>

step1:安裝模塊

npm install  style-loader css-loader --save-dev

step2:webpack中編寫loader

 
 
module: { rules: [ { test: /\.css$/, use: [
{ loader: 'style-loader'}, {loader: 'css-loader',options: { modules: true}} ] } ] }
 

 2.使用post-loader+autoprefixer自動爲css加前綴

某些css屬性,要爲不一樣的瀏覽器加不一樣的前綴,手動添加相應的前綴是個苦差事,post-loader能夠很好的解決這個問題,咱們只需寫一個正常的樣式

div
{
   transform:rotate(7deg);
}

使用post-loader後會自動轉成

div
{
transform:rotate(7deg);
-ms-transform:rotate(7deg);     /* IE 9 */
-moz-transform:rotate(7deg);     /* Firefox */
-webkit-transform:rotate(7deg); /* Safari 和 Chrome */
-o-transform:rotate(7deg);     /* Opera */
}

網上不少文章比較坑,會告訴你用autoprefixer插件或用post-loader插件,實際上兩個插件都須要安裝!

npm install  autoprefixer  post-loader --save-dev

3.使用less-loader來編譯.less文件

4.使用extract-text-webpack-plugin插件來提取style-loader向頁面中插入的樣式

使用前須要先安裝該插件,而後使用const ExtractTextPlugin = require("extract-text-webpack-plugin");//導入模塊,最後在plugins數組中建立一個實例

new ExtractTextPlugin('css/[name].css'),  

 

 使用name屬性時,從入口中index.js中抽取出來後的文件是index.css,固然也能夠指定爲一個固定的名字好比page,css

 當咱們指定css/xxx.css時,最終的生成路徑其實是webpack的output.path+'css/xxx.css'

5.相關的配置

注意use中的loader順序,不要寫反了

        {
          // 用正則去匹配要用該 loader 轉換的 CSS 文件
          test: /\.css$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", // 編譯後用什麼loader來提取css文件 use: ['css-loader?minimize=true','postcss-loader'] }) },

 

6.css壓縮處理

須要使用optimize-css-assets-webpack-plugin插件來專門處理css。

注意:對於webpack v3或更低版本,請使用optimize-css-assets-webpack-plugin@3.2.0。 optimize-css-assets-webpack-plugin@4.0.0及以上版本支持webpack v4。

若是你搜網上那些教程,通常會告訴你css-loader有壓縮屬性,又或者告訴你你在pageage.json中建立一個打包的scrpits命令,配置以下:

  "scripts": {
    "build": "webpack -p", },

 

假如你按他們的方法來打包,最後你發現你的css文件中的代碼仍是原封不動的,根本就沒有刪除空格,也沒有刪除註釋!

 
插件比較簡單,實例化時最經常使用的配置以下:
    new OptimizeCssAssetsPlugin({
      cssProcessor: require('cssnano'),
      canPrint: true
    })

 

  6、圖片資源的處理

主要使用url-loader和file-loader,網上看博文說url-loader是file-loader的升級版,能夠獨立使用,可是我在webpack2中使用時報錯了,安裝url-loader後仍是得安裝file-loader.

容易出錯的地方,請查看這篇

  7、實時編譯並查看運行結果(HRM)

主要使用webpack-dev-server,安裝好該包後,需在package.json中和webpack.config.js中進行配置。

首先在package.json的scripts節點增長一個命令,我習慣叫dev,而後在命令行中就可使用 npm run dev來啓動調試服務器了

  "scripts": {
    "dev": "webpack-dev-server --open --inline --hot
  },

 

open--參數表示啓動服務器後自動打開網頁

inline 和hot配合實現熱刷新,hot參數必須寫在這裏,在webpack.config中配置無效

接下來在webpack.config中增長一個devServer配置項:

  devServer: {
    contentBase:'dist', //網站文件目錄,通常指向打包輸出目錄
    historyApiFallback: true,//不跳轉
    progress: true
  },

更詳細的配置,點我

 

  8、如何處理sourcemap?

主要使用devTool選項,最經常使用的有如下幾種:

上述選項由上到下打包速度愈來愈快,不過同時也具備愈來愈多的負面做用,較快的打包速度的後果就是對打包後的文件的的執行有必定影響。

生成source map的目的是爲了方便調式,出錯時能定位到最原始的資源,無論該資源是react的jsx,仍是vue的單文件,所以使用eval選項並無意義,這也是在上表中沒有列出的緣由

那爲何也不使用inline-source-map選項呢?主要仍是由於打包速度慢,文件體積變大

總結:

在開發環境中咱們使用:cheap-module-eval-source-map

在生產環境中咱們使用:cheap-module-source-map。

  9、區分開發環境和生產環境

主要的工做有如下幾步:
1.寫一個各類環境都會用到的配置文件,假設叫webpack.common.js,再分別建各個環境的配置文件,例如webpack.dev.js和webpack.prod.js
2.各自的配置文件中,使用webpack自帶的webpack-merge函數來合併配置,而後使用webpack.DefinePlugin來定義在該環境下可供全局共享的變量,一個完整的配置例子以下:
const common = require('./webpack.common.js');
const merge = require('webpack-merge');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const webpack=require('webpack')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const path=require('path')
const rootPath=path.resolve(__dirname,'../')
//const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
const prodConfig={
  plugins:[
    new webpack.DefinePlugin({
      'process.env': JSON.stringify({env:'development','BASE_API':'http://www.baidu.gov.cn'})
    }),
    new CleanWebpackPlugin(['build'], {
      root: '', // An absolute path for the root  of webpack.config.js
      verbose: true,// Write logs to console.
      dry: false // Do not delete anything, good for testing.
    }),
    /*
    new DllReferencePlugin({
      manifest: require('../dist/vendor.manifest.json'),
    }),
    */
    new UglifyJSPlugin(),
    new OptimizeCssAssetsPlugin({
      cssProcessor: require('cssnano'),
      canPrint: true
    })
  ]
}
module.exports=merge(common,prodConfig)

須要注意的是DefinePlugin中某個項的值的寫法,假如你想在代碼中訪問process.env裏存儲的內容,那麼在定義時必須使用Json.stringify('prod'),或者是'"prod"',而不是'production'

3.修改package腳本,讓webpack使用指定的配置
  "scripts": {
    "build": "webpack --config webpack.prod.js",
    "dev": "webpack-dev-server webpack.dev.js",
  },

 須要注意配置文件的路徑,若是不是在根目錄下,而是在config文件夾下,則應修改成

"build": "webpack --config config/webpack.prod.js",
相關文章
相關標籤/搜索