webpack系列-優化

HappyPick

開啓子線程打包,加快打包速度,使用id標識css

rules: [
      {
        test:/\.css/,
        use: 'Happypack/loader?id=css'
        
      },
      {
        test: /\.js$/,
        exclude:/node_modules/,
        include:path.resolve(__dirname,'src'),
        use: 'Happypack/loader?id=js'
      }
    ]
    
    // plugins
    new HappyPack({
      id:'css',
      use:['style-loader','css-loader']
    }),
    new HappyPack({
      id:'js',
      use: [{
          loader: 'babel-loader',
          options: {
            presets: [
              '@babel/preset-env',
              '@babel/preset-react' // react jsx 
            ]
          }
      }]
    }),
複製代碼

libraryTarget 和 library

當用 Webpack 去構建一個能夠被其餘模塊導入使用的庫時須要用到它們html

  • output.libraryTarget 配置以何種方式導出庫
  • output.library 配置導出庫的名稱 output.libraryTarget 是字符串的枚舉類型,支持如下配置

var(默認)

編寫的庫將經過var被賦值給經過library指定名稱的變量。node

// bundle.js
    var calculator=(function (modules) {}({})
    // index.html
    <script src="bundle.js"></script>
    <script>
        let ret = calculator.add(1,2);
        console.log(ret);
    </script>
複製代碼

commonjs(exports)

exports["calculator"] = (function (modules) {}({})
    
    require('npm-name')['calculator'].add(1,2);
複製代碼

commonjs2

module.exports = (function (modules) {}({})
    
    require('npm-name').add();
複製代碼

this

this["calculator"]= (function (modules) {}({})
    
    this.calculator.add();
複製代碼

window

window["calculator"]= (function (modules) {}({})
    
    window.calculator.add();
複製代碼

global

global["calculator"]= (function (modules) {}({})
    
    global.calculator.add();
複製代碼

DLLPlugin

把基礎模塊獨立出來打包到單獨的動態鏈接庫裏.react

定義DLL(DLLPlugin,注意output.library和DLLPlugin的name須要保持一致)

const path=require('path');
    const DllPlugin=require('webpack/lib/DllPlugin');
    module.exports={
        entry: {
            react:['react','react-dom']
        },// 把 React 相關模塊的放到一個單獨的動態連接庫
        output: {
            path: path.resolve(__dirname,'dist'),// 輸出的文件都放到 dist 目錄下
            filename: '[name].dll.js',//輸出的動態連接庫的文件名稱,[name] 表明當前動態連接庫的名稱
            library: '_dll_[name]',//存放動態連接庫的全局變量名稱,例如對應 react 來講就是 _dll_react
        },
        plugins: [
            new DllPlugin({
                // 動態連接庫的全局變量名稱,須要和 output.library 中保持一致
                // 該字段的值也就是輸出的 manifest.json 文件 中 name 字段的值
                // 例如 react.manifest.json 中就有 "name": "_dll_react"
                name: '_dll_[name]',
                // 描述動態連接庫的 manifest.json 文件輸出時的文件名稱
                path: path.join(__dirname, 'dist', '[name].manifest.json')
            })
        ]
    }
複製代碼

使用動態連接庫文

const DllReferencePlugin = require('webpack/lib/DllReferencePlugin')
    plugins: [
      new DllReferencePlugin({
        manifest:require('./dist/react.manifest.json')
      })
    ]
複製代碼

html中使用

須要將dll文件先提早引入webpack

<script src="react.dll.js"></script>
    <script src="bundle.js"></script>
複製代碼

CDN

  • HTML文件不緩存,放在本身的服務器上,關閉本身服務器的緩存,靜態資源的URL變成指向CDN服務器的地址web

  • 靜態的JavaScript、CSS、圖片等文件開啓CDN和緩存,而且文件名帶上HASH值npm

    • 帶上 Hash 值的緣由是文件名會隨着文件內容而變化,只要文件發生變化其對應的 URL 就會變化,它就會被從新下載,不管緩存時間有多長
  • 爲了並行加載不阻塞,把不一樣的靜態資源分配到不一樣的CDN服務器上json

    • 多個域名後會增長域名解析時間
    • 能夠經過在 HTML HEAD 標籤中 加入去預解析域名,以下降域名解析帶來的延遲
  • 接入cdn(publicPath)promise

output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name]_[hash:8].js',
        publicPath: 'http://img.zhufengpeixun.cn'
    },
複製代碼

Tree Shaking

Tree Shaking 能夠用來剔除JavaScript中用不上的死代碼。它依賴靜態的ES6模塊化語法,例如經過import和export導入導出。緩存

不要編譯ES6模塊

  • 要讓 Tree Shaking 正常工做的前提是交給 Webpack 的 JavaScript 代碼必須是採用 ES6 模塊化語法的
  • "modules": false 的含義是關閉 Babel 的模塊轉換功能,保留本來的 ES6 模塊化語法。
use:[{
        loader: 'babel-loader',
            options: {
               presets:[['@babel/preset-env',{modules: false }],'@babel/preset-react']
        }
    }]
複製代碼

顯示未使用的導出實例

npx webpack --display-used-exports
複製代碼

剔除用不上的代碼(UglifyJS處理)

webpack --display-used-exports --optimize-minimize
複製代碼

啓動壓縮

optimization: {
      minimizer: [
             new UglifyJsPlugin({
                 cache: true,//啓動緩存
                 parallel: true,//啓動並行壓縮
                 //若是爲true的話,能夠得到sourcemap
                 sourceMap: true // set to true if you want JS source maps
             }),
             //壓縮css資源的
             new OptimizeCSSAssetsPlugin({})
         ]
}
複製代碼

提取公共代碼

大網站有多個頁面,每一個頁面因爲採用相同技術棧和樣式代碼,會包含不少公共代碼,若是都包含進來會有問題

optimization: {
        splitChunks: {
            cacheGroups: {
                commons: { // 頁面之間的公共代碼
                    chunks: "initial",
                    minChunks: 2,//最小重複的次數
                    minSize: 0//最小提取字節數
                },
                vendor: { // 第三方庫
                    test: /node_modules/,
                    chunks: "initial", // 先抽離公共的第三方庫
                    name: "vendor",
                }
            }
        }
    }
複製代碼

Scope Hoisting

Scope Hoisting 可讓 Webpack 打包出來的代碼文件更小、運行的更快, 它又譯做 "做用域提高",是在 Webpack3 中新推出的功能。

// hello.js
    export default 'Hello';
    // index.js
    import str from './hello.js';
    console.log(str);
    // main.js
    var n = name = "Hello";
    console.log(n)
複製代碼

動態導入和懶加載(import導入返回一個promise)

用戶當前須要用什麼功能就只加載這個功能對應的代碼,也就是所謂的按需加載 在給單頁應用作按需加載優化時,通常採用如下原則:

  • 對網站功能進行劃分,每一類一個chunk
  • 對於首次打開頁面須要的功能直接加載,儘快展現給用戶
  • 某些依賴大量代碼的功能點能夠按需加載
  • 被分割出去的代碼須要一個按需加載的時機
// handler.js
    module.exports=function () {
        alert('你點我啦!');
    }
    // index.js
    document.querySelector('#clickBtn').addEventListener('mouseover',() => {
        import('./handler').then(clickMe => {
            window.clickMe=clickMe.default;
        });
    });
    // html
    <div id="clickBtn" onclick="clickMe()">彈框</div>
複製代碼

react-router4 路由懶加載

// index.js 
    import React from 'react';
    import ReactDOM from 'react-dom';
    import {HashRouter as Router,Route} from 'react-router-dom';
    import Bundle from './Bundle';
    let LazyAbout=(props) => (<Bundle {...props} load={()=>import('./About')}/>)
    let Home=() => <div>Home</div>
    ReactDOM.render(
    <Router>
        <div>
          <Route path="/" component={Home} />
          <Route path="/about" component={LazyAbout}/>
        </div>
    </Router>,document.getElementById('root'));
    
    // Bundle
    import React from 'react';
    export default class Bundle extends React.Component{
        state={Mod: null}
        componentWillMount() {
            this.props.load().then(mod=>this.setState({Mod: mod.default? mod.default:mod}));
        }
        render() {
            let Mod=this.state.Mod;
            return Mod&&<Mod  {...this.props}/>;
        }
    }
    
    // about
    import React from 'react';
    export default props => <div>About</div>
複製代碼

熱更新

配置hot

const webpack = require('webpack');
    module.exports = {
    entry:{
      main:'./src/index.js',
    },
    plugins: [
      // 該插件的做用就是實現模塊熱替換,實際上當啓動時帶上 `--hot` 參數,會注入該插件,生成 .hot-update.json 文件。
      new webpack.NamedModulesPlugin(), // 用於啓動 HMR 時能夠顯示模塊的相對路徑
      new webpack.HotModuleReplacementPlugin(), // Hot Module Replacement 的插件
    ],
    devServer:{
      // 告訴 DevServer 要開啓模塊熱替換模式
      hot: true,      
    }  
    };
複製代碼

代碼實現

import React from 'react';
    import { render } from 'react-dom';
    import App from './App';
    import './index.css';
    render(<App/>, document.getElementById('root'));
    
    // 只有當開啓了模塊熱替換時 module.hot 才存在
    if (module.hot) {
      // accept 函數的第一個參數指出當前文件接受哪些子模塊的替換,這裏表示只接受 ./AppComponent 這個子模塊
      // 第2個參數用於在新的子模塊加載完畢後須要執行的邏輯
      module.hot.accept(['./App'], () => {
        // 新的 AppComponent 加載成功後從新執行下組建渲染邏輯
        let App=require('./App').default;  
        render(<App/>, document.getElementById('root'));
      });
    }
複製代碼
相關文章
相關標籤/搜索