YYDS: Desenvolvimento do plugin do Webpack

YYDS: Desenvolvimento do plugin do Webpack
  Como um pequeno desenvolvedor que não está no front end há muito tempo, preciso webpackbater um papo . Quando comecei a entrar em contato com o webpack, qual foi a primeira reação (⊙_⊙)? Por que é tão complicado, parece tão difícil, esqueça! O tempo é uma coisa boa. À medida que a 前端工程化prática e a compreensão corretas se aprofundavam gradualmente, e mais e mais contato com o webpack, ele foi finalmente convencido por ele e não pôde deixar de gritar " webpack yyds(永远滴神)!"

  Eu queria escrever alguns artigos sobre webpack no meio do ano passado, mas foi atrasado por vários motivos (principalmente eu senti que não entendia o webpack o suficiente, e não me atrevi a escrevê-lo com arrogância); Toque no webpack, organize alguns "produtos de ano novo" e compartilhe com o xdm que precisar! Vou continuar a escrever alguns artigos [Webpack] no acompanhamento, supervisionado por xdm ···

Guia

  Este artigo apresenta principalmente o processo específico de desenvolvimento de plug-in por meio da implementação de um cdn优化plug - in. O meio envolverá o uso de plug-ins, a configuração de plug-ins webpack no projeto e a descrição dos pontos de conhecimento relacionados ao webpack. O texto completo tem mais de 2800 palavras e deve levar de 5 a 10 minutos. Espero que o xdm possa aprender, pensar e produzir depois de lê-lo!CdnPluginInjectwebpackpluginhtml-webpack-pluginvue/cli3+

Observação: os exemplos do artigo são baseados na vue/cli3+implantação do projeto!

1. Uso regular de CDN

index.html:

<head>
  ···
</head>
<body>
  <div id="app"></div>
  <script src="https://cdn.bootcss.com/vuex/3.1.0/vuex.min.js"></script>
  <script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.min.js"></script>
  ···
</body>

vue.config.js:

module.exports = {
  ···
  configureWebpack: {
    ···
    externals: {
      'vuex': 'Vuex',
      'vue-router': 'VueRouter',
      ···
    }
  },

2. Desenvolva um plugin webpack

O site oficial do webpack apresenta isso: O plug-in fornece aos desenvolvedores terceirizados recursos completos no mecanismo do webpack. Usando callbacks de construção em estágios, os desenvolvedores podem introduzir seus próprios comportamentos no processo de construção do webpack. Criar um plugin é mais avançado do que criar um carregador, porque você precisa entender alguns dos recursos internos subjacentes do webpack para implementar os ganchos correspondentes!

Um plug-in consiste no seguinte:

  • Uma função JavaScript nomeada.
  • Defina o método de aplicação em seu protótipo.
  • Especifique um gancho de evento próprio do webpack de toque .
  • Manipule dados específicos da instância dentro do webpack.
  • Chame o retorno de chamada fornecido pelo webpack depois que a função for implementada.
    // 一个 JavaScript class
    class MyExampleWebpackPlugin {
    // 将 `apply` 定义为其原型方法,此方法以 compiler 作为参数
    apply(compiler) {
    // 指定要附加到的事件钩子函数
     compiler.hooks.emit.tapAsync(
       'MyExampleWebpackPlugin',
       (compilation, callback) =&gt; {
         console.log('This is an example plugin!');
         console.log('Here’s the `compilation` object which represents a single build of assets:', compilation);
         // 使用 webpack 提供的 plugin API 操作构建结果
         compilation.addModule(/* ... */);
         callback();
       }
     );
    }
    }

Três, implementação de plug-in de otimização de CDN

Ideias:

  • 1, crie uma JavaScriptfunção nomeada (uso ES6de classrealização);
  • 2, é definido no applymétodo de protótipo ;
  • 3. Especifique um gancho de evento que toque o próprio webpack (o compilationgancho é tocado aqui : após a compilação ser criada, o plugin é executado);
  • 4, em caso de funcionamento do gancho index.html(para cdno script标签inserido no index.htmlcentro);
  • 5. Coloque-o em applyantes que o método é executado ;cdn的参数webpack外部扩展externals
  • 6. Chamada webpackfornecida após a função ser implementada callback;

Etapas de implementação:

1, crie um nome JavaScript(usando a função ES6de classrealização)

  Crie uma classe cdnPluginInject, adicione o construtor da classe para receber os parâmetros passados; aqui definimos o formato dos parâmetros recebidos da seguinte maneira:

modules:[
  {
    name: "xxx",    //cdn包的名字
    var: "xxx", //cdn引入库在项目中使用时的变量名
    path: "http://cdn.url/xxx.js" //cdn的url链接地址
  },
  ···
]

As variáveis ​​da classe de definição modulesrecebem os cdn参数resultados de processamento passados :

class CdnPluginInject {
  constructor({
    modules,
  }) {
    // 如果是数组,将this.modules变换成对象形式
    this.modules = Array.isArray(modules) ? { ["defaultCdnModuleKey"]: modules } : modules; 
  }
 ···
}
module.exports = CdnPluginInject;

2, é definido no applymétodo de protótipo

O plug-in é um construtor (o objeto de protótipo tem esse applymétodo de construtor ) instanciado. Este applymétodo de instalação do plug, será chamado uma vez compilador webpack. applyO método pode receber uma referência ao objeto compilador webpack, para que o objeto compilador possa ser acessado na função de retorno de chamada

cdnPluginInject.jscódigo mostrado abaixo:

class CdnPluginInject {
  constructor({
    modules,
  }) {
    // 如果是数组,将this.modules变换成对象形式
    this.modules = Array.isArray(modules) ? { ["defaultCdnModuleKey"]: modules } : modules; 
  }
  //webpack plugin开发的执行入口apply方法
  apply(compiler) {
    ···
  }

module.exports = CdnPluginInject;

3. Especifique um gancho de evento que toque o próprio webpack

  O compilationgancho é tocado aqui : após a compilação ser criada, o plugin é executado.

YYDS: Desenvolvimento do plugin do Webpack

  compilationSim compiler, uma função de gancho. A compilação criará uma nova instância do processo de compilação. Uma instância de compilação é adequada 访问所有模块和它们的依赖. Depois de obter esses módulos, você pode manipulá-los conforme necessário!

class CdnPluginInject {
  constructor({
    modules,
  }) {
    // 如果是数组,将this.modules变换成对象形式
    this.modules = Array.isArray(modules) ? { ["defaultCdnModuleKey"]: modules } : modules; 
  }
  //webpack plugin开发的执行入口apply方法
  apply(compiler) {
    //获取webpack的输出配置对象
    const { output } = compiler.options;
    //处理output.publicPath, 决定最终资源相对于引用它的html文件的相对位置
    output.publicPath = output.publicPath || "/";
    if (output.publicPath.slice(-1) !== "/") {
      output.publicPath += "/";
    }
    //触发compilation钩子函数
    compiler.hooks.compilation.tap("CdnPluginInject", compilation => { 
     ···
  }
}

module.exports = CdnPluginInject;

4. Operar no evento de ganchoindex.html

  Este passo é principalmente a ser alcançado para cdno script标签inserida index.htmlno ; como consegui-lo? No projeto vue, webpack realmente usa html-webpack-plugin para gerar .htmlarquivos ao empacotar , portanto, também podemos html-webpack-plugininserir tags de script cdn operando arquivos html aqui .

// 4.1 引入html-webpack-plugin依赖
const HtmlWebpackPlugin = require("html-webpack-plugin");

class CdnPluginInject {
  constructor({
    modules,
  }) {
    // 如果是数组,将this.modules变换成对象形式
    this.modules = Array.isArray(modules) ? { ["defaultCdnModuleKey"]: modules } : modules; 
  }
  //webpack plugin开发的执行入口apply方法
  apply(compiler) {
    //获取webpack的输出配置对象
    const { output } = compiler.options;
    //处理output.publicPath, 决定最终资源相对于引用它的html文件的相对位置
    output.publicPath = output.publicPath || "/";
    if (output.publicPath.slice(-1) !== "/") {
      output.publicPath += "/";
    }
    //触发compilation钩子函数
    compiler.hooks.compilation.tap("CdnPluginInject", compilation => { 
      // 4.2 html-webpack-plugin中的hooks函数,当在资源生成之前异步执行
      HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration
       .tapAsync("CdnPluginInject", (data, callback) => {   // 注册异步钩子
            //获取插件中的cdnModule属性(此处为undefined,因为没有cdnModule属性)
          const moduleId = data.plugin.options.cdnModule;  
          // 只要不是false(禁止)就行
          if (moduleId !== false) {    
             // 4.3得到所有的cdn配置项
            let modules = this.modules[                    
                moduleId || Reflect.ownKeys(this.modules)[0] 
            ];
            if (modules) {
              // 4.4 整合已有的js引用和cdn引用
              data.assets.js = modules
                .filter(m => !!m.path)
                .map(m => {
                  return m.path;
                })
                .concat(data.assets.js);
              // 4.5 整合已有的css引用和cdn引用
              data.assets.css = modules
                .filter(m => !!m.style)
                .map(m => {
                  return m.style;
                })
                .concat(data.assets.css); 
            }
          }
            // 4.6 返回callback函数
          callback(null, data);
        });
  }
}

module.exports = CdnPluginInject;

Em seguida, analise a realização acima, passo a passo:

  • 4.1. Introduzir a dependência html-webpack-plugin, nem é preciso dizer;
  • 4.2. html-webpack-pluginA hooksfunção na chamada é html-webpack-pluginexecutada de forma assíncrona antes de o recurso ser gerado; aqui está html-webpack-plugino autor se gabando sinceramente , ta html-webpack-pluginconstruiu uma série de funções de gancho no plugin durante o desenvolvimento para os desenvolvedores incorporarem diferentes operações em diferentes estágios de chamada do plugin ; portanto, aqui podemos usar html-webpack-pluginum beforeAssetTagGenerationpar de operação html;
  • 4.3 beforeAssetTagGenerationObtenha todos os dados de configuração que precisam ser importados pelo cdn in.
  • 4.4 Integrar referências js e referências cdn existentes; data.assets.jsvocê pode obter compilationtodos js资源os links / caminhos gerados no estágio (e finalmente inseridos em index.html), e cdn的path数据(cdn的url)mesclar aqueles que precisam ser configurados ;
  • 4.5. Integre as referências css e referências cdn existentes; data.assets.cssvocê pode obter compilationtodos css资源os links / caminhos gerados no estágio (e finalmente inseridos em index.html), e cdn的path数据(cdn的url)mesclar os tipos css que precisam ser configurados ;
  • 4.6. Retorne a função callback, o objetivo é dizer webpackque a operação foi concluída e você pode prosseguir para a próxima etapa;

5, definindo webpacko外部扩展externals

  Antes de o applymétodo ser executado, há mais uma etapa que deve ser concluída: cdn的参数configure 外部扩展externals-o, você pode compiler.options.externalsobter diretamente o atributo externals no webpack, e configurar os dados na configuração cdn após a operação.

6 、callback

  Retorne o callback para informar que o CdnPluginInjectplug-in do webpack foi concluído;

// 4.1 引入html-webpack-plugin依赖
const HtmlWebpackPlugin = require("html-webpack-plugin");

class CdnPluginInject {
  constructor({
    modules,
  }) {
    // 如果是数组,将this.modules变换成对象形式
    this.modules = Array.isArray(modules) ? { ["defaultCdnModuleKey"]: modules } : modules; 
  }
  //webpack plugin开发的执行入口apply方法
  apply(compiler) {
    //获取webpack的输出配置对象
    const { output } = compiler.options;
    //处理output.publicPath, 决定最终资源相对于引用它的html文件的相对位置
    output.publicPath = output.publicPath || "/";
    if (output.publicPath.slice(-1) !== "/") {
      output.publicPath += "/";
    }
    //触发compilation钩子函数
    compiler.hooks.compilation.tap("CdnPluginInject", compilation => { 
      // 4.2 html-webpack-plugin中的hooks函数,当在资源生成之前异步执行
      HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration
       .tapAsync("CdnPluginInject", (data, callback) => {   // 注册异步钩子
            //获取插件中的cdnModule属性(此处为undefined,因为没有cdnModule属性)
          const moduleId = data.plugin.options.cdnModule;  
          // 只要不是false(禁止)就行
          if (moduleId !== false) {    
             // 4.3得到所有的cdn配置项
            let modules = this.modules[                    
                moduleId || Reflect.ownKeys(this.modules)[0] 
            ];
            if (modules) {
              // 4.4 整合已有的js引用和cdn引用
              data.assets.js = modules
                .filter(m => !!m.path)
                .map(m => {
                  return m.path;
                })
                .concat(data.assets.js);
              // 4.5 整合已有的css引用和cdn引用
              data.assets.css = modules
                .filter(m => !!m.style)
                .map(m => {
                  return m.style;
                })
                .concat(data.assets.css); 
            }
          }
            // 4.6 返回callback函数
          callback(null, data);
        });

      // 5.1 获取externals
        const externals = compiler.options.externals || {};
      // 5.2 cdn配置数据添加到externals
      Reflect.ownKeys(this.modules).forEach(key => {
        const mods = this.modules[key];
        mods
          .forEach(p => {
          externals[p.name] = p.var || p.name; //var为项目中的使用命名
        });
      });
      // 5.3 externals赋值
      compiler.options.externals = externals; //配置externals

      // 6 返回callback
      callback();
  }
}

module.exports = CdnPluginInject;

  Neste ponto, um plugin completo do webpack CdnPluginInjectfoi desenvolvido! Vamos tentar a seguir.

Quatro, uso de plug-in otimizado para cdn

vue.config.jsApresente e use   no arquivo do projeto vue CdnPluginInject:

Arquivo de configuração cdn CdnConfig.js:

/*
 * 配置的cdn
 * @name: 第三方库的名字
 * @var: 第三方库在项目中的变量名
 * @path: 第三方库的cdn链接
 */
module.exports = [
  {
    name: "moment",
    var: "moment",
    path: "https://cdn.bootcdn.net/ajax/libs/moment.js/2.27.0/moment.min.js"
  },
  ···
];

Configure em configureWebpack:

const CdnPluginInject = require("./CdnPluginInject");
const cdnConfig = require("./CdnConfig");

module.exports = {
  ···
  configureWebpack: config => {
    //只有是生产山上线打包才使用cdn配置
    if(process.env.NODE.ENV =='production'){
      config.plugins.push(
        new CdnPluginInject({
          modules: CdnConfig
        })
      )
    }
  }
  ···
}

Configuração em chainWebpack:

const CdnPluginInject = require("./CdnPluginInject");
const cdnConfig = require("./CdnConfig");

module.exports = {
  ···
  chainWebpack: config => {
    //只有是生产山上线打包才使用cdn配置
    if(process.env.NODE.ENV =='production'){
      config.plugin("cdn").use(
        new CdnPluginInject({
          modules: CdnConfig
        })
      )
    }
  }
  ···
}

  Usando CdnPluginInject:

  • 1. Realizar a gestão e manutenção da otimização do CDN através da configuração;
  • 2. Realizar a otimização da configuração do CDN para diferentes ambientes (o ambiente de desenvolvimento usa diretamente as dependências de instalação local para depuração e o ambiente de produção se adapta ao modo CDN para otimizar o carregamento);

V. Resumo

  Após a leitura, deve haver webpackalgumas dúvidas dos marmanjos , este plugin não é a versão mendiga do webpack-cdn-plugin ! CdnPluginInjectÉ que eu webpack-cdn-pluginaprendo com o código-fonte, combinado com a versão de imitação que meu projeto realmente precisa modificar.Comparado webpack-cdn-plugincom o encapsulamento da geração do link cdn, CdnPluginInjecté configurar diretamente o link cdn , o que é mais fácil de escolher a configuração cdn. Se você quiser saber mais, pode olhar webpack-cdn-plugino código-fonte do xdm . Após a atualização iterativa contínua do autor, ele fornece parâmetros mais configuráveis ​​e funções mais poderosas (novamente).

重点:整理不易,觉得还可以的xdm记得 一键三连 哟!

Referência do artigo

Acho que você gosta

Origin blog.51cto.com/15066867/2597973
Recomendado
Clasificación