likes
comments
collection
share

回顾webpack在vue.config.js中写loader和plugin

作者站长头像
站长
· 阅读数 15

本文正在参加「金石计划」

前言

我们先思考一个问题:如果不使用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是一个静态模块资源打包工具,官网: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仓库地址:github.com/vuejs/vue-c…

大家把代码下载下来,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')
}

对应截图如下:

回顾webpack在vue.config.js中写loader和plugin

其实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文件看到 如下图,看一下:

回顾webpack在vue.config.js中写loader和plugin

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仓库:github.com/shuirongshu…

更多开发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('我是参数')
    ]
  },


从0到1安装一下webpack

从0到1搭建webpack有两个核心包不能遗漏,一个是核心webpack,另外一个是webpack的脚手架,叫做webpack-cli。是的webpack也有脚手架...

第一步,初始化webpack

  • npm init -y
  • 会生成一个package.json文件

如下:

{
  "name": "webpack1",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

第二步,安装webpack和webpack-cli

cnpm i webpack@4.46.0 webpack-cli@4.10.0 -D

-D表示是开发依赖,上线的时候不需要,这里要指定版本,暂时先使用4版本的webpack,最新版本的缓一缓,等5版本的webpack生态都差不多齐全了,再使用(上述是最新版本的,可在npm官网上看到)

结果如下:

回顾webpack在vue.config.js中写loader和plugin

第三步,执行npx webpack命令去打包webpack

npx webpack执行逻辑,会去找node_modules/.bin/webpack.cmd文件,可以打开大依赖文件夹看到

@ECHO off
SETLOCAL
CALL :find_dp0

SET _maybeQuote="
IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET _maybeQuote=
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

%_maybeQuote%%_prog%%_maybeQuote%  "%dp0%\..\_webpack@4.46.0@webpack\bin\webpack.js" %*
ENDLOCAL
EXIT /b %errorlevel%
:find_dp0
SET dp0=%~dp0
EXIT /b

上述代码意思:如果当前目录下有node.exe文件,就去执行node.exe,不过是没有的,就走ELSE,就去执行 当前目录下的上一级的webpack文件:_webpack@4.46.0@webpack\bin\webpack.js

即webpack文件夹下的bin文件夹下的webpack.js文件。

let notify = "One CLI for webpack must be installed. These are recommended choices, delivered as separate packages:";

即为提示你要安装webpack的cli脚手架

打包以后,会生成一个dist文件夹,内容main.js,可以右键run code运行,会输出结果。

回顾webpack在vue.config.js中写loader和plugin

默认打包生产模式

执行npx webpack会有提示:

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

// 即,让我们加配置

第四步,webpack打包支持模块化代码

所以我们可以模块化拆分写,再引入,webpack依旧可以打包的,如下:

回顾webpack在vue.config.js中写loader和plugin

因为webpack是node的写法,所以这里的模块化的模块暴露和模块导出,都是遵循node的模块化的规范,不能使用ES6的模块化规范的

第五步,新增webpack.config.js文件用于精细化控制webpack打包

为何配置文件叫做webpack.config.js参见node_modules/webpack-cli/lib/webpack-cli.js文件,如下代码:

const builtInFlags = [
    // For configs
    {
        name: "config",
        alias: "c",
        configs: [
            {
                type: "string",
            },
        ],
        multiple: true,
        description: "Provide path to a webpack configuration file e.g. ./webpack.config.js.",
    },
    // ......
]

打包出来的文件.js实际上是一个 自执行函数,很多原生js模块都是依赖自执行函数实现的

webpack内置插件服务webpack-dev-server

原理是通过express实现的开发服务

安装:cnpm i webpack-dev-server -D

也是开发依赖,生产不需要


未完待续。今天就先写(水)到这里吧

转载自:https://juejin.cn/post/7208823936301826107
评论
请登录