webpack 常見問題及優化


Loaders

1、文件Loader

url-loaderfile loader 一樣工作,可以返回 data URL

{
     test: /\.(jpg|png|gif|jpeg)$/,
     loader: 'url-loader',
     options:{
        name: 'img/[name]-[hash:8].[ext]',
        outputPath: 'img',
        esModule: false
     }
}

file-loader 將文件發送到輸出文件夾,並返回相對URL

{
      exclude: /\.(html|js|jpg|css|less)$/,
      loader: 'file-loader',
      options: {
            name: '[hash:8].[ext]',
            outputPath: 'media'
      }
}

2、轉換編譯(Transpiling)

Name Description
script-loader 在全局上下文中執行一次 JavaScript 文件(如在 script 標簽),不需要解析
babel-loader 加載 ES2015+ 代碼,然后使用 Babel 轉譯為 ES5
ts-loader awesome-typescript-loaderJavaScript 一樣加載 TypeScript 2.0+
coffee-loader JavaScript一樣加載CoffeeScript`

3、模板(Templating)

Name Description
html-loader 導出 HTML 為字符串,需要引用靜態資源
pug-loader 加載 Pug 模板並返回一個函數
jade-loader 加載 Jade 模板並返回一個函數
markdown-loader Markdown 轉譯為 HTML

4、樣式

  • style-loader 將模塊的導出作為樣式添加到 DOM
  • css-loader 解析 CSS 文件后,使用 import 加載,並且返回 CSS 代碼
  • less-loader 加載和轉譯 LESS 文件
  • sass-loader 加載和轉譯 SASS/SCSS 文件
  • postcss-loader 使用 PostCSS 加載和轉譯
 yarn add postcss-loader postcss-preset-env
  • stylus-loader 加載和轉譯 Stylus 文件

5、清理和測試(Linting)

eslint-loader 使用 ESLint語法檢查 設置代碼檢查的規則:

在package.json里面添加:

eslintConfig:{
      extends: 'airbns'
}

添加依賴文件

yarn add eslint-config-airbnb-base  eslint-plugin-import  eslint -D

webpack.config.js 文件中

{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'eslint-loader',
      options: {
            fix: true //自動格式化代碼
      }
}

eslint-loader 使用 ESLint兼容性檢查

1、 preset-env 只能檢查簡單的語法。
2、@babel/polyfull 能解決兼用性問題。但是全部引入體積太大了。
3、 core-js第三方按需加載的包

添加依賴文件

npm install babel-loader @babel/core @bebel/preset-env -D 

webpack.config.js文件中

{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader',
      options: {
            presets: [
                  @babel/preset-env,
                  {
                        useBuiltIns: 'usage',//按需加載
                        corejs: {
                              versions: 3//指定版本
                        },
                        targets: {
                              chrome: 60 //指定瀏覽器的版本
                        }
                  }
            ]
      }
}

Plugins

1、HtmlWebpackPlugin 簡單創建 HTML 文件

 plugins: [
      new HtmlWebpackPlugin({
            title: '',
            filename: '',
            template: './src/index.html',
            publicPath: '',
            favicon: '',
            neta: {}
      })
]

壓縮HTML文件

plugins:[
      new HtmlWebpackPlugin({
            template: './src/index.html',
            minify: {
                  collapseWhitespace: true,
                  removeComments: true
            }
      })
]

2、MinCssExtractPlugin 單獨提取css文件

plugins: [
      new MinCssExtractPlugin({
            filename: 'css/style.css',
            chunkFilename: ''
      })
]

配合loader,postcss-loader 做瀏覽器的兼容性。添加了postcss-loader 后,要在package.json 里面配置browserslist:{development: {}, production: {}}對象。

{
      test: /\.css$/,
      use: [
            //style-loader
            MinCssExtractPlugin.laoder,
            css-loader,
            {
                  loader: 'postcss-loader',
                  options: {
                        ident: 'postcss',
                        plugins: () => require('postcss-preset-env')()
                  }
            }
      ]
}

3、OptimizeCssAssetsWebpackPlugin 壓縮css

plugins:[
      new OptimizeCssAssetsWebpackPlugin()
]

一些小的優化點

resolve.extensions:用來表明文件后綴列表,默認查找順序是 ['.js', '.json'],如果你的導入文件沒有添加后綴就會按照這個順序查找文件。我們應該盡可能減少后綴列表長度,然后將出現頻率高的后綴排在前面。
resolve.alias:可以通過別名的方式來映射一個路徑,能讓 Webpack 更快找到路徑。

module.exports ={
    // ...省略其他配置
    resolve: {
        extensions: [".js",".jsx",".json",".css"],
        alias:{
            "jquery":jquery
        }
    }
};

提高 Webpack 打包速度

1、優化Loader搜索范圍(include,exclude

合理的使用exclude或者include的配置,來盡量減少loader被頻繁執行的頻率。當loader執行頻率降低時,也會提升webpack的打包速度。比如:
對於 Loader 來說,影響打包效率首當其沖必屬 Babel 了。因為 Babel 會將代碼轉為字符串生成 AST,然后對 AST 繼續進行轉變最后再生成新的代碼,項目越大,轉換代碼越多,效率就越低。當然了,我們是有辦法優化的。
首先我們可以優化 Loader 的文件搜索范圍,在使用loader時,我們可以指定哪些文件不通過loader處理,或者指定哪些文件通過loader處理。

module.exports = {
  module: {
    rules: [
      {
        // js 文件才使用 babel
        test: /\.js$/,
        use: ['babel-loader'],
        // 只處理src文件夾下面的文件
        include: path.resolve('src'),
        // 不處理node_modules下面的文件
        exclude: /node_modules/
      }
    ]
  }
}

2、cache-loader緩存loader處理結果

緩存分為 babel緩存和文件資源緩存

Babel 緩存 cacheDirectory: true

{
      cacheDirectory: true
}

文件資源的緩存

1.hash 每次webpack構建時都會生成一個唯一的hash值 問題: 應為js和css同時使用一個hash值,如果重新打包就會導致緩存失效。
2.chunkhash 根據chunk生產的hash值,如果打包來源於同一個chunk,那么同一個文件的js和css的hash值就時一樣的。
3.contenthash 根據文件的內容生成hash值,不同文件的hash值是不一樣的。

module.exports = {
  module: {
    rules: [
      {
        // js 文件才使用 babel
        test: /\.js$/,
        use: [
          'cache-loader',
          ...loaders
        ],
      }
    ]
  }
}

3、使用多線程處理打包

module: {
  rules: [
    {
        test: /\.js$/,
        // 把對 .js 文件的處理轉交給 id 為 babel 的 HappyPack 實例
        use: ['happypack/loader?id=babel'],
        exclude: path.resolve(__dirname, 'node_modules'),
    },
    {
        test: /\.css$/,
        // 把對 .css 文件的處理轉交給 id 為 css 的 HappyPack 實例
        use: ['happypack/loader?id=css']
    }
  ]
},
plugins: [
  	new HappyPack({
        id: 'js', //ID是標識符的意思,ID用來代理當前的happypack是用來處理一類特定的文件的
        threads: 4, //你要開啟多少個子進程去處理這一類型的文件
        loaders: [ 'babel-loader' ]
    }),
    new HappyPack({
        id: 'css',
        threads: 2,
        loaders: [ 'style-loader', 'css-loader' ]
    })
]

4、DllPlugin&DllReferencePlugin 成動態鏈接庫

DllPlugin可以將特定的類庫提前打包成動態鏈接庫,在一個動態鏈接庫中可以包含給其他模塊調用的函數和數據,把基礎模塊獨立出來打包到單獨的動態連接庫里,當需要導入的模塊在動態連接庫里的時候,模塊不用再次被打包,而是去動態連接庫里獲取。這種方式可以極大的減少打包類庫的次數,只有當類庫更新版本才有需要重新打包,並且也實現了將公共代碼抽離成單獨文件的優化方案。
這里我們可以先將react、react-dom單獨打包成動態鏈接庫,首先新建一個新的webpack配置文件:webpack.dll.js

const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');
module.exports = {
	// 想統一打包的類庫
    entry:['react','react-dom'],
    output:{
        filename: '[name].dll.js',  //輸出的動態鏈接庫的文件名稱,[name] 代表當前動態鏈接庫的名稱
        path:path.resolve(__dirname,'dll'),  // 輸出的文件都放到 dll 目錄下
        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, 'dll', '[name].manifest.json')
        })
    ]
}

然后我們需要執行這個配置文件生成依賴文件:

webpack --config webpack.dll.js --mode development

接下來我們需要使用 DllReferencePlugin 將依賴文件引入項目中

const DllReferencePlugin = require('webpack/lib/DllReferencePlugin')
module.exports = {
  // ...省略其他配置
  plugins: [
    new DllReferencePlugin({
      // manifest 就是之前打包出來的 json 文件
      manifest:path.join(__dirname, 'dll', 'react.manifest.json')
    })
  ]
}

5、noParse 可以用於配置那些模塊文件的內容不需要進行解析(即無依賴)

module.noParse 屬性,可以用於配置那些模塊文件的內容不需要進行解析(即無依賴) 的第三方大型類庫(例如jquery,lodash)等,使用該屬性讓 Webpack不掃描該文件,以提高整體的構建速度。

module.exports = {
    module: {
      noParse: /jquery|lodash/, // 正則表達式
      // 或者使用函數
      noParse(content) {
        return /jquery|lodash/.test(content)
      }
    }
}

6、IgnorePlugin IgnorePlugin用於忽略某些特定的模塊

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

module.exports = {
  // ...省略其他配置
  plugins: [
    new webpack.IgnorePlugin(/^\.\/locale/,/moment$/)
  ]
}

webpack.IgnorePlugin()參數中第一個參數是匹配引入模塊路徑的正則表達式,第二個參數是匹配模塊的對應上下文,即所在目錄名。

7、打包文件分析工具

webpack-bundle-analyzer插件的功能是可以生成代碼分析報告,幫助提升代碼質量和網站性能。

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports={
      plugins: [
          new BundleAnalyzerPlugin({
            generateStatsFile: true, // 是否生成stats.json文件
          })  
        // 默認配置的具體配置項
        // new BundleAnalyzerPlugin({
        //   analyzerMode: 'server',
        //   analyzerHost: '127.0.0.1',
        //   analyzerPort: '8888',
        //   reportFilename: 'report.html',
        //   defaultSizes: 'parsed',
        //   openAnalyzer: true,
        //   generateStatsFile: false,
        //   statsFilename: 'stats.json', 
        //   statsOptions: null,
        //   excludeAssets: null,
        //   logLevel: info
        // })
  ]
}

使用方式:

"generateAnalyzFile": "webpack --profile --json > stats.json", // 生成分析文件
"analyz": "webpack-bundle-analyzer --port 8888 ./dist/stats.json" // 啟動展示打包報告的http服務器

8、費時分析

speed-measure-webpack-plugin,打包速度測量插件。這個插件可以測量webpack構建速度,可以測量打包過程中每一步所消耗的時間,然后讓我們可以有針對的去優化代碼。

const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin');
const smw = new SpeedMeasureWebpackPlugin();
// 用smw.wrap()包裹webpack的所有配置項
module.exports =smw.wrap({
    module: {},
    plugins: []
});

減少 Webpack 打包后的文件體積

1、對圖片進行壓縮和優化

image-webpack-loader這個loder可以幫助我們對打包后的圖片進行壓縮和優化,例如降低圖片分辨率,壓縮圖片體積等。

module.exports ={
    // ...省略其他配置
    module: {
        rules: [
            {
                test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
                use: [
                    'file-loader',
                    {
                        loader: 'image-webpack-loader',
                        options: {
                            mozjpeg: {
                                progressive: true,
                                quality: 65
                            },
                            optipng: {
                                enabled: false,
                            },
                            pngquant: {
                                quality: '65-90',
                                speed: 4
                            },
                            gifsicle: {
                                interlaced: false,
                            },
                            webp: {
                                quality: 75
                            }
                        }
                    }
                ]
            }
        ]
    }
};

2、刪除無用的CSS樣式

有時候一些時間久遠的項目,可能會存在一些CSS樣式被迭代廢棄,需要將其剔除掉,此時就可以使用purgecss-webpack-plugin插件,該插件可以去除未使用的CSS,一般與 glob、glob-all 配合使用。
注意:此插件必須和CSS代碼抽離插件mini-css-extract-plugin配合使用。
例如我們有樣式文件style.css:

body{
    background: red
}
.class1{
    background: red
}

這里的.class1顯然是無用的,我們可以搜索src目錄下的文件,刪除無用的樣式。

const glob = require('glob');
const PurgecssPlugin = require('purgecss-webpack-plugin');

module.exports ={
    // ...
    plugins: [
        // 需要配合mini-css-extract-plugin插件
        new PurgecssPlugin({
            paths: glob.sync(`${path.join(__dirname, 'src')}/**/*`, 
                  {nodir: true}), // 不匹配目錄,只匹配文件
            })
        }),
    ]
}

3、以CDN方式加載資源

我們知道,一般常用的類庫都會發布在CDN上,因此,我們可以在項目中以CDN的方式加載資源,這樣我們就不用對資源進行打包,可以大大減少打包后的文件體積。
以CDN方式加載資源需要使用到add-asset-html-cdn-webpack-plugin插件。我們以CDN方式加載jquery為例:

const AddAssetHtmlCdnPlugin = require('add-asset-html-cdn-webpack-plugin')

module.exports ={
    // ...
    plugins: [
        new AddAssetHtmlCdnPlugin(true,{
            'jquery':'https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js'
        })
    ],
    //在配置文件中標注jquery是外部的,這樣打包時就不會將jquery進行打包了
    externals:{
      'jquery':'$'
    }
}

4、開啟Tree Shaking (去除無用的代碼,減少代碼的體積。前提條件: 1.模塊必須是ES6模塊,2.必須是production環境)

Tree-shaking,搖晃樹。顧名思義就是當我們搖晃樹的時候,樹上干枯的沒用的葉子就會掉下來。類比到我們的代碼中就是將沒用的代碼搖晃下來,從而實現刪除代碼中未被引用的代碼。
這個功能在webpack4中,當我們將mode設置為production時,會自動進行tree-shaking

在package.json 種配置 sideEffects: false 所有的代碼都可以被tree-shaking 問題是: 可能吧css文件和 @babel/polyfill 等副作用全部干掉。要小心使用。
解決方法 {sideEffects: ['*.css']}

5、按需加載&動態加載

必大家在開發單頁面應用項目的時候,項目中都會存在十幾甚至更多的路由頁面。如果我們將這些頁面全部打包進一個文件的話,雖然將多個請求合並了,但是同樣也加載了很多並不需要的代碼,耗費了更長的時間。那么為了首頁能更快地呈現給用戶,我們肯定是希望首頁能加載的文件體積越小越好,這時候我們就可以使用按需加載,將每個路由頁面單獨打包為一個文件。在給單頁應用做按需加載優化時,一般采用以下原則:

  • 對網站功能進行划分,每一類一個chunk
  • 對於首次打開頁面需要的功能直接加載,盡快展示給用戶,某些依賴大量代碼的功能點可以按需加載
  • 被分割出去的代碼需要一個按需加載的時機
    動態加載目前並沒有原生支持,需要babel的插件:plugin-syntax-dynamic-import。安裝此插件並且在.babelrc中配置:
{
  // 添加
  "plugins": ["transform-vue-jsx", "transform-runtime"],
  
}

列如下面的實例:index.js

let btn = document.createElement('button');
btn.innerHTML = '點擊加載視頻';
btn.addEventListener('click',()=>{
    import(/* webpackChunkName: "video" */'./video').then(res=>{
        console.log(res.default);
    });
});
document.body.appendChild(btn);

webpack.config.js

module.exports = {
    // ...
    output:{
      chunkFilename:'[name].min.js'
    }
}

6、這樣打包后的結果最終的文件就是 video.min.js,並且剛啟動項目時不會加載該文件,只有當用戶點擊了按鈕時才會動態加載該文件。

代碼分割 code-slpit

1、根據入口文件構建分割代碼。

  1. 單入口打包,構建的就是一個文件,。
module.exports = {
   enrty: './src/index.js',
   output: {
      filename: 'js/[name].js',
      path: path.resolve(__dirname, 'dist')
   }
}
  

2.多入口打包就會產生多個代碼塊

module.exports = {
   enrty: {
      index: './src/index.js',
      home: './src/home.js'
   },
   output: {
      filename: 'js/[name]-[hash:8].js',
      path: path.resolve(__dirname, 'dist')
   }
}
 

2、用webapck自帶的 optimization 1.來吧node_modules 單獨打包到一個chunk輸出 2.自動分析多入口文件種有沒有公共的文件,如果有會打包成一個單獨的chunk 。

optimization: {
      splitChunks: {
            chunks: 'all'
      }
}

3、用js代碼讓文件單獨打包成一個chunk 默認webapck會按文件的id打包生成文件名稱,可以用 /webpackChunkName: 'abc'/ 修改生成的文件名。

import(/*webpackChunkName: 'abc'*/'xxx.js').then(() => {
      console.log('success')
}).catch(err){
      console.log('error')
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM