前言

咱们先思考一个问题:如果不应用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-cliwebpack的关系,换句话说,也像苹果手机富士康的关系...

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工作流程

  1. 咱们在vue.config.js中写的合乎vue-cli语法的代码,会被传递到vue-cli代码中
  2. vue-cli接管到当前,会再转化一下,转化成为合乎webpack语法的配置
  3. 并通过webpack-merge这个插件,传递给webpack中。
  4. 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('我是参数')    ]  },
未完待续。明天就先写(水)到这里吧