webpack打包詳解(頁面性能優(yōu)化)

webpack打包詳解(頁面性能優(yōu)化)

先附上webpack的中文文檔地址:https://www.webpackjs.com/

先讓我們來了解一下什么是webpack

webpack 是一個現(xiàn)代 JavaScript 應(yīng)用程序的靜態(tài)模塊打包器(module bundler)。當(dāng) webpack 處理應(yīng)用程序時,它會遞歸地構(gòu)建一個依賴關(guān)系圖(dependency graph),其中包含應(yīng)用程序需要的每個模塊,然后將所有這些模塊打包成一個或多個 bundle,在webpack眼里一切都可以打包,打包成js文件

一.下載webpack環(huán)境

npm i webpack@4 webpack-cli@3 -D
安裝webpack4 配置 cli3

接下來做一個小測試
在src下創(chuàng)建app.js
src/app.js

import name from './name.js'
console.log(name)

src/name.js

export default 'test'

命令行輸入打包指令

 webpack --entry ./src/app.js --output dist/bundle.js
//--entry后為入口文件地址 --output后為出口文件地址
// 可以進(jìn)入dist文件夾中查看是否打包成功

二.正式配置

第一步運行完畢,成功打包,說明了webpack的環(huán)境已經(jīng)安裝成功,接下來就可以開始正式的打包配置了

2.1 入口及出口的配置

src/app.js

console.log(config)
//app.js 入口文件中打印一個字符

根目錄下創(chuàng)建config/webpack.config.dev.js

const path = require('path')
module.exports = {
    mode: 'development',
   //入口,從入口開始逐個模塊打包
  entry: {
    app: path.resolve(__dirname, '../src/app.js')
  },
 //出口,編譯完成輸出文件夾
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].js' //[name] 拿到entry中的key值
  }
}

配置啟動命令

  "scripts": {
    "dev": "",
    "build": "webpack --config ./config/webpack.config.dev.js"
  }
2.2. 載入其他功能插件

npm i html-webpack-plugin -D/ yarn add html-webpack-plugin -D 解析html
npm i copy-webpack-plugin -D/ yarn add copy-webpack-plugin -D打包小圖標(biāo)
npm i clean-webpack-plugin -D/ yarn add clean-webpack-plugin -D
刪除dist中的文件

解析html:導(dǎo)入html-webpack-plugin

const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    ...,
 //插件
  plugins: [
    new HtmlWebpackPlugin({
   //導(dǎo)入文件名
      filename: 'index.html',
    //模板:導(dǎo)入的html路徑
      template: path.resolve(__dirname, '../public/index.html')
    })
  ]
}

打包小圖標(biāo):導(dǎo)入copy-webpack-plugin

const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
    ...,
  plugins: [
    ...,
    new CopyWebpackPlugin({
        patterns: [
        {
          from: resolve('public/favicon.ico'),
          to: resolve('dist/')
        }
      ]
        })
  ]
}

刪除dist圖片:npm i clean-webpack-plugin

const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
    ...,
  plugins: [
    new CleanWebpackPlugin (),
    ...
  ]
}
2.3. 樣式處理

打包css也是css代碼壓縮,簡單來說就是無效代碼刪除和css語義合并css的樣式文件可分為css、stylus、scss,打包不同類型的文件,也需要下載對應(yīng)的模塊
css:npm i css-loader -D / yarn add css-loader -D
stylus:npm i stylus stylus-loader -D / yarn add stylus stylus-loader -D
scss:npm i node-sass sass-loader -D / yarn add ndoe-sass sass-loader -D
style-loader:npm i style-loader -D / yarn add style-loader -D
css抽離:npm i mini-css-extract-plugin -D / yarn add mini-css-extract-plugin -D

2.3.1. css:css類型文件的打包

module.exports = {
  ...,
//模塊
  module: {
    rules: [
        ...,
      {
        //正則驗證,.css為后綴的使用css-loader
        test: /\.css$/,
        //載入程序:
        loader: 'css-loader'
      }
    ]
  },
  ...
}

若樣式導(dǎo)入進(jìn)js中,將其解析出來需要使用style-loader 模塊
npm i style-loader -D / yarn add style-loader -D

module.exports = {
  ...,
  module: {
    rules: [
        ...,
      {
        test: /\.css$/,
        // loader: 'css-loader'
         loaders: ['style-loader', 'css-loader'] // 后面的模塊為前面的服務(wù)
      }
    ]
  },
  ...
}

2.3.2. stylus:stylus類型的打包

module.exports = {
  ...,
  module: {
    rules: [
        ...,
      {
        test: /\.(css|styl)$/,
        // loader: 'css-loader'
        loaders: ['style-loader', 'css-loader', 'stylus-loader'] // 后面的為前面的服務(wù)
      }
    ]
  },
  ...
}

2.3.3. scss:scss類型的打包

module.exports = {
  ...,
  module: {
    rules: [
        ...,
      {
        test: /\.scss$/,
        loaders: ['style-loader', 'css-loader', 'sass-loader'] // 后面的為前面的服務(wù)
      }
    ]
  },
  ...
}

2.3.4. css的抽離:css代碼被css-loader轉(zhuǎn)換后,交給的是style-loader進(jìn)行處理。

style-loader使用的方式是用一段js代碼,將樣式加入到style元素中。而實際的開發(fā)中,我們往往希望依賴的樣式最終形成一個css文件

此時,就需要用到一個庫:mini-css-extract-plugin,該庫提供了1個plugin和1個loader

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
    ...,
  module: {
    rules: [
      ...,
      {
        test: /\.(css|styl)$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'stylus-loader']
      },
      {
        test: /\.scss$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
      }
    ]
  },
  plugins: [
    ...,
    new MiniCssExtractPlugin({
      filename: 'style/[name].css'
    })
  ],
  ...
}

2.3.5. 自動補全css
對于css屬性,尤其對css3的新屬性而言,瀏覽器私有屬性前綴,是非常滴重要,然鵝,如果要知道每個屬性的瀏覽器私有前綴,那是非常的麻煩,“如何補齊css前綴”,且看**autoprefixer

//配置文件中
const loaderUse = (fileLoader) => {
  return [
    {
      loader: MiniCssExtractPlugin.loader,
      options: {
        publicPath: '../'
      }
    },
    'css-loader',
    'postcss-loader', // ******
    fileLoader
  ]
} 

//posts.config.js
module.exports = {
  plugins: [
    require('autoprefixer')({
      overrideBrowserslist: ['last 100 versions']
    })
  ]
}
//如果不設(shè)置配置文件
const loaderUse = (fileLoader) => {
  return [
    {
      loader: MiniCssExtractPlugin.loader,
      options: {
        publicPath: '../'
      }
    },
    'css-loader',
    {
      loader: 'postcss-loader',
      options: {
        postcssOptions: {
          plugins: [
            [
              'autoprefixer',
              {
                overrideBrowserslist: ['last 100 versions']
              }
            ],
            [
              'postcss-preset-env',
              {
                // Options
              }
            ]
          ]
        }
      }
    },
    fileLoader
  ]
} 
2.4. 處理圖片

方式一:src/assets圖片

module.exports = {
    ...,
  module: {
    rules: [
      ...,
      {
        test: /\.(jpg|png|jpeg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 2048 // 如果設(shè)置的值夠大,顯示base64(內(nèi)存中),如果設(shè)置的值小,顯示圖片地址
            }
          }
        ]
      }
    ]
  },
  ...
}

如果需要設(shè)置圖片的輸出目錄

module.exports = {
    ...,
  module: {
    rules: [
      ...,
      {
        test: /\.(jpg|png|jpeg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 2048, // 如果設(shè)置的值夠大,顯示base64,如果設(shè)置的值小,顯示圖片地址
              outputPath: '../dist/images' // 不要寫絕對路徑 類似 path.resolve()?
            }
          }
        ]
      }
    ]
  },
  ...
}

方式二:放入public 拷貝至 dist

不受webpack管轄,直接拷貝即可,使用copy-webpack-plugin插件

module.exports = {
    ...,
  plugins: [
    ...,
    new CopyWebpackPlugin({
      patterns: [
        {
          context: resolve('public/'), // 一定要添加上下文對象,否則直接復(fù)制public目錄至dist
          from: '**/*',
          to: resolve('dist/'),
          globOptions: {
            ignore: ['index.html']
          }
        }
      ]
    }),
        ...
  ],
  ...
}
2.5. 處理高級js

npm i @babel/core @babel/preset-env babel-loader -D / yarn add @babel/core @babel/preset-env babel-loader -D
@babel/preset-env是一系列插件的集合,包含了我們在babel6中常用的es2015,es2016, es2017等最新的語法轉(zhuǎn)化插件,允許我們使用最新的js語法,比如 let,const,箭頭函數(shù)等等,但不包括stage-x階段的插件。

module.exports = {
    ...,
  module: {
    rules: [
      {
        test: /\.js$/,
        // loader: 'babel-loader '
        // bower_components 是以前的一種包管理器 bootstrap
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
      ...
    ]
  },
  ...
}

但是如果給類添加屬性之后,就會發(fā)生錯誤,此時需要添加新的插件

npm i @babel/plugin-proposal-class-properties -D / yarn add @babel/plugin-proposal-class-properties -D

module.exports = {
    ...,
  module: {
    rules: [
      {
        test: /\.js$/,
        // loader: 'babel-loader '
        // bower_components 是以前的一種包管理器 bootstrap
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            plugins: ['@babel/plugin-proposal-class-properties']
          }
        }
      },
      ...
    ]
  },
  ...
}

如果代碼包含 async await,需要添加新的插件

npm i @babel/plugin-transform-runtime @babel/runtime -D / yarn add @babel/plugin-transform-runtime @babel/runtime -D

module.exports = {
    ...,
  module: {
    rules: [
      {
        test: /\.js$/,
        // loader: 'babel-loader '
        // bower_components 是以前的一種包管理器 bootstrap
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            plugins: [
              '@babel/plugin-proposal-class-properties',
              '@babel/plugin-transform-runtime'
            ]
          }
        }
      },
      ...
    ]
  },
  ...
}

如果代碼中有裝飾器,同樣需要添加新的插件

npm i @babel/plugin-proposal-decorators -D / yarn add @babel/plugin-proposal-decorators -D

module.exports = {
    ...,
  module: {
    rules: [
      {
        test: /\.js$/,
        // loader: 'babel-loader '
        // bower_components 是以前的一種包管理器 bootstrap
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            plugins: [
              '@babel/plugin-transform-runtime', // 處理async await
              [
                '@babel/plugin-proposal-decorators',
                {
                  legacy: true
                }
              ],
              [
                '@babel/plugin-proposal-class-properties',  // 處理類的屬性
                {
                  loose: true
                }
              ]
            ] 
          }
        }
      },
      ...
    ]
  },
  ...
}

如果js中含有瀏覽器解析不了的語句,可以使用墊片

npm i @babel/polyfill -D / yarn add @babel/polyfill -D

方式一:入口文件頂級添加如下語句

import "@babel/polyfill";

方式二:配置文件入口處

module.exports = {
  entry: {
    app: ["@babel/polyfill", path.resolve(__dirname, '../src/app.js')]
  }
}

方式三:添加@babel/preset-env時添加配置選項

module.exports = {
    ...,
  module: {
    rules: [
      {
        test: /\.js$/,
        // loader: 'babel-loader '
        // bower_components 是以前的一種包管理器 bootstrap
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              [
                '@babel/preset-env',
                {
                  useBuiltIns: 'usage'
                }
              ]
            ],
            plugins: [
              '@babel/plugin-transform-runtime', // 處理async await
              [
                '@babel/plugin-proposal-decorators',
                {
                  legacy: true
                }
              ],
              [
                '@babel/plugin-proposal-class-properties',  // 處理類的屬性
                {
                  loose: true
                }
              ]
            ] 
          }
        }
      },
      ...
    ]
  },
  ...
}

此時運行查看會有提示信息安裝core-js模塊,需要安裝配置

npm i core-js@3 -D / yarn add core-js@3 -D

module.exports = {
    ...,
  module: {
    rules: [
      {
        test: /\.js$/,
        // loader: 'babel-loader '
        // bower_components 是以前的一種包管理器 bootstrap
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              [
                '@babel/preset-env',
                {
                  useBuiltIns: 'usage',
                  corejs:3
                }
              ]
            ],
            plugins: [
              '@babel/plugin-transform-runtime', // 處理async await
              [
                '@babel/plugin-proposal-decorators',
                {
                  legacy: true
                }
              ],
              [
                '@babel/plugin-proposal-class-properties',  // 處理類的屬性
                {
                  loose: true
                }
              ]
            ] 
          }
        }
      },
      ...
    ]
  },
  ...
}

為什么要使用墊片

Babel默認(rèn)只轉(zhuǎn)換新的JavaScript句法(syntax),而不轉(zhuǎn)換新的API,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局對象,以及一些定義在全局對象上的方法(比如Object.assign)都不會轉(zhuǎn)碼。舉個例子,ES6在Array對象上新增了Array.from方法。Babel就不會轉(zhuǎn)碼這個方法。如果想讓這個方法運行,必須使用babel-polyfill,為當(dāng)前環(huán)境提供一個墊片。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容