Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

手写webpack plugin #40

Open
zgfang1993 opened this issue Mar 8, 2020 · 0 comments
Open

手写webpack plugin #40

zgfang1993 opened this issue Mar 8, 2020 · 0 comments

Comments

@zgfang1993
Copy link
Owner

zgfang1993 commented Mar 8, 2020

手写 webpack plugin

以下面几个例子来说明如果写webpack plugin:

  • 同步插件 DonePlugin
  • 异步插件 AsyncPlugin
  • 实现内联资源的插件 InlineSourcePlugins
  • 生成一个打包文件信息汇总文件的插件 FileListPlugin

webpack.config.js

const path = require('path')
const DonePlugin = require('./plugins/DonePlugins')
const AsyncPlugins = require('./plugins/AsyncPlugins')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FileListPlugin = require('./plugins/FileListPlugin')
const InlineSourcePlugins = require('./plugins/InlineSourcePlugins')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'build.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      }
    ]
  },
  plugins: [
    new DonePlugin(), // 同步插件
    new AsyncPlugins(), // 异步插件
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html'
    }),
    new MiniCssExtractPlugin({
      filename: 'index.css'
    }),
    new InlineSourcePlugins({
      match: /\.(js|css)/
    }),
    new FileListPlugin({
      filename: 'list.md'
    })
  ]
}

./plugins/DonePlugins

class DonePlugins {
  apply(compiler) {
    console.log('DonePlugins');
    compiler.hooks.done.tap('DonePlugin', (stats) => {
      console.log('编译完成');
    })
  }
}


module.exports = DonePlugins

./plugins/AsyncPlugins

class AsyncPlugins {
  apply(compiler) {
    console.log('AsyncPlugins');

    compiler.hooks.emit.tapAsync('AsyncPlugin', (complete, callback) => {
      setTimeout(() => {
        console.log('1s后完成');
        callback()
      }, 1000)
    })

    compiler.hooks.emit.tapPromise('AsyncPlugin', (complete, callback) => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('再等1s');
          resolve()
        }, 1000)
      })
    })
  }
}


module.exports = AsyncPlugins

./plugins/FileListPlugin

生成一个打包文件信息汇总文件,到dist目录下。

class FileListPlugin {
  constructor({ filename }) {
    this.filename = filename
  }
  apply(compiler) {
    compiler.hooks.emit.tap('FileListPlugin', (compilation) => {
      let assets = compilation.assets; // {'build.js': {}, 'index.html': {}}
      let content = `## 文件名  资源大小\r\n`

      //[['build.js', {}], ['index.html', {}]]
      Object.entries(assets).forEach(([filename, stateObj]) => {
        content += `- ${filename}    ${stateObj.size()}\r\n`
      })

      //  在最终输出到dist的文件对象上,加上list.md文件
      assets[this.filename] = {
        source() {
          return content;
        },
        size() {
          return content.length
        }
      }
    })
  }
}

module.exports = FileListPlugin

dist/list.md

使用FileListPlugin插件,在dist目录生成list.md文件。

## 文件名  资源大小
- build.js    3860
- index.html    252

./plugin/InlineSourcePlugins 内联的webpack插件

打包后把css、js内联在index.html文件中

const HtmlWebpackPlugin = require('html-webpack-plugin')

/**
 * 把外链的标签编程内联的标签
 * link -> style
 * script src -> 内联script
 */
class InlineSourcePlugins {
  constructor({ match }) {
    this.reg = match  //  /\.(js|css)/
  }

  // 处理单个标签
  processTag(tag, compilation) {
    let newTag = {}
    let url = ''

    // link -> style
    if (tag.tagName === 'link' && this.reg.test(tag.attributes.href)) {
      newTag = {
        tagName: 'style',
        attributes: { type: 'text/css' }
      }
      url = tag.attributes.href
    } 
    // script 转内联
    else if (tag.tagName === 'script' && this.reg.test(tag.attributes.src)) {
      newTag = {
        tagName: 'script',
        attributes: { type: 'application/javascript' }
      }
      url = tag.attributes.src
    }
    
    if (url) {
      newTag.innerHTML = compilation.assets[url].source(); // 获取文件内容,放到标签内
      delete compilation.assets[url]   // 删除原来的资源

      return newTag
    }

    return tag
  }

  // 处理引入标签的数据
  processTags(data, compilation) {
    const headTags = []
    const bodyTags = []

    data.headTags.forEach(headTag => {
      headTags.push(this.processTag(headTag, compilation))
    })
    data.bodyTags.forEach(bodyTag => {
      bodyTags.push(this.processTag(bodyTag, compilation))
    })

    // console.log({ ...data, headTags, bodyTags })
    return { ...data, headTags, bodyTags }
  }

  apply(compiler) {
    compiler.hooks.compilation.tap('InlineSourcePlugins', (compilation) => {
      // HtmlWebpackPlugin有一个更改标签的资源组的钩子
      HtmlWebpackPlugin.getHooks(compilation).alterAssetTagGroups.tapAsync(
        'alertPlugin',
        (data, callback) => {
          data = this.processTags(data, compilation)   // compilation.assets 资源的链接
          callback(null, data)
        })
    })

  }
}

module.exports = InlineSourcePlugins

alterAssetTagGroups钩子中返回的data数据:

{ headTags: [ { tagName: 'link', voidTag: true, attributes: [Object] } ],
  bodyTags:
   [ { tagName: 'script', voidTag: false, attributes: [Object] } ],
  outputName: 'index.html',
  plugin:
   HtmlWebpackPlugin {
     options:
      { template:
         '/Users/zhangguifang/Desktop/code/learning/webpack/webpack-plugin/node_modules/html-webpack-plugin/lib/loader.js!/Users/zhangguifang/Desktop/code/learning/webpack/webpack-plugin/src/index.html',
        templateContent: false,
        templateParameters: [Function: templateParametersGenerator],
        filename: 'index.html',
        hash: false,
        inject: true,
        compile: true,
        favicon: false,
        minify: 'auto',
        cache: true,
        showErrors: true,
        chunks: 'all',
        excludeChunks: [],
        chunksSortMode: 'auto',
        meta: {},
        base: false,
        title: 'Webpack App',
        xhtml: false },
     childCompilerHash: '2ff53904581eec6eae0e',
     childCompilationOutputName: 'index.html',
     assetJson: '["build.js","index.css"]',
     hash: undefined,
     version: 4 } }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant