前言
我们先思考一个问题:如果不使用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('我是参数')
]
},
未完待续。今天就先写(水)到这里吧
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。