前言
我們先思考一個問題:如果不使用webpack,前端能夠開發專案嗎?
先彆著急說出答案,我們繼續往下看...
工作中,我們都是使用框架的腳手架進行開發,主流的趨勢...
vue-cli & create-react-app和webpack的關係
我們知道,無論是Vue的vue-cli還是React的create-react-app這樣的腳手架,實際上都是給webpack做了一層封裝,包了一層殼子,並預設了一些預設常用的配置項(當然還有別的東西),以便提升開發效率。
所以它們的關係是:腳手架只是給webpack穿上了一個馬甲...
不過有時候腳手架提供的預設配置項不夠用了,就需要我們手動去寫一些plugin或者loader從而實現我們想要的功能。
學習本文的收穫
- 通俗易懂地回顧webpack知識點
- 學習在vue-cli腳手架中寫webpack的plugin的知識點
- 學習在vue-cli腳手架中寫webpack的loader的知識點
webpack
平常我們寫程式碼都是模組化、元件化(工程化)
進行開發,導致程式碼會進行拆分、細化、公共部分提取、引用等...
為何要模組化、元件化(工程化)
進行開發呢?因為:這是軟體開發的主流和趨勢...
什麼是webpack & 談談你對webpack的理解
- 平常我們寫程式碼都是
模組化、元件化(工程化)
進行開發,導致程式碼會進行拆分、細化、公共部分提取、引用等... - 比如:我們會寫很多.vue檔案(當然還有別的檔案如.less等)。但是我們寫的程式碼是最終要被瀏覽器訪問解析執行的,而瀏覽器不認識.vue檔案,也不認識.less檔案!
- 瀏覽器不認識就不能解析,不能用。
- 瀏覽器倒是認識js、css、html,可以解析出相應內容,並渲染展示。
- 又因為 .vue檔案和.less檔案實際上也只是html、css、js
化妝之後
的樣式。 - 那這樣,搞一個工具,能夠讓.vue檔案和.less檔案
卸妝
成html、css、js就行了。 - 而webpack恰恰能夠做到這一點(編譯轉化打包)
所以,webpack就是:一個轉化編譯打包工具,將一些瀏覽器不認識的花裡胡哨的檔案轉化為瀏覽器認識的html、css、js檔案。
還記得我們最終打包好的dist資料夾目錄嗎?裡面就只有:
html、css、js等
一些資源...這樣描述,不是十分嚴謹。準確來說,webpack是一個靜態模組資源打包工具,官網:https://webpack.docschina.org/concepts/
回到最開始的那個問題~
如果不使用webpack,前端能夠開發專案嗎?
- 問:如果不使用webpack,前端能夠開發專案嗎?
- 答:如果一個專案炒雞小,只是用來展示一點點東西,完全可以使用原生的html、css、js去寫,這樣的話,就用不到
我們要知道webpack的作用就是,去轉化編譯打包腳手架、工程化的大專案。如果是一個小專案,完全不需要去用工程化的方式開發,直接寫,寫完了丟到伺服器上,直接用
前端工程化 == 模組化 + 元件化 + 自動化 + ...
webpack的幾個重要概念
- 打包入口(entry)
- 打包輸出(output)
- 載入器(loader)
- 外掛(plugin)
- 模式(mode)
- nodejs環境(environment)
webpack打包入口-entry
- 我們知道,我們開發專案有很多檔案,比如a、b、c等。a引用了b中的東西,而b又引用了c中的東西。那麼打包翻譯的話,就需要指定從哪個檔案開始打包,打包的過程中如果遇到了有別的引用,就順藤摸瓜...
- webpack中預設打包的入口檔案是
./src/index.js
檔案,這個檔案中引用了好多別的檔案,所以webpack繼續順藤摸瓜尋找、編譯、轉化、打包。 - 而vue-cli腳手架中的入口檔案是src目錄下的main.js檔案(vue-cli改寫了webpack預設的入口檔案位置罷了)
- 這裡我們可以去vue-cli倉庫程式碼中去找到相關的程式碼,能看到指定的打包入口
vue-cli倉庫地址:https://github.com/vuejs/vue-cli
大家把程式碼下載下來,Code --> Download ZIP ,然後在vscode中開啟程式碼,在左上角第二個放大鏡中搜尋關鍵字:src/main.js
有很多的關鍵詞,其中有一個get entryFile
方法,程式碼如下:
/**
* Get the entry file taking into account typescript.
*
* @readonly
*/
get entryFile () {
if (this._entryFile) return this._entryFile
return (this._entryFile = fs.existsSync(this.resolve('src/main.ts')) ? 'src/main.ts' : 'src/main.js')
}
對應截圖如下:
其實vue-cli中有很多的地方的程式碼,都告知了我們vue-cli是將main.js作為webpack打包的入口的,大家多看看...
好了,至此我們見證了webpack的打包入口(entry)在vue-cli腳手架中的具體應用展現形式。大家也可以在create-react-app中去看一下webpack的打包入口檔案,一個意思
vue-cli
和webpack
的關係,換句話說,也像蘋果手機
和富士康
的關係...
webpack打包輸出-output
我們知道,平常專案寫完,釋出上線,打包輸出的一般都是一個dist資料夾(也可以改)
原始的webpack是這樣寫:
const path = require('path');
module.exports = {
entry: './src/index.js', // 從當前同級目錄下的index.js作為入口檔案,順藤摸瓜開始打包
output: {
path: path.resolve(__dirname, 'dist'), // 這裡的path值要是一個絕對路徑,如:E:\echarts\code\dist,所以要使用path模組來操作
filename: 'myDemo.js',
},
};
vue-cli中叫做outputDir並指定了預設值為dist(實際上就是webpack中的output,又是套殼子),我們透過在vue.config.js檔案中更改outputDir的值,即可修改打包換名字了
vue-cli中的程式碼如下:
exports.defaults = () => ({
// project deployment base
publicPath: '/',
// where to output built files
outputDir: 'dist', // 即為webpack中的output
// where to put static assets (js/css/img/font/...)
assetsDir: '',
// filename for index.html (relative to outputDir)
indexPath: 'index.html',
// ......
devServer: {
/*
open: process.platform === 'darwin',
host: '0.0.0.0',
port: 8080,
https: false,
hotOnly: false,
proxy: null, // string | Object
before: app => {}
*/
}
})
注意看,上述的引數,就是vue.config.js需要我們設定的一些引數
vue-cli中的webpack工作流程
- 我們在vue.config.js中寫的符合vue-cli語法的程式碼,會被傳遞到vue-cli程式碼中
- vue-cli接收到以後,會再轉化一下,轉化成為符合webpack語法的配置
- 並透過webpack-merge這個外掛,傳遞給webpack中。
- webpack拿到對應配置項以後,再執行相應的打包策略方式
create-react-app這個腳手架也是類似,大抵都是套殼子,定規則,拿引數(處理),丟給webpack去打包
模式(mode)
- 開發模式(development)
- 生產模式(production)
nodejs環境(environment)
我們知道webpack是用js語言寫的,在nodejs建立的環境中執行,我們可以透過專案中的node_modules/webpack/bin/webpack.js檔案看到 如下圖,看一下:
child_process為node的子程式
一目瞭然...
webpack工作流程
在nodejs環境下,從入口遞迴尋找各個模組(元件)依賴關係,去打包,遇到不能直接識別的比如.vue檔案、.less檔案,就使用對應的loader去解析它。另外如果還可以在webpack的生命週期鉤子的某一個時間節點,去操作打包的內容,從而控制打包的結果。
vue.config配置webpack外掛的方法,物件寫法或函式寫法
實際上,學習webpack學的就是,別的開發者或者公司去開發的loader或者plugin,學的是webpack的生態。
webpack載入器-loader
什麼是loader
loader顧名思義,就是載入的意思,載入什麼呢?載入webpack不能直接認識的檔案,載入好以後,以供webpack去打包。
webpack直接認識的只有js和json檔案內容
有哪些常見的loader
- vue-loader去載入.vue檔案
- react-loader用於載入react元件的
- style-loader將css樣式掛到style標籤下
- css-loader編譯css樣式檔案
- less-loader去載入.less樣式檔案
- postcss-loader給樣式加上瀏覽器字首
- file-loader和url-loader可以壓縮圖片資源(後者可壓成base64)
- babel-loader、ts-loader、eslint-loader等
loader執行順序
從下到上,從右到左。
簡單的loader之去除console.log
第一步,src目錄下新建資料夾myLoader,內建立js檔案removeConsole.js檔案
一個loader就是一個模組化的js檔案
第二步,暴露一個函式,loader本質上是一個函式,在函式體內部,可以去對程式碼(字串)進行加工
plugin也是類似,也可以對程式碼字串進行加工,不過功能更加強大
第三步,寫loader函式邏輯內容
const reg = /(console.log\()(.*)(\))/g;
module.exports = function (source) {
console.log('source', source);
source = source.replace(reg, "")
return source;
}
loader就是一個加工函式,回想起js中的經典的一句話,萬物皆可函式
第四步,在vue.config.js中的configureWebpack中新增使用自己寫的loader
/**
* 新增自己寫的模組loader
* */
module: {
rules: [
/**
* 對.vue和.js檔案生效,不包含node_modules大資料夾,載入器的位置在
* 當前目錄下的./src/myLoader/removeConsole.js
* */
// {
// test: /\.vue$/,
// exclude: /node_modules/,
// loader: path.resolve(__dirname, "./src/myLoader/removeConsole.js")
// },
// {
// test: /\.js$/,
// exclude: /node_modules/,
// loader: path.resolve(__dirname, "./src/myLoader/removeConsole.js")
// }
],
}
如果想要給loader傳參,接參,可以在loader函式內部使用this.query接收,或者npm i -D loader-utils工具包去進一步操作。
完整程式碼示例,見GitHub倉庫:https://github.com/shuirongshuifu/elementSrcCodeStudy
更多開發loader細節,見官方文件:
webpack外掛-plugin
什麼是plugin
- loader處理不了的,去使用plugin去處理
- webpack在打包資原始碼檔案時,也會有先後執行步驟,換句話說即為webpack的生命週期
- 所以我們可以在對應生命週期的鉤子函式中,去編寫一些程式碼從而影響最終的打包結果
- 這些編寫的程式碼,即為webpack的外掛(程式碼)
webpack的打包也會有很多生命週期,plugin就是在合適的時機,透過webpack提供的api,去改變輸出結果。注意,loader是一個轉換器,執行在打包之前,而plugin在整個編譯週期都起作用。
plugin結構
- plugin是一個class類(建構函式)
- 類中的constructor函式用來接參
- apply函式的compiler物件自帶很多的api可以供我們呼叫
- 透過這些api的使用最終影響打包結果
如下程式碼:
class myPlugin {
constructor(options) {
console.log("接參-->", options);
}
apply(compiler) {
console.log('上面有非常多api,可參見compiler列印結果', compiler)
}
}
module.exports = myPlugin
列印的compiler物件
透過下方的列印結果,我們的確可以看到compiler.hooks.xxx有很多暴露出的api
實際上去編寫webpack中的plugin就是,去合理使用Compiler的相關hooks
Compiler {
_pluginCompat: SyncBailHook {
_args: [ 'options' ],
taps: [ [Object], [Object], [Object] ],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
// 鉤子函式,即為生命週期
hooks: {
shouldEmit: SyncBailHook {
_args: [Array],
taps: [],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
done: AsyncSeriesHook {
_args: [Array],
taps: [Array],
interceptors: [],
call: undefined,
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
additionalPass: AsyncSeriesHook {
_args: [],
taps: [Array],
interceptors: [],
call: undefined,
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
beforeRun: AsyncSeriesHook {
_args: [Array],
taps: [Array],
interceptors: [],
call: undefined,
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
run: AsyncSeriesHook {
_args: [Array],
taps: [],
interceptors: [],
call: undefined,
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
emit: AsyncSeriesHook {
_args: [Array],
taps: [Array],
interceptors: [Array],
call: undefined,
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
assetEmitted: AsyncSeriesHook {
_args: [Array],
taps: [],
interceptors: [],
call: undefined,
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
afterEmit: AsyncSeriesHook {
_args: [Array],
taps: [Array],
interceptors: [Array],
call: undefined,
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
thisCompilation: SyncHook {
_args: [Array],
taps: [],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
compilation: SyncHook {
_args: [Array],
taps: [Array],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
normalModuleFactory: SyncHook {
_args: [Array],
taps: [Array],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
contextModuleFactory: SyncHook {
_args: [Array],
taps: [],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
beforeCompile: AsyncSeriesHook {
_args: [Array],
taps: [],
interceptors: [],
call: undefined,
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
compile: SyncHook {
_args: [Array],
taps: [],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
make: AsyncParallelHook {
_args: [Array],
taps: [Array],
interceptors: [],
call: undefined,
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
afterCompile: AsyncSeriesHook {
_args: [Array],
taps: [],
interceptors: [],
call: undefined,
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
watchRun: AsyncSeriesHook {
_args: [Array],
taps: [],
interceptors: [],
call: undefined,
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
failed: SyncHook {
_args: [Array],
taps: [],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
invalid: SyncHook {
_args: [Array],
taps: [Array],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
watchClose: SyncHook {
_args: [],
taps: [],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
infrastructureLog: SyncBailHook {
_args: [Array],
taps: [],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
environment: SyncHook {
_args: [],
taps: [],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
afterEnvironment: SyncHook {
_args: [],
taps: [],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
afterPlugins: SyncHook {
_args: [Array],
taps: [],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
afterResolvers: SyncHook {
_args: [Array],
taps: [],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
entryOption: SyncBailHook {
_args: [Array],
taps: [],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
infrastructurelog: SyncBailHook {
_args: [Array],
taps: [],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
}
},
name: undefined,
parentCompilation: undefined,
outputPath: '',
outputFileSystem: NodeOutputFileSystem {
mkdirp: [Function: mkdirP] {
mkdirP: [Circular],
mkdirp: [Circular],
sync: [Function: sync]
},
mkdir: [Function: bound mkdir],
rmdir: [Function: bound rmdir],
unlink: [Function: bound unlink],
writeFile: [Function: bound writeFile],
join: [Function: bound join]
},
inputFileSystem: CachedInputFileSystem {
fileSystem: NodeJsInputFileSystem {},
_statStorage: Storage {
duration: 60000,
running: Map {},
data: Map {},
levels: [Array],
count: 0,
interval: null,
needTickCheck: false,
nextTick: null,
passive: true,
tick: [Function: bound tick]
},
_readdirStorage: Storage {
duration: 60000,
running: Map {},
data: Map {},
levels: [Array],
count: 0,
interval: null,
needTickCheck: false,
nextTick: null,
passive: true,
tick: [Function: bound tick]
},
_readFileStorage: Storage {
duration: 60000,
running: Map {},
data: Map {},
levels: [Array],
count: 0,
interval: null,
needTickCheck: false,
nextTick: null,
passive: true,
tick: [Function: bound tick]
},
_readJsonStorage: Storage {
duration: 60000,
running: Map {},
data: Map {},
levels: [Array],
count: 0,
interval: null,
needTickCheck: false,
nextTick: null,
passive: true,
tick: [Function: bound tick]
},
_readlinkStorage: Storage {
duration: 60000,
running: Map {},
data: Map {},
levels: [Array],
count: 0,
interval: null,
needTickCheck: false,
nextTick: null,
passive: true,
tick: [Function: bound tick]
},
_stat: [Function: bound bound ],
_statSync: [Function: bound bound ],
_readdir: [Function: bound readdir],
_readdirSync: [Function: bound readdirSync],
_readFile: [Function: bound bound readFile],
_readFileSync: [Function: bound bound readFileSync],
_readJson: [Function],
_readJsonSync: [Function],
_readlink: [Function: bound bound readlink],
_readlinkSync: [Function: bound bound readlinkSync]
},
recordsInputPath: null,
recordsOutputPath: null,
records: {},
removedFiles: Set {},
fileTimestamps: Map {},
contextTimestamps: Map {},
resolverFactory: ResolverFactory {
_pluginCompat: SyncBailHook {
_args: [Array],
taps: [Array],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined
},
hooks: { resolveOptions: [HookMap], resolver: [HookMap] },
cache2: Map {}
},
infrastructureLogger: [Function: logger],
resolvers: {
normal: { plugins: [Function: deprecated], apply: [Function: deprecated] },
loader: { plugins: [Function: deprecated], apply: [Function: deprecated] },
context: { plugins: [Function: deprecated], apply: [Function: deprecated] }
},
options: {
mode: 'development',
context: 'E:\\echarts\\code',
devtool: 'eval-cheap-module-source-map',
node: {
setImmediate: false,
process: 'mock',
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
console: false,
global: true,
Buffer: true,
__filename: 'mock',
__dirname: 'mock'
},
output: {
path: 'E:\\echarts\\code\\dist',
filename: 'js/[name].js',
publicPath: '/',
chunkFilename: 'js/[name].js',
globalObject: "(typeof self !== 'undefined' ? self : this)",
webassemblyModuleFilename: '[modulehash].module.wasm',
library: '',
hotUpdateFunction: 'webpackHotUpdate',
jsonpFunction: 'webpackJsonp',
chunkCallbackName: 'webpackChunk',
devtoolNamespace: '',
libraryTarget: 'var',
pathinfo: true,
sourceMapFilename: '[file].map[query]',
hotUpdateChunkFilename: '[id].[hash].hot-update.js',
hotUpdateMainFilename: '[hash].hot-update.json',
crossOriginLoading: false,
jsonpScriptType: false,
chunkLoadTimeout: 120000,
hashFunction: 'md4',
hashDigest: 'hex',
hashDigestLength: 20,
devtoolLineToLine: false,
strictModuleExceptionHandling: false
},
resolve: {
alias: [Object],
extensions: [Array],
modules: [Array],
plugins: [Array],
unsafeCache: true,
mainFiles: [Array],
aliasFields: [Array],
mainFields: [Array],
cacheWithContext: true,
preferAbsolute: true,
ignoreRootsErrors: true,
roots: [Array]
},
resolveLoader: {
modules: [Array],
plugins: [Array],
unsafeCache: true,
mainFields: [Array],
extensions: [Array],
mainFiles: [Array],
cacheWithContext: true
},
module: {
noParse: /^(vue|vue-router|vuex|vuex-router-sync)$/,
rules: [Array],
unknownContextRequest: '.',
unknownContextRegExp: false,
unknownContextRecursive: true,
unknownContextCritical: true,
exprContextRequest: '.',
exprContextRegExp: false,
exprContextRecursive: true,
exprContextCritical: true,
wrappedContextRegExp: /.*/,
wrappedContextRecursive: true,
wrappedContextCritical: false,
strictExportPresence: false,
strictThisContextOnImports: false,
unsafeCache: true,
defaultRules: [Array]
},
optimization: {
splitChunks: [Object],
minimizer: [Array],
removeAvailableModules: false,
removeEmptyChunks: true,
mergeDuplicateChunks: true,
flagIncludedChunks: false,
occurrenceOrder: false,
sideEffects: false,
providedExports: true,
usedExports: false,
concatenateModules: false,
runtimeChunk: undefined,
noEmitOnErrors: false,
checkWasmTypes: false,
mangleWasmImports: false,
namedModules: true,
hashedModuleIds: false,
namedChunks: true,
portableRecords: false,
minimize: false,
nodeEnv: 'development'
},
plugins: [
VueLoaderPlugin {},
[DefinePlugin],
[CaseSensitivePathsPlugin],
[FriendlyErrorsWebpackPlugin],
[HtmlWebpackPlugin],
[PreloadPlugin],
[PreloadPlugin],
[CopyPlugin],
[HotModuleReplacementPlugin],
[ProgressPlugin],
HelloPlugin {}
],
entry: { app: [Array] },
cache: true,
target: 'web',
performance: false,
infrastructureLogging: { level: 'info', debug: false }
},
context: 'E:\\echarts\\code',
requestShortener: RequestShortener {
currentDirectoryRegExp: /(^|!)E:\/echarts\/code/g,
parentDirectoryRegExp: /(^|!)E:\/echarts\//g,
buildinsAsModule: true,
buildinsRegExp: /(^|!)E:\/echarts\/code\/node_modules\/_webpack@4\.46\.0@webpack/g,
cache: Map {}
},
running: false,
watchMode: false,
_assetEmittingSourceCache: WeakMap { <items unknown> },
_assetEmittingWrittenFiles: Map {},
watchFileSystem: NodeWatchFileSystem {
inputFileSystem: CachedInputFileSystem {
fileSystem: NodeJsInputFileSystem {},
_statStorage: [Storage],
_readdirStorage: [Storage],
_readFileStorage: [Storage],
_readJsonStorage: [Storage],
_readlinkStorage: [Storage],
_stat: [Function: bound bound ],
_statSync: [Function: bound bound ],
_readdir: [Function: bound readdir],
_readdirSync: [Function: bound readdirSync],
_readFile: [Function: bound bound readFile],
_readFileSync: [Function: bound bound readFileSync],
_readJson: [Function],
_readJsonSync: [Function],
_readlink: [Function: bound bound readlink],
_readlinkSync: [Function: bound bound readlinkSync]
},
watcherOptions: { aggregateTimeout: 200 },
watcher: EventEmitter {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
options: [Object],
watcherOptions: [Object],
fileWatchers: [],
dirWatchers: [],
mtimes: [Object: null prototype] {},
paused: false,
aggregatedChanges: [],
aggregatedRemovals: [],
aggregateTimeout: 0,
_onTimeout: [Function: bound _onTimeout]
}
}
}
簡單的plugin寫一個生成的靜態資原始檔
外掛程式碼
class myPlugin {
constructor(options) { // constructor建構函式接收new myPlugin(params)時傳遞的引數params
console.log("我是new這個類時所傳遞的引數-->", options);
}
apply(compiler) {
// console.log('^_^', compiler) // 上面有非常多api,可供使用(參見compiler列印結果)
compiler.hooks.emit.tapAsync('lss',(compliation,cb)=>{
console.log('compliation',compliation.assets);
const content=`
- 生活不易
- 打工仔加油努力
- 奧利給
- ???
`
compliation.assets['FBI-WARNING.md'] = {
size() {
return content.length
},
source: function () {
return content
}
}
cb()
})
}
}
module.exports = myPlugin
vue.config.js檔案中使用外掛
// 引入這個外掛
const myPlugin = require('./src/plugin/myPlugin')
configureWebpack: {
// 在plugins陣列中例項化物件,若需要傳參,變傳遞引數
plugins: [
new myPlugin('我是引數')
]
},
未完待續。今天就先寫(水)到這裡吧