中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

webpack中loader和plugin的示例分析

發布時間:2021-08-07 13:52:37 來源:億速云 閱讀:135 作者:小新 欄目:web開發

這篇文章主要介紹webpack中loader和plugin的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

1 基礎回顧

首先我們先回顧一下webpack常見配置,因為后面會用到,所以簡單介紹一下。

1.1 webpack常見配置

// 入口文件
 entry: {
  app: './src/js/index.js',
 },
 // 輸出文件
 output: {
  filename: '[name].bundle.js',
  path: path.resolve(__dirname, 'dist'),
  publicPath: '/'   //確保文件資源能夠在 http://localhost:3000 下正確訪問
 },
 // 開發者工具 source-map
 devtool: 'inline-source-map',
 // 創建開發者服務器
 devServer: {
  contentBase: './dist',
  hot: true        // 熱更新
 },
 plugins: [
  // 刪除dist目錄
  new CleanWebpackPlugin(['dist']),
  // 重新穿件html文件
  new HtmlWebpackPlugin({
   title: 'Output Management'
  }),
  // 以便更容易查看要修補(patch)的依賴
  new webpack.NamedModulesPlugin(),
  // 熱更新模塊
  new webpack.HotModuleReplacementPlugin()
 ],
 // 環境
 mode: "development",
 // loader配置
 module: {
  rules: [
   {
    test: /\.css$/,
    use: [
     'style-loader',
     'css-loader'
    ]
   },
   {
    test: /\.(png|svg|jpg|gif)$/,
    use: [
     'file-loader'
    ]
   }
  ]
 }

這里面我們重點關注 module和plugins屬性,因為今天的重點是編寫loader和plugin,需要配置這兩個屬性。

1.2 打包原理

  • 識別入口文件

  • 通過逐層識別模塊依賴。(Commonjs、amd或者es6的import,webpack都會對其進行分析。來獲取代碼的依賴)

  • webpack做的就是分析代碼。轉換代碼,編譯代碼,輸出代碼

  • 最終形成打包后的代碼

這些都是webpack的一些基礎知識,對于理解webpack的工作機制很有幫助。

2 loader

OK今天第一個主角登場

2.1 什么是loader?

loader是文件加載器,能夠加載資源文件,并對這些文件進行一些處理,諸如編譯、壓縮等,最終一起打包到指定的文件中

  • 處理一個文件可以使用多個loader,loader的執行順序是和本身的順序是相反的,即最后一個loader最先執行,第一個loader最后執行。

  • 第一個執行的loader接收源文件內容作為參數,其他loader接收前一個執行的loader的返回值作為參數。最后執行的loader會返回此模塊的JavaScript源碼

2.2 手寫一個loader

需求:

  • 處理.txt文件

  • 對字符串做反轉操作

  • 首字母大寫

例如:abcdefg轉換后為Gfedcba

OK,我們開始

1)首先創建兩個loader(這里以本地loader為例)

為什么要創建兩個laoder?理由后面會介紹

webpack中loader和plugin的示例分析

reverse-loader.js

module.exports = function (src) {
 if (src) {
  console.log('--- reverse-loader input:', src)
  src = src.split('').reverse().join('')
  console.log('--- reverse-loader output:', src)
 }
 return src;
}

uppercase-loader.js

module.exports = function (src) {
 if (src) {
  console.log('--- uppercase-loader input:', src)
  src = src.charAt(0).toUpperCase() + src.slice(1)
  console.log('--- uppercase-loader output:', src)
 }
 // 這里為什么要這么寫?因為直接返回轉換后的字符串會報語法錯誤,
 // 這么寫import后轉換成可以使用的字符串
 return `module.exports = '${src}'`
}

看,loader結構是不是很簡單,接收一個參數,并且return一個內容就ok了。

然后創建一個txt文件

webpack中loader和plugin的示例分析

2)mytest.txt

abcdefg

3)現在開始配置webpack

module.exports = {
 entry: {
  index: './src/js/index.js'
 },
 plugins: [...],
 optimization: {...},
 output: {...},
 module: {
  rules: [
   ...,
   {
    test: /\.txt$/,
    use: [
     './loader/uppercase-loader.js',
     './loader/reverse-loader.js'
    ]
   }
  ]
 }
}

這樣就配置完成了

4)我們在入口文件中導入這個腳本

為什么這里需要導入呢,我們不是配置了webapck處理所有的.txt文件么?

因為webpack會做過濾,如果不引用該文件的話,webpack是不會對該文件進行打包處理的,那么你的loader也不會執行

import _ from 'lodash';
import txt from '../txt/mytest.txt'
import '../css/style.css'
function component() {
 var element = document.createElement('div');
 var button = document.createElement('button');
 var br = document.createElement('br');

 button.innerHTML = 'Click me and look at the console!';
 element.innerHTML = _.join('【' + txt + '】');
 element.className = 'hello'
 element.appendChild(br);
 element.appendChild(button);

 // Note that because a network request is involved, some indication
 // of loading would need to be shown in a production-level site/app.
 button.onclick = e => import(/* webpackChunkName: "print" */ './print').then(module => {
  var print = module.default;

  print();
 });

 return element;
}
document.body.appendChild(component());

package.json配置

{
 ...,
 "scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "build": "webpack --config webpack.prod.js",
  "start": "webpack-dev-server --open --config webpack.dev.js",
  "server": "node server.js"
 },
 ...
}

然后執行命令

npm run build

webpack中loader和plugin的示例分析

這樣我們的loader就寫完了。

現在回答為什么要寫兩個loader?

看到執行的順序沒,我們的配置的是這樣的

use: [
 './loader/uppercase-loader.js',
 './loader/reverse-loader.js'
]

正如前文所說, 處理一個文件可以使用多個loader,loader的執行順序是和本身的順序是相反的

我們也可以自己寫loader解析自定義模板,像vue-loader是非常復雜的,它內部會寫大量的對.vue文件的解析,然后會生成對應的html、js和css。

我們這里只是講述了一個最基礎的用法,如果有更多的需要,可以查看《loader官方文檔》

3 plugin

3.1 什么是plugin?

在 Webpack 運行的生命周期中會廣播出許多事件,Plugin 可以監聽這些事件,在合適的時機通過 Webpack 提供的 API 改變輸出結果。

plugin和loader的區別是什么?

對于loader,它就是一個轉換器,將A文件進行編譯形成B文件,這里操作的是文件,比如將A.scss或A.less轉變為B.css,單純的文件轉換過程

plugin是一個擴展器,它豐富了wepack本身,針對是loader結束后,webpack打包的整個過程,它并不直接操作文件,而是基于事件機制工作,會監聽webpack打包過程中的某些節點,執行廣泛的任務。

3.2 一個最簡的插件

/plugins/MyPlugin.js(本地插件)

class MyPlugin {
 // 構造方法
 constructor (options) {
  console.log('MyPlugin constructor:', options)
 }
 // 應用函數
 apply (compiler) {
  // 綁定鉤子事件
  compiler.plugin('compilation', compilation => {
   console.log('MyPlugin')
  ))
 }
}

module.exports = MyPlugin

webpack配置

const MyPlugin = require('./plugins/MyPlugin')
module.exports = {
 entry: {
  index: './src/js/index.js'
 },
 plugins: [
  ...,
  new MyPlugin({param: 'xxx'})
 ],
 ...
};

這就是一個最簡單的插件(雖然我們什么都沒干)

  • webpack 啟動后,在讀取配置的過程中會先執行 new MyPlugin(options) 初始化一個 MyPlugin 獲得其實例。

  • 在初始化 compiler 對象后,再調用 myPlugin.apply(compiler) 給插件實例傳入 compiler 對象。

  • 插件實例在獲取到 compiler 對象后,就可以通過 compiler.plugin(事件名稱, 回調函數) 監聽到 Webpack 廣播出來的事件。

  • 并且可以通過 compiler 對象去操作 webpack。

看到這里可能會問compiler是啥,compilation又是啥?

Compiler 對象包含了 Webpack 環境所有的的配置信息,包含 options,loaders,plugins 這些信息,這個對象在 Webpack 啟動時候被實例化,它是全局唯一的,可以簡單地把它理解為 Webpack 實例;

Compilation 對象包含了當前的模塊資源、編譯生成資源、變化的文件等。當 Webpack 以開發模式運行時,每當檢測到一個文件變化,一次新的 Compilation 將被創建。Compilation 對象也提供了很多事件回調供插件做擴展。通過 Compilation 也能讀取到 Compiler 對象。

Compiler 和 Compilation 的區別在于:

Compiler 代表了整個 Webpack 從啟動到關閉的生命周期,而 Compilation 只是代表了一次新的編譯。

3.3 事件流

  • webpack 通過 Tapable 來組織這條復雜的生產線。

  • webpack 的事件流機制保證了插件的有序性,使得整個系統擴展性很好。

  • webpack 的事件流機制應用了觀察者模式,和 Node.js 中的 EventEmitter 非常相似。

綁定事件

compiler.plugin('event-name', params => {
 ...	 
});

觸發事件

compiler.apply('event-name',params)

3.4 需要注意的點

  •  只要能拿到 Compiler 或 Compilation 對象,就能廣播出新的事件,所以在新開發的插件中也能廣播出事件,給其它插件監聽使用。

  • 傳給每個插件的 Compiler 和 Compilation 對象都是同一個引用。也就是說在一個插件中修改了 Compiler 或 Compilation 對象上的屬性,會影響到后面的插件。

  • 有些事件是異步的,這些異步的事件會附帶兩個參數,第二個參數為回調函數,在插件處理完任務時需要調用回調函數通知 webpack,才會進入下一處理流程 。例如:

compiler.plugin('emit',function(compilation, callback) {
 ...
  
 // 處理完畢后執行 callback 以通知 Webpack 
 // 如果不執行 callback,運行流程將會一直卡在這不往下執行 
 callback();
});

關于complier和compilation,webpack定義了大量的鉤子事件。開發者可以根據自己的需要在任何地方進行自定義處理。

3.5 手寫一個plugin

場景:

小程序mpvue項目,通過webpack編譯,生成子包(我們作為分包引入到主程序中),然后考入主包當中。生成子包后,里面的公共靜態資源wxss引用地址需要加入分包的前綴:/subPages/enjoy_given。

在未編寫插件前,生成的資源是這樣的,這個路徑如果作為分包引入主包,是沒法正常訪問資源的。

webpack中loader和plugin的示例分析

所以需求來了:

修改dist/static/css/pages目錄下,所有頁面的樣式文件(wxss文件)引入公共資源的路徑。

因為所有頁面的樣式都會引用通用樣式vender.wxss

那么就需要把@import "/static/css/vendor.wxss"; 改為:@import "/subPages/enjoy_given/static/css/vendor.wxss";復制代碼

OK 開始!

1)創建插件文件 CssPathTransfor.js

webpack中loader和plugin的示例分析

CssPathTransfor.js

class CssPathTransfor {
 apply (compiler) {
  compiler.plugin('emit', (compilation, callback) => {
   console.log('--CssPathTransfor emit')
   // 遍歷所有資源文件
   for (var filePathName in compilation.assets) {
    // 查看對應的文件是否符合指定目錄下的文件
    if (/static\/css\/pages/i.test(filePathName)) {
     // 引入路徑正則
     const reg = /\/static\/css\/vendor\.wxss/i
     // 需要替換的最終字符串
     const finalStr = '/subPages/enjoy_given/static/css/vendor.wxss'
     // 獲取文件內容
     let content = compilation.assets[filePathName].source() || ''
     
     content = content.replace(reg, finalStr)
     // 重寫指定輸出模塊內容
     compilation.assets[filePathName] = {
      source () {
       return content;
      },
      size () {
       return content.length;
      }
     }
    }
   }
   callback()
  })
 }
}
module.exports = CssPathTransfor

看著挺多,實際就是遍歷compilation.assets模塊。對符合要求的文件進行正則替換。

2)修改webpack配置

var baseWebpackConfig = require('./webpack.base.conf')
var CssPathTransfor = require('../plugins/CssPathTransfor.js')

var webpackConfig = merge(baseWebpackConfig, {
 module: {...},
 devtool: config.build.productionSourceMap ? '#source-map' : false,
 output: {...},
 plugins: [
  ...,
  // 配置插件
  new CssPathTransfor(),
 ]
})

插件編寫完成后,執行編譯命令

webpack中loader和plugin的示例分析

以上是“webpack中loader和plugin的示例分析”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

平潭县| 阜城县| 广安市| 保德县| 根河市| 共和县| 亳州市| 宣威市| 商都县| 宁海县| 安化县| 漾濞| 海门市| 普宁市| 治多县| 灵丘县| 巢湖市| 彭州市| 鹤庆县| 和政县| 富宁县| 拉孜县| 临洮县| 县级市| 孟村| 油尖旺区| 翁源县| 淮南市| 轮台县| 兰考县| 朝阳市| 毕节市| 长汀县| 荔波县| 东丰县| 大足县| 新竹市| 宜黄县| 清镇市| 清苑县| 集安市|