webpack由淺入深——(webpack優化配置)

webpack系列文章

  1. webpack由淺入深——(webpack基礎配置)
  2. webpack由淺入深——(webpack優化配置)
  3. webpack由淺入深——(tapable)
  4. webpack由淺入深——(webapck簡易版)
  5. webpack由淺入深——(ast、loader和plugin)

webpack的優化分類

  1. 優化webpack解析編譯過程,減小webpack打包的時間,可是不能減小生成資源文件體積
  2. 優化webpack編譯輸出的代碼,減小生成資源文件體積

優化webpack解析編譯過程

resolve參數設置

  • extensions:使用require或import引入文件時能夠省略後綴
resolve:{
    extensions:['.js','.css','.vue']
},
複製代碼
  • alias:別名,簡化引用路徑
resolve:{
    extensions:['.js','.css','.vue'],
    alias:{
        bootstrap:'bootstrap/dist/css/bootstrap.css'    //import 'bootstrap'會找對應的路徑
    }
},
複製代碼
  • mainFields:require和import默認經過第三方插件的package.json中main字段中的地址來引用文件,而bootstrap中main字段使用的,能夠經過設置來使用備用字段
resolve:{
    extensions:['.js','.css','.vue'],
    mainFields:['style','main']     //先找style字段再找main
},
複製代碼
  • modules:限制引入第三方的範圍
resolve:{
    extensions:['.js','.css','.vue'],
    mainFields:['style','main'],
    modules:[path.resolve(__dirname,node_modules),modle]    //modle爲本身寫的插件或組件
},
複製代碼
  • mianFiles:範圍下的默認優先查找的文件名
resolve:{
    extensions:['.js','.css','.vue'],
    mainFields:['style','main'],
    modules:[path.resolve(__dirname,node_modules),modle],
    mianFiles:['index.js','main.js']
},
複製代碼

noParse

對於某些庫,例如:lodash和jquery,其中沒有使用require和import引用模塊,那麼庫就不須要使用webpack進行解析分析依賴。javascript

module.exports = {
    entry:{
        index:'./src/index'
    },
    output: {
        filename:'[name].[hash:8].js',
        path: path.resolve(__dirname,'dist')
    },
    module: {
        noParse:/jquery|lodash/,
        rules:[]
    }
}
複製代碼

提供和暴露變量

  • webpack.ProvidePlugin:該插件會提供一個全局變量,在文件中不須要使用require和import引入模塊,可是不能提供給外界使用。
npm install jquery 
複製代碼
const path = require('path');
const webpack =require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
    entry:{
        index:'./src/index'
    },
    output: {
        filename:'[name].[hash:8].js',
        path: path.resolve(__dirname,'dist')
    },
    module: {
        rules:[
            {
                test:/\.css$/,
                use:[{
                    loader:MiniCssExtractPlugin.loader,
                },'css-loader']
            },
            {
                test:/\.jpg|png/,
                use:{
                    loader:'url-loader',
                    options:{
                        limit:8*1024   
                    }
                }
            },
            {
                test:/\.html$/,
                use:'html-withimg-loader'
            },
            {   //使用babel-loader
                test:/\.js$/,
                use:'babel-loader',
                exclude:'/node_modules/'
            }
        ]
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new webpack.ProvidePlugin({
            "$":'jquery'    //在全局下添加$變量,不須要再次引入
        }),
        new MiniCssExtractPlugin({
            filename:index.css,
        }),
        new HtmlWebpackPlugin({
            template:'./src/index.html',
            filename:'index.html',
            chunks:['index']
        })
    ],
    devServer: {    
        contentBase:'./dist',
        port:'3000',
        hot:true
    },
    resolve:{},
}
複製代碼
//index.js
console.log($)  //在全局下添加$變量,不須要再次引入import
//undefine,不會掛載在window上,頁面中插入的script標籤中獲取不到
console.log(window.$)   
複製代碼
  • expose-loader:掛載在window上暴露給外界使用變量,可是必須先在文件中使用require和import引入一次才能夠。
npm install export-loader -D
複製代碼
const path = require('path');
const webpack =require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
    entry:{
        index:'./src/index'
    },
    output: {
        filename:'[name].[hash:8].js',
        path: path.resolve(__dirname,'dist')
    },
    module: {
        rules:[
            {
                test:/\.css$/,
                use:[{
                    loader:MiniCssExtractPlugin.loader,
                },'css-loader']
            },
            {
                test:/\.jpg|png/,
                use:{
                    loader:'url-loader',
                    options:{
                        limit:8*1024   
                    }
                }
            },
            {
                test:/\.html$/,
                use:'html-withimg-loader'
            },
            {   
                test:/\.js$/,
                use:'babel-loader',
                exclude:'/node_modules/'
            },
            {   
                test:/jquery/,
                use:{
                    loader:'expose-loader', //expose-loader暴露$
                    options:{
                        $:'jquery'
                    }
                },
            }
        ]
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new MiniCssExtractPlugin({
            filename:index.css,
        }),
        new HtmlWebpackPlugin({
            template:'./src/index.html',
            filename:'index.html',
            chunks:['index']
        })
    ],
    devServer: {    
        contentBase:'./dist',
        port:'3000',
        hot:true
    },
    resolve:{},
}
複製代碼
//index.js
const $ = require('jquery');
console.log($)  
console.log(window.$)   //可以掛載在window上
複製代碼

happyPack

webpack支持多個線程進行同時進行打包,以便提升編譯打包的速度,可是須要注意,若是項目比較簡單,不要採用這種方式,由於線程時須要cpu的花銷的,簡單的項目而使用多線程編譯打包,不只不能加快打包的速度,反而會下降打包的速度css

const path = require('path');
cosnt HappyPack = require('happypack');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname,'dist'),
    filename: 'index_bundle.js',
  },
  module:{
      rules:[
      {
        test: /\.js$/,
        loader: 'happypack/loader?id=js',
        exclude: /node_modules/,
        include: path.resolve(__dirname,'src')
      },
      {
        test: /\.css$/,
        loader: 'happypack/loader?id=css',
        exclude: /node_modules/,
        include: path.resolve(__dirname,'src')
      }] 
  },
  plugins: [
    new HappyPack({
      id: 'js',
      threadPool: happyThreadPool,
      loaders: [ 'babel-loader' ]
    }),
    new HappyPack({
      id: 'css',
      threadPool: happyThreadPool,
      loaders: [ 'style-loader', 'css-loader', 'less-loader' ]
    })
  ]
}
複製代碼

Dllplugin

預先編譯和打包不會存在變更的文件,在業務代碼中直接引入,加快webpack編譯打包的速度,可是並不能減小最後生成的代碼體積。html

import React,{Component} from 'react';
import ReactDoM,{render} from 'react-dom';
複製代碼

這樣會存在一個問題,react和reat-dom中的代碼基本不會修改,因此用戶編寫代碼修改時,這些代碼也會從新編譯和打包,這樣就很浪費時間,Dllplugin一次編譯打包後就會生成一份不變的代碼供其餘模塊引用,節省開發時編譯打包的時間。vue

  • 配置webpack.dll.config.js
const path = require('path');
const webpack = require('webpack');
module.exports ={
  entry: {
    vendor: ['react', 'redux', 'react-router'],
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].dll.js',
    library: '[name]_[hash]'    //提供全局的變量
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.join(__dirname, 'dist', '[name].manifest.json'),
      name: '[name]_[hash]',
    }),
  ],
};
複製代碼
  • 配置script
"scripts": {
    "dev": "webpack-dev-server",
    "build": "webpack",
    "dll":"webpack --config webpack.dll.config.js"
  },
複製代碼
  • 配置webpack.config.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname,'dist'),
    filename: 'index_bundle.js',
  },
  plugins: [
    new webpack.DllReferencePlugin({
      context: path.join(__dirname),
      manifest:path.resolve(__dirname,'dist','vendor.manifest.json')
    }),
    new HtmlWebpackPlugin(),
    new AddAssetHtmlPlugin({
      filepath: path.resolve(__dirname,'dist','vendor.manifest.json')
    }),
  ],
};
複製代碼

結構會生成這樣的頁面java

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Webpack App</title>
  </head>
  <body>
    <script type="text/javascript" src="vendor-manifest.json"></script>
    <script type="text/javascript" src="index_bundle.js"></script>
  </body>
</html>
複製代碼

抽離公共代碼

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: {
      //兩個模塊都引用了c模塊和d模塊
      pageA:'./src/pageA',  
      pageB:'./src/pageB',
  },
  output: {
    path: path.resolve(__dirname,'dist'),
    filename: '[name].js',
  },
  optimization:{
      splitChunks:{
          cacheGroups:{
              common:{
                chunks:'initial',
                minChunks:2,    //用兩個模塊以上同時引用的模塊纔會抽離出來
                minSize:0       //限制大小,過小了不必抽離
              }
          }
      }
  },
  plugins:[
    new HtmlWebpackPlugin()
  ]
}
複製代碼
//pageA.js pageB.js
import './pageC'
import './pageD'
複製代碼
//pageC.js
console.log('c')
複製代碼
console.log('d')
複製代碼

生成的頁面node

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Webpack App</title>
  </head>
  <body>
    <script type="text/javascript" src="common~pageA~pageB.js"></script>
    <script type="text/javascript" src="pageA.js"></script>
    <script type="text/javascript" src="pageB.js"></script>
  </body>
</html>
複製代碼

優化webpack編譯輸出的代碼

external和cdn引用

將一些公共的文件例如react、react-dom等文件以cdn的方式引入,而後源文件中就不須要打包這些模塊,增長一次請求可是減小代碼體積。react

const path = require('path');
const webpack =require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
    entry:{
        index:'./src/index'
    },
    output: {
        filename:'[name].[hash:8].js',
        path: path.resolve(__dirname,'dist')
    },
    module: {
        rules:[
            {
                test:/\.css$/,
                use:[{
                    loader:MiniCssExtractPlugin.loader,
                },'css-loader']
            },
            {
                test:/\.jpg|png/,
                use:{
                    loader:'url-loader',
                    options:{
                        limit:8*1024   
                    }
                }
            },
            {
                test:/\.html$/,
                use:'html-withimg-loader'
            },
            {   
                test:/\.js$/,
                use:'babel-loader',
                exclude:'/node_modules/'
            }
        ]
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new MiniCssExtractPlugin({
            filename:index.css,
        }),
        new HtmlWebpackPlugin({
            template:'./src/index.html',
            filename:'index.html',
            chunks:['index']
        })
    ],
    externals:{
        'jquery':'$'    //代表jquery是外鏈CDN引用
    }
    devServer: {    
        contentBase:'./dist',
        port:'3000',
        hot:true
    },
    resolve:{},
}
複製代碼
//index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<div id="root"></div>
<script
  src="https://code.jquery.com/jquery-3.3.1.min.js"
  integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
  crossorigin="anonymous"></script>
</body>
</html>
複製代碼

IgnorePlugin

IgnorePlugin用於忽略某些特定的模塊,讓 webpack 不把這些指定的模塊打包進去jquery

const path = require('path');
const webpack =require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
    entry:{
        index:'./src/index'
    },
    output: {
        filename:'[name].[hash:8].js',
        path: path.resolve(__dirname,'dist')
    },
    module: {},
    plugins: [
        //moment中的語言包很大,其餘語言根本就沒有必要打包
        //須要的語言單獨引入
        new webpack.IgnorePlugin(/^\.\/locale/,/moment$/)
    ],
    devServer: {    
        contentBase:'./dist',
        port:'3000',
        hot:true
    },
    resolve:{},
}
複製代碼
import moment from  'moment';
//須要的語言單獨引入
require('moment/locale/zh-cn');
console.log(moment);
複製代碼

使用IgnorePlugin前 webpack

使用IgnorePlugin前
使用IgnorePlugin後
使用IgnorePlugin後

懶加載

webpack內部定義用戶異步加載模塊的使用方法,用於減小資源文件的體積。es6

//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>
複製代碼

tree-shaking

tree-shaking會將一些沒有用到的代碼自動刪除掉,這是webpack4內部自帶的功能,這裏有一個前提就是代碼必須採用es6的模塊方式import,否則沒有辦法使用tree-shaking

//a.js
export a = ()=>'a';
export b = ()=>'b';
複製代碼
//index.js
import {a} from './a'
console.log(a());
複製代碼

最後打後的代碼會刪除掉b

變量提高

webpack會將某些代碼進行優化,以更簡潔的方式來替代。

//c.js
export default 'kbz'
複製代碼
//index.js
import c from './c'
console.log(c); 
//這兩行會簡化爲 var c = 'kbz'
複製代碼
const path = require('path');
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin')
module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname,'dist'),
    filename: 'index_bundle.js',
  },
  module:{
  },
  plugins: [
    new ModuleConcatenationPlugin()
  ]
}
複製代碼

結語:

以上就是一些關於webpack優化的一些手段。

相關文章
相關標籤/搜索