webpack常用loader和plugin

Loader

簡介

webpack中提供了一種處理多種文件格式的機制,這便是Loader,我們可以把Loader當(dāng)成一個轉(zhuǎn)換器,它可以將某種格式的文件轉(zhuǎn)換成Wwebpack支持打包的模塊。

在Webpack中,一切皆模塊,我們常見的Javascript、CSS、Less、Typescript、Jsx、圖片等文件都是模塊,不同模塊的加載是通過模塊加載器來統(tǒng)一管理的,當(dāng)我們需要使用不同的 Loader 來解析不同類型的文件時,我們可以在module.rules字段下配置相關(guān)規(guī)則。

loader特點

  • loader 本質(zhì)上是一個函數(shù),output=loader(input) // input可為工程源文件的字符串,也可是上一個loader轉(zhuǎn)化后的結(jié)果;
  • 第一個 loader 的傳入?yún)?shù)只有一個:資源文件(resource file)的內(nèi)容;
  • loader支持鏈式調(diào)用,webpack打包時是按照數(shù)組從后往前的順序?qū)①Y源交給loader處理的。
  • 支持同步或異步函數(shù)。

代碼結(jié)構(gòu)

代碼結(jié)構(gòu)通常如下:

// source:資源輸入,對于第一個執(zhí)行的 loader 為資源文件的內(nèi)容;后續(xù)執(zhí)行的 loader 則為前一個 loader 的執(zhí)行結(jié)果
// sourceMap: 可選參數(shù),代碼的 sourcemap 結(jié)構(gòu)
// data: 可選參數(shù),其它需要在 Loader 鏈中傳遞的信息,比如 posthtml/posthtml-loader 就會通過這個參數(shù)傳遞參數(shù)的 AST 對象
const loaderUtils = require('loader-utils');

module.exports = function(source, sourceMap?, data?) {
  // 獲取到用戶給當(dāng)前 Loader 傳入的 options
  const options = loaderUtils.getOptions(this);
  // TODO: 此處為轉(zhuǎn)換source的邏輯
  return source;
};

常用的Loader

1. babel-loader

babel-loader基于babel,用于解析JavaScript文件。babel有豐富的預(yù)設(shè)和插件,babel的配置可以直接寫到options里或者單獨寫道配置文件里。

Babel是一個Javscript編譯器,可以將高級語法(主要是ECMAScript 2015+ )編譯成瀏覽器支持的低版本語法,它可以幫助你用最新版本的Javascript寫代碼,提高開發(fā)效率。

webpack通過babel-loader使用Babel。

****用法****:

 # 環(huán)境要求:
webpack 4.x || 5.x | babel-loader 8.x | babel 7.x
 # 安裝依賴包:
npm install -D babel-loader @babel/core @babel/preset-env webpack

然后,我們需要建立一個Babel配置文件來指定編譯的規(guī)則。

Babel配置里的兩大核心:插件數(shù)組(plugins) 和 預(yù)設(shè)數(shù)組(presets)。

Babel 的預(yù)設(shè)(preset)可以被看作是一組Babel插件的集合,由一系列插件組成。

****常用預(yù)設(shè):****

  • @babel/preset-env ES2015+ 語法
  • @babel/preset-typescript TypeScript
  • @babel/preset-react React
  • @babel/preset-flow Flow

****插件和預(yù)設(shè)的執(zhí)行順序:****

  • 插件比預(yù)設(shè)先執(zhí)行
  • 插件執(zhí)行順序是插件數(shù)組從前向后執(zhí)行
  • 預(yù)設(shè)執(zhí)行順序是預(yù)設(shè)數(shù)組從后向前執(zhí)行

****webpack配置代碼:****

// webpack.config.js
module: {
  rules: [
    {
      test: /\.m?js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: [
            ['@babel/preset-env', { targets: "defaults" }]
          ],
          plugins: ['@babel/plugin-proposal-class-properties'],
          // 緩存 loader 的執(zhí)行結(jié)果到指定目錄,默認為node_modules/.cache/babel-loader,之后的 webpack 構(gòu)建,將會嘗試讀取緩存
          cacheDirectory: true,
        }
      }
    }
  ]
}

以上options參數(shù)也可單獨寫到配置文件里,許多其他工具都有類似的配置文件:ESLint (.eslintrc)、Prettier (.prettierrc)。

配置文件我們一般只需要配置 presets(預(yù)設(shè)數(shù)組) 和 plugins(插件數(shù)組) ,其他一般也用不到,代碼示例如下:

// babel.config.js
module.exports = (api) => {
    return {
        presets: [
            '@babel/preset-react',
            [
                '@babel/preset-env', {
                    useBuiltIns: 'usage',
                    corejs: '2',
                    targets: {
                        chrome: '58',
                        ie: '10'
                    }
                }
            ]
        ],
        plugins: [
            '@babel/plugin-transform-react-jsx',
            '@babel/plugin-proposal-class-properties'
        ]
    };
};

****推薦閱讀:****

2. ts-loader

為webpack提供的 TypeScript loader,打包編譯Typescript

****安裝依賴:****

npm install ts-loader --save-dev
npm install typescript --dev

****webpack配置如下:****

// webpack.config.json
module.exports = {
  mode: "development",
  devtool: "inline-source-map",
  entry: "./app.ts",
  output: {
    filename: "bundle.js"
  },
  resolve: {
    // Add `.ts` and `.tsx` as a resolvable extension.
    extensions: [".ts", ".tsx", ".js"]
  },
  module: {
    rules: [
      // all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
      { test: /\.tsx?$/, loader: "ts-loader" }
    ]
  }
};

還需要typescript編譯器的配置文件****tsconfig.json****:

{
  "compilerOptions": {
    // 目標(biāo)語言的版本
    "target": "esnext",
    // 生成代碼的模板標(biāo)準
    "module": "esnext",
    "moduleResolution": "node",
    // 允許編譯器編譯JS,JSX文件
    "allowJS": true,
    // 允許在JS文件中報錯,通常與allowJS一起使用
    "checkJs": true,
    "noEmit": true,
    // 是否生成source map文件
    "sourceMap": true,
    // 指定jsx模式
    "jsx": "react"
  },
  // 編譯需要編譯的文件或目錄
  "include": [
    "src",
    "test"
  ],
  // 編譯器需要排除的文件或文件夾
  "exclude": [
    "node_modules",
    "**/*.spec.ts"
  ]
}

更多配置請看 官網(wǎng)

3. markdown-loader

markdown編譯器和解析器

****用法:****

只需將 loader 添加到您的配置中,并設(shè)置 options。

****js代碼里引入markdown文件:****

// file.js

import md from 'markdown-file.md';

console.log(md);

****webpack配置:****

// wenpack.config.js
const marked = require('marked');
const renderer = new marked.Renderer();

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.md$/,
        use: [
            {
                loader: 'html-loader'
            },
            {
                loader: 'markdown-loader',
                options: {
                    pedantic: true,
                    renderer
                }
            }
        ]
      }
    ],
  },
};

4. raw-loader

可將文件作為字符串導(dǎo)入

// app.js
import txt from './file.txt';
// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.txt$/,
        use: 'raw-loader'
      }
    ]
  }
}

5. file-loader

用于處理文件類型資源,如jpg,png等圖片。返回值為publicPath為準

// file.js
import img from './webpack.png';
console.log(img); // 編譯后:https://www.tencent.com/webpack_605dc7bf.png
// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif)$/i,
        loader: 'file-loader',
        options: {
          name: '[name]_[hash:8].[ext]',
          publicPath: "https://www.tencent.com",
        },
      },
    ],
  },
};

css文件里的圖片路徑變成如下:

/* index.less */
.tag {
  background-color: red;
  background-image: url(./webpack.png);
}
/* 編譯后:*/
background-image: url(https://www.tencent.com/webpack_605dc7bf.png);

6. url-loader:

它與file-loader作用相似,也是處理圖片的,只不過url-loader可以設(shè)置一個根據(jù)圖片大小進行不同的操作,如果該圖片大小大于指定的大小,則將圖片進行打包資源,否則將圖片轉(zhuǎn)換為base64字符串合并到j(luò)s文件里。

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|jpeg)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              name: '[name]_[hash:8].[ext]',
              // 這里單位為(b) 10240 => 10kb
              // 這里如果小于10kb則轉(zhuǎn)換為base64打包進js文件,如果大于10kb則打包到對應(yīng)目錄
              limit: 10240,
            }
          }
        ]
      }
    ]
  }
}

7. svg-sprite-loader

會把引用的 svg文件 塞到一個個 symbol 中,合并成一個大的SVG sprite,使用時則通過 SVG 的 <use> 傳入圖標(biāo) id 后渲染出圖標(biāo)。最后將這個大的 svg 放入 body 中。symbol的id如果不特別指定,就是你的文件名。

該loader可以搭配****svgo-loader**** 一起使用,svgo-loader是svg的優(yōu)化器,它可以刪除和修改SVG元素,折疊內(nèi)容,移動屬性等,具體不展開描述。感興趣的可以移步 [官方介紹]https://github.com/svg/svgo-loader)。

****用途:可以用來開發(fā)統(tǒng)一的圖標(biāo)管理庫。****

****示例代碼:****

// js文件里用法
import webpack from './webpack/webpack.svg';
const type = 'webpack';
const svg =  `<svg>
    <use xlink:href="#${type}"/>
  </svg>`;
const dom = `<div class="tag">
  ${svg}
  </div>`;
document.getElementById('react-app').innerHTML = dom;
// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|jpeg)$/,
        use: [
          {
            test: /\.svg$/,
            use: [
                {
                  loader: 'svg-sprite-loader'
                },
                'svgo-loader'
            ]
          },
        ]
      }
    ]
  }
}

原理:利用 svg 的 symbol 元素,將每個 icon 包裹在 symbol 中,通過 use 使用該 symbol。

8. style-loader

通過注入<style>標(biāo)簽將CSS插入到DOM中

****注意:****

  • 如果因為某些原因你需要將CSS提取為一個文件(即不要將CSS存儲在JS模塊中),此時你需要使用插件 ****mini-css-extract-plugin****(后面的Pugin部分會介紹);
  • 對于development模式(包括 webpack-dev-server)你可以使用style-loader,因為它是通過<style></style>標(biāo)簽的方式引入CSS的,加載會更快;
  • 不要將 style-loader 和 mini-css-extract-plugin 針對同一個CSS模塊一起使用!

代碼示例見下文 postcss-loader

9. css-loader

僅處理css的各種加載語法(@import和url()函數(shù)等),就像 js 解析 import/require() 一樣

代碼示例見下文 postcss-loader

10. postcss-loader

PostCSS 是一個允許使用 JS 插件轉(zhuǎn)換樣式的工具。 這些插件可以檢查(lint)你的 CSS,支持 CSS Variables 和 Mixins, 編譯尚未被瀏覽器廣泛支持的先進的 CSS 語法,內(nèi)聯(lián)圖片,以及其它很多優(yōu)秀的功能。

PostCSS 在業(yè)界被廣泛地應(yīng)用。PostCSS 的 ****autoprefixer**** 插件是最流行的 CSS 處理工具之一。

autoprefixer 添加了瀏覽器前綴,它使用 Can I Use 上面的數(shù)據(jù)。

****安裝****

npm install postcss-loader autoprefixer --save-dev

****代碼示例:****

// webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const isDev = process.NODE_ENV === 'development';
module.exports = {
  module: {
    rules: [
      {
        test: /\.(css|less)$/,
        exclude: /node_modules/,
        use: [
          isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
            }
          },
          {
            loader: 'postcss-loader'
          },
          {
              loader: 'less-loader',
              options: {
                  lessOptions: {
                      javascriptEnabled: true
                  }
              }
          }
        ]
      }
    ]
  }
}

然后在項目根目錄創(chuàng)建postcss.config.js,并且設(shè)置支持哪些瀏覽器,必須設(shè)置支持的瀏覽器才會自動添加添加瀏覽器兼容

module.exports = {
  plugins: [
    require('precss'),
    require('autoprefixer')({
      'browsers': [
        'defaults',
        'not ie < 11',
        'last 2 versions',
        '> 1%',
        'iOS 7',
        'last 3 iOS versions'
      ]
    })
  ]
}

截止到目前,PostCSS 有 200 多個插件。你可以在 插件列表搜索目錄 找到它們

了解更多請移步 鏈接

11. less-loader

解析less,轉(zhuǎn)換為css

****代碼示例見上文 postcss-loader****

了解更多請移步 鏈接

Plugin

Plugin簡介

Webpack 就像一條生產(chǎn)線,要經(jīng)過一系列處理流程后才能將源文件轉(zhuǎn)換成輸出結(jié)果。 這條生產(chǎn)線上的每個處理流程的職責(zé)都是單一的,多個流程之間有存在依賴關(guān)系,只有完成當(dāng)前處理后才能交給下一個流程去處理。 插件就像是一個插入到生產(chǎn)線中的一個功能,在特定的時機對生產(chǎn)線上的資源做處理。

Webpack 通過 Tapable 來組織這條復(fù)雜的生產(chǎn)線。 Webpack 在運行過程中會廣播事件,插件只需要監(jiān)聽它所關(guān)心的事件,就能加入到這條生產(chǎn)線中,去改變生產(chǎn)線的運作。 Webpack 的事件流機制保證了插件的有序性,使得整個系統(tǒng)擴展性很好。

——「深入淺出 Webpack」

常用Plugin

1. copy-webpack-plugin

將已經(jīng)存在的單個文件或整個目錄復(fù)制到構(gòu)建目錄。

const CopyPlugin = require("copy-webpack-plugin");

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        { 
          from: './template/page.html', 
          to: `${__dirname}/output/cp/page.html` 
        },
      ],
    }),
  ],
};

2. html-webpack-plugin

基本作用是生成html文件

  • 單頁應(yīng)用可以生成一個html入口,多頁應(yīng)用可以配置多個html-webpack-plugin實例來生成多個頁面入口
  • 為html引入外部資源如script、link,將entry配置的相關(guān)入口chunk以及mini-css-extract-plugin抽取的css文件插入到基于該插件設(shè)置的template文件生成的html文件里面,具體的方式是link插入到head中,script插入到head或body中。
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: {
    news: [path.resolve(__dirname, '../src/news/index.js')],
    video: path.resolve(__dirname, '../src/video/index.js'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'news page',
      // 生成的文件名稱 相對于webpackConfig.output.path路徑而言
      filename: 'pages/news.html',
      // 生成filename的文件模板
      template: path.resolve(__dirname, '../template/news/index.html'),
      chunks: ['news']

    }),
    new HtmlWebpackPlugin({
      title: 'video page',
      // 生成的文件名稱
      filename: 'pages/video.html',
      // 生成filename的文件模板
      template: path.resolve(__dirname, '../template/video/index.html'),
      chunks: ['video']
    }),
  ]
};

3. clean-webpack-plugin

默認情況下,這個插件會刪除webpack的output.path中的所有文件,以及每次成功重新構(gòu)建后所有未使用的資源。

這個插件在生產(chǎn)環(huán)境用的頻率非常高,因為生產(chǎn)環(huán)境經(jīng)常會通過 hash 生成很多 bundle 文件,如果不進行清理的話每次都會生成新的,導(dǎo)致文件夾非常龐大。

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

4. mini-css-extract-plugin

本插件會將 CSS 提取到單獨的文件中,為每個包含 CSS 的 JS 文件創(chuàng)建一個 CSS 文件。

// 建議 mini-css-extract-plugin 與 css-loader 一起使用
// 將 loader 與 plugin 添加到 webpack 配置文件中
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  plugins: [new MiniCssExtractPlugin()],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],

      }
    ],
  },
};

可以結(jié)合上文關(guān)于style-loader的介紹一起了解該插件。

5. webpack.HotModuleReplacementPlugin

模塊熱替換插件,除此之外還被稱為 HMR。

該功能會在應(yīng)用程序運行過程中,替換、添加或刪除 模塊,而無需重新加載整個頁面。主要是通過以下幾種方式,來顯著加快開發(fā)速度:

  • 保留在完全重新加載頁面期間丟失的應(yīng)用程序狀態(tài)。
  • 只更新變更內(nèi)容,以節(jié)省寶貴的開發(fā)時間。
  • 在源代碼中 CSS/JS 產(chǎn)生修改時,會立刻在瀏覽器中進行更新,這幾乎相當(dāng)于在瀏覽器 devtools 直接更改樣式。

****啟動方式有2種:****

  • 引入插件webpack.HotModuleReplacementPlugin 并且設(shè)置devServer.hot: true
  • 命令行加 --hot參數(shù)

****package.json配置:****

{
  "scripts": {
    "start": "NODE_ENV=development webpack serve --progress --mode=development --config=scripts/dev.config.js --hot"
  }
}

****webpack的配置如下:****

// scripts/dev.config.js文件
const webpack = require('webpack');
const path = require('path');
const outputPath = path.resolve(__dirname, './output/public');

module.exports = {
  mode: 'development',
  entry: {
    preview: [
      './node_modules/webpack-dev-server/client/index.js?path=http://localhost:9000',
      path.resolve(__dirname, '../src/preview/index.js')
    ],
  },
  output: {
    filename: 'static/js/[name]/index.js',
    // 動態(tài)生成的chunk在輸出時的文件名稱
    chunkFilename: 'static/js/[name]/chunk_[chunkhash].js',
    path: outputPath
  },
  plugins: [
    // 大多數(shù)情況下不需要任何配置
    new webpack.HotModuleReplacementPlugin(),
  ],
  devServer: {
        // 僅在需要提供靜態(tài)文件時才進行配置
        contentBase: outputPath,
        // publicPath: '', // 值默認為'/'
        compress: true,
        port: 9000,
        watchContentBase: true,
        hot: true,
        // 在服務(wù)器啟動后打開瀏覽器
        open: true,
        // 指定打開瀏覽器時要瀏覽的頁面
        openPage: ['pages/preview.html'],
        // 將產(chǎn)生的文件寫入硬盤。 寫入位置為 output.path 配置的目錄
        writeToDisk: true,
    }
}

****注意:HMR 絕對不能被用在生產(chǎn)環(huán)境。****

6. webpack.DefinePlugin

創(chuàng)建一個在編譯時可以配置的全局常量。這會對開發(fā)模式和生產(chǎn)模式的構(gòu)建允許不同的行為非常有用。

因為這個插件直接執(zhí)行文本替換,給定的值必須包含字符串本身內(nèi)的實際引號。

通常,有兩種方式來達到這個效果,使用'"production"', 或者使用 JSON.stringify('production')

// webpack.config.js
const isProd = process.env.NODE_ENV === 'production';
module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      PAGE_URL: JSON.stringify(isProd
        ? 'https://www.tencent.com/page'
        : 'http://testsite.tencent.com/page'
      )
    }),
  ]
}

// 代碼里面直接使用
console.log(PAGE_URL);

7. webpack-bundle-analyzer

可以看到項目各模塊的大小,可以按需優(yōu)化.一個webpack的bundle文件分析工具,將bundle文件以可交互縮放的treemap的形式展示。

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}

****啟動服務(wù):****

  • 生產(chǎn)環(huán)境查看:NODE_ENV=production npm run build
  • 開發(fā)環(huán)境查看:NODE_ENV=development npm run start

****最終效果:****

了解更多請移步 鏈接

8. SplitChunksPlugin

代碼分割。

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  optimization: {
    splitChunks: {
      // 分隔符
      // automaticNameDelimiter: '~',
      // all, async, and initial
      chunks: 'all',
      // 它可以繼承/覆蓋上面 splitChunks 中所有的參數(shù)值,除此之外還額外提供了三個配置,分別為:test, priority 和 reuseExistingChunk
      cacheGroups: {
        vendors: {
          // 表示要過濾 modules,默認為所有的 modules,可匹配模塊路徑或 chunk 名字,當(dāng)匹配的是 chunk 名字的時候,其里面的所有 modules 都會選中
          test: /[\\/]node_modules\/antd\//,
          // priority:表示抽取權(quán)重,數(shù)字越大表示優(yōu)先級越高。因為一個 module 可能會滿足多個 cacheGroups 的條件,那么抽取到哪個就由權(quán)重最高的說了算;
          // priority: 3,
          // reuseExistingChunk:表示是否使用已有的 chunk,如果為 true 則表示如果當(dāng)前的 chunk 包含的模塊已經(jīng)被抽取出去了,那么將不會重新生成新的。
          reuseExistingChunk: true,
          name: 'antd'
        }
      }
    }
  },
}
?著作權(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ù)。

相關(guān)閱讀更多精彩內(nèi)容

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