webpack4進階配置

移動端CSS px自動轉換成rem

須要兩步來實現:javascript

  1. px2rem-loader 在構建階段將px轉換成rem
  2. lib-flexible 頁面渲染時動態計算根元素的font-size值(手機淘寶開源庫)

下載插件並配置:css

npm i px2rem-loader lib-flexible

module: {
   rules: [
      {
         test: /\.less$/,
         use: [
            MiniCssExtractPlugin.loader, 
            "css-loader",
            {
               loader: "postcss-loader",
               ...
            },
            {
               loader: "px2rem-loader",
               options: {
                  remUnit: 75,
                  remPrecision: 8
               }
            },
            "less-loader",
         ]
      },
   ]
},

而後,須要將淘寶的插件的內聯在html文件中:html

<script src='../node_modules/lib-flexible/flexible.js'></script>

靜態資源內聯

靜態資源內聯是指將CSS、JS等靜態文件中的內容抽離出來,內聯到html中。咱們以前藉助插件作到了將內聯的資源獨立成文件,爲何還要將資源內聯呢?html5

資源內聯的意義:java

  • 代碼層面:
  1. 頁面框架的初始化腳本(這些腳本每每須要在第一時間加載,多將其寫在<head>中)
  2. 上報相關打點
  3. CSS內聯避免頁面閃動
  4. HTML能夠動態地將不一樣的meta標籤內聯進來
  • 請求層面:
  1. 減小http請求

HTML和JS內聯node

使用raw-loader,注意要使用0.5的版本,新版本的存在一些問題。react

npm i raw-loader@0.5.1

/src/meta.html 【例:騰訊NOW直播官網的meta標籤】
<meta charset="UTF-8">
<meta name="viewport" content="viewport-fit=cover,width=device-width,initial-scale=1,user-scalable=no">
<meta name="format-detection" content="telephone=no">
<meta name="keywords" content="now,now直播,直播,騰訊直播,QQ直播,美女直播,附近直播,才藝直播,小視頻,我的直播,美女視頻,在線直播,手機直播">
<meta name="name" itemprop="name" content="NOW直播—騰訊旗下全民視頻社交直播平臺">
<meta name="description" itemprop="description" content="NOW直播,騰訊旗下全民高清視頻直播平臺,聚集中外大咖,最in網紅,草根偶像,明星藝人,校花,小鮮肉,逗逼段子手,各種美食、音樂、旅遊、時尚、健身達人與你24小時不間斷互動直播,各類奇葩刺激的直播玩法,讓你躍躍欲試,你會發現,原來人人均可以當主播賺錢!">
<meta name="image" itemprop="image" content="https://pub.idqqimg.com/pc/misc/files/20170831/60b60446e34b40b98fa26afcc62a5f74.jpg">
<meta name="baidu-site-verification" content="G4ovcyX25V">
<meta name="apple-mobile-web-app-capable" content="no">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">

/node_modules/lib-flexible/flexible.js 【npm下載的淘寶庫】


/src/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    ${ require('raw-loader!./meta/meta.html') }
    <title>Hello Webpack</title>
    <script>${ require('raw-loader!babel-loader!../node_modules/lib-flexible/flexible.js') }</script> <!-- 先用raw-loader內聯,再用babel-laoder轉換語法 -->
</head>
<body>
<div id="root"></div>
</body>
</html>

CSS內聯webpack

藉助style-loader便可web

{
    loader: 'style-loader',
    options: {
        insertAt: 'top', // 樣式插入到<head>
        singleton: true, // 將全部style標籤合併成一個
    }
}

多頁面應用打包方案

每當增長一個頁面,咱們就手動地在webapck配置中對應增長一個entry、html-webpack-plugin。chrome

必定能夠有更優的方案,那就是:動態獲取entry的數量、而後自動生成html-webpack-plugin。

須要藉助glob.sync,能夠在每次構建的時候獲取頁面的數量。這須要咱們規範目錄,將每一個頁面(文件夾)都放在src文件夾下,每一個頁面(文件夾)入口文件是index.js,入口頁面是index.html。

npm i glob

const glob = require('glob');

const setMPA = () => {
   let entry = {};
   let htmlWebpackPlugins = []; 
   const entryFiles = glob.sync(path.join(__dirname, './src/*/index.js')); // 獲取全部頁面的入口文件路徑。entryFiles是全部頁面index.js的絕對路徑造成的數組

   entryFiles.forEach((file)=>{
      let name = file.match(/src\/(.*)\/index\.js/);
      let pageName = name && name[1];
      entry[pageName] = file;
      htmlWebpackPlugins.push(new HtmlWebpackPlugin({
         template: file.replace("index.js","index.html"),
         filename: `${pageName}.html`,
         chunks: [`${pageName}`],
         inject: true,
         minify: {
            html5: true,
            collapseWhitespace: true,
            preserveLineBreaks: false,
            minifyCSS: true,
            minifyJS: true,
            removeComments: true,
         },
      }))
   });

   return {
      entry,
      htmlWebpackPlugins
   }
};

const result = setMPA();

module.exports = {
   entry: result.entry,

   output: {
      filename: "bundle[chunkhash:8].js",
      path: path.join(__dirname, "/dist")
   },
   mode: 'production',
   module: {
      rules: [
          ...
      ]
   },
   plugins: [
      ...result.htmlWebpackPlugins, // 將N個HtmlWebpackPlugin對象插入列表
      new MiniCssExtractPlugin({
         filename: '[name][contenthash:8].css'
      }),
      new OptimizeCssAssetsPlugin({
         assetNameRegExp: /\.css$/g,
         cssProcessor: require('cssnano'),
      }),
      new CleanWebpackPlugin(),
};

使用Source Map

什麼是source map?顧名思義,就是代碼地圖,能夠將構建先後的兩份代碼作一個映射。

sourcemap的做用:構建後的代碼已經變了模樣,沒有可讀性,開發調試的時候沒法定位問題所在。sourcemap的存在能夠直接將問題定位到源代碼,排查問題。

配置文件中devtool屬性能夠設置,有很是多的類型可選,通常狀況下,開發環境使用"source-map",生產環境關閉。

module.exports = {
   devtool: "source-map"
};

代碼分割

對於大型的web應用來講,將全部的代碼編譯成一個文件顯示很差,會形成文件體積過大以及須要加載大量與首屏無關的代碼,用戶體驗很差。webpack提供將代碼分割成chunk(語塊),當代碼運行到須要的它們的時候再進行加載。

代碼分割有兩種類型:

  • 抽離公共資源。(好比基礎庫入React/Vue,React-Router/Vue-Router。或者本身編寫的公共模塊)
  • JS懶加載,使得初始狀態下的代碼更小(首屏優化)

抽離公共資源

須要藉助插件,有兩個插件可供選擇:html-webpack-externals-plugin 和 SplitChunsPlugin(webpack4內置,代替webpack3的CommonsChunkPlugin)

說明:

  • 兩個插件不要混用
  • html-webpack-externals-plugin既能夠將本地基礎庫分離,也能夠直接使用CDN。本人試圖將本地庫進行分離,老是失敗,暫沒查明緣由。
  • 使用SplitChunksPlugin中chunks時,若是沒有使用異步加載,chunks建議"all",但若是使用了異步加載,"all"則會致使錯誤,這時建議用"initial"

html-webpack-externals-plugin:

npm i html-webpack-externals-plugin

const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');

plugins: [
      new HtmlWebpackExternalsPlugin({
         externals: [
            {
               module: "react", // 代碼中引入的庫的名稱
               entry: "https://11.url.cn/now/lib/16.2.0/react.min.js",
               // entry: "cjs/react", // 相對於node_modules/react的路徑【官網示例,實際並不成功】
               global: "React", // 代碼中引入的庫的全局對象的名稱
            },
            {
               module: "react-dom",
               entry: "https://11.url.cn/now/lib/16.2.0/react-dom.min.js",
               // entry: "dist/react-dom", // 相對於node_modules/react-dom的路徑 【官網示例,實際並不成功】
               global: "ReactDOM",
            },

         ],
      })
]

SplitChunksPlugin:

module.exports = {
   optimization: {
      splitChunks: {
         minSize: 0, // 分離包體積的最小大小
         cacheGroups: {
            commons: {
               // test: /(react|react-dom)/,  // 經過正則匹配,只分離react/react-dom這兩個庫,不寫test則不做限制
               name: 'commonss',  // 分離塊的名稱,須要加在HtmlWebpackPlugin插件的chunks屬性中,才能正確命名分離後的文件
               chunks: "all", // 須要分割哪些代碼塊,all表示全部代碼塊,async按需加載的代碼塊,initial初始化代碼塊
               // minChunks: 2, // 最小引用次數,少於這個引用次數就不會單獨提取出來
            }
         }
      }
   }
};

JS懶加載

實現懶加載不須要對webpack配置,動態引入import('')語法尚未成爲JS標準(也許不久的未來會),目前要藉助js插件

實現懶加載:

  • 若是使用的CommonJS,直接require.ensure便可
  • 若是使用ES6模塊化,須要藉助第三方插件實現。(babel插件@babel/plugin-syntax-dynamic-import,且在.babelrc文件內配置)
npm i @babel/plugin-syntax-dynamic-import

.babelrc
{
  "presets": [
    "@babel/preset-react",
    "@babel/preset-env"
  ],
  "plugins": [
    "@babel/plugin-syntax-dynamic-import",  // 懶加載插件
    "@babel/plugin-proposal-class-properties"
  ]
}

在此基礎上,若是使用React開發項目,還可使用react-loadable插件來更好的實現異步加載

npm i react-loadable

index.js中部分代碼

import Loadable from "react-loadable";
const TextLoad = Loadable({
   loader: () => import(/* webpackChunkName: 'text' */    './text'), // 經過註釋的方式指定打包後的chunk的名字
   loading: ()=> <div>正在加載</div>
});

class Search extends React.Component{

   constructor(props){
      super(props);
      this.state = {
         text: false,
      };
   }

   loadComponent = () => {
      this.setState({
         text: true
      })
   };

   render() {
      const { text } = this.state;
      return (
         <>
            <div>你好,顯示字體 Hello Webpacks</div>
            {
               text ? <TextLoad /> : null
            }
            <img alt="" src={logo} style={{ width: 100 }} onClick={this.loadComponent}/>
         </>
      )
   }
}

export default Search;

tree shaking(搖樹優化)

一個文件會有多個方法、對象、語句,只要用到其中一小部分,便會將整個文件內的全部內容打包進去。tree shaking只把用到的方法打包進去,沒用到的會在uglfiy階段被擦出掉。

使用:webpack默認支持,生產環境默認開啓

要求:必須是ES6的寫法,CommonJS的方式不支持

Scope Hoisting

構建以後的代碼存在大量閉包,致使:

  • 文件體積增大
  • 運行代碼時建立的函數做用於變多,內存開銷變大

scope hoisting就是減小閉包函數的聲明。

原理:將全部模塊的代碼按照引用順序放在一個函數做用域裏,而後適當地重命名一些變量以防止變量名衝突,以此減小閉包函數聲明代碼。

使用:webpack默認支持,生產環境默認開啓

要求:必須是ES6寫法,CommonJS的方式不支持

優化構件時命令行的顯示日誌

每次構建,命令行輸出大量日誌,有不少是咱們徹底不關心的,想要優化它,能夠在webpack中設置state屬性。

// 若是是打包,在最外層的state屬性
module.exports = {
   stats: 'errors-only',
};
// 若是是devserver熱更新
const devConfig = {
   devServer: {
      contentBase: './dist',
      hot: true,
      stats: 'errors-only', 
   },
};

state的取值有:

  • errors-only  只發生錯誤時輸出
  • minimal       只發生錯誤或有新的編譯時輸出
  • none       沒有輸出
  • normal         標準輸出
  • verbose       所有輸出

咱們但願是在發生錯誤的時候輸出,當state爲error-only時,若是沒有錯誤,沒有任何輸出,不太友好。

藉助插件:friendly-errors-webpack-plugin。此時將state設置成errors-only

npm i friendly-errors-webpack-plugin

const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
module.exports = {
   plugins: [
      new FriendlyErrorsWebpackPlugin(),
   ],
};

 

個人博客即將同步至騰訊雲+社區,邀請你們一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=1jot771valo8l

相關文章
相關標籤/搜索