YYDS: desarrollo del complemento Webpack

YYDS: desarrollo del complemento Webpack
  Como pequeño desarrollador que no ha estado en el front-end durante mucho tiempo, tengo que hablar de ello webpack. Cuando comencé a contactar con webpack, ¿cuál fue la primera reacción (⊙_⊙)? ¿Por qué es tan complicado, se siente tan difícil, olvídalo! El tiempo es algo bueno. A medida que la 前端工程化práctica y la comprensión correctas se profundizaron gradualmente, y más y más contacto con el paquete web, finalmente lo convenció y no pudo evitar gritar " webpack yyds(永远滴神)!".

  Quería escribir algunos artículos sobre el paquete web a mediados del año pasado, pero se retrasó debido a varias razones (principalmente sentí que no entendía lo suficiente el paquete web y no me atrevía a escribirlo con arrogancia); Toque el paquete web, organice algunos "productos de año nuevo" y compártalos con xdm que lo necesite. Continuaré escribiendo algunos artículos de [Webpack] en el seguimiento, supervisado por xdm ···

Guía

  Este artículo presenta principalmente el proceso específico de desarrollo de complementos a través de la implementación de un cdn优化complemento . El medio implicará el uso de complementos, la configuración de complementos de paquetes web en el proyecto y la descripción de puntos de conocimiento relacionados con paquetes web. El texto completo tiene aproximadamente 2800 palabras y se espera que tome de 5 a 10 minutos. ¡Espero que xdm pueda aprender, pensar y generar resultados después de leerlo!CdnPluginInjectwebpackpluginhtml-webpack-pluginvue/cli3+

Nota: Los ejemplos del artículo se basan en la vue/cli3+implementación del proyecto.

1. Uso habitual 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. Desarrollar un complemento de paquete web

El sitio web oficial del paquete web presenta esto: el complemento proporciona a los desarrolladores externos capacidades completas en el motor del paquete web. Utilizando devoluciones de llamada de compilación por etapas, los desarrolladores pueden introducir sus propios comportamientos en el proceso de compilación del paquete web. Crear un complemento es más avanzado que crear un cargador, porque necesitará comprender algunas de las características internas subyacentes del paquete web para implementar los ganchos correspondientes.

Un complemento consta de lo siguiente:

  • Una función de JavaScript con nombre.
  • Defina el método de aplicación en su prototipo.
  • Especifique un enlace de evento propio del paquete web táctil .
  • Manipule datos específicos de la instancia dentro del paquete web.
  • Llame a la devolución de llamada proporcionada por webpack después de que se implemente la función.
    // 一个 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();
       }
     );
    }
    }

Tres, implementación del complemento de optimización de CDN

Ideas:

  • 1, crea una JavaScriptfunción nombrada (uso ES6de classrealización);
  • 2, se define en el applymétodo del prototipo ;
  • 3. Especifique un gancho de evento que toque el propio paquete web (el compilationgancho se toca aquí : después de que se crea la compilación, se ejecuta el complemento);
  • 4, en caso de funcionamiento del gancho index.html(a cdnla script标签insertado en el index.htmlmedio);
  • 5. Póngalo en applyantes de ejecutar el método ;cdn的参数webpack外部扩展externals
  • 6. Llamada webpackproporcionada después de que se implementa la función callback;

Pasos de implementación:

1, crea un nombre JavaScript(usando la función ES6de la classrealización)

  Cree una clase cdnPluginInject, agregue el constructor de la clase para recibir los parámetros pasados; aquí definimos el formato de los parámetros recibidos de la siguiente manera:

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

Las variables de la clase de definición modulesreciben los cdn参数resultados de procesamiento pasados :

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

2, se define en el applymétodo del prototipo

El complemento es un constructor (el objeto prototipo tiene este applymétodo de constructor ) instanciado. Este applymétodo de instalación del complemento se llamará una vez que el compilador webpack. applyEl método puede recibir una referencia al objeto del compilador del paquete web, de modo que se pueda acceder al objeto del compilador en la función de devolución de llamada

cdnPluginInject.jsel código se muestra a continuación:

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

module.exports = CdnPluginInject;

3. Especifique un enlace de evento que toque el propio paquete web

  El compilationgancho se toca aquí : después de que se crea la compilación, se ejecuta el complemento.

YYDS: desarrollo del complemento Webpack

  compilationcompiler, una función de enlace. La compilación creará una nueva instancia del proceso de compilación. Una instancia de compilación está bien 访问所有模块和它们的依赖. Después de obtener estos módulos, ¡puede manipularlos según sea necesario!

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 en el evento de ganchoindex.html

  Este paso se debe principalmente a alcanzar a cdnla script标签insertado index.htmlen , la forma de lograrlo? En el proyecto vue, webpack realmente usa html-webpack-plugin para generar .htmlarchivos al empaquetar , por lo que también podemos html-webpack-plugininsertar etiquetas de script cdn operando archivos html aquí .

// 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;

A continuación, analice la realización anterior paso a paso:

  • 4.1 Introducir la dependencia html-webpack-plugin, no hace falta decirlo;
  • 4.2. html-webpack-pluginLa hooksfunción en la llamada se html-webpack-pluginejecuta de forma asincrónica antes de que se genere el recurso; aquí está html-webpack-pluginel autor que se jacta sinceramente de que ha html-webpack-plugincreado muchas funciones de enlace en el complemento durante el desarrollo para que los desarrolladores incrusten diferentes operaciones en diferentes etapas de la llamada al complemento. Por lo tanto, aquí podemos usar html-webpack-pluginun beforeAssetTagGenerationpar de html operar;
  • 4.3 beforeAssetTagGenerationObtenga todos los datos de configuración que debe importar cdn en.
  • 4.4 Integre las referencias js y cdn existentes, data.assets.jspuede obtener compilationtodos js资源los enlaces / rutas generados en el escenario (y finalmente insertados en index.html), y cdn的path数据(cdn的url)fusionar los que necesitan ser configurados ;
  • 4.5 Integre las referencias css y cdn existentes, data.assets.csspuede obtener compilationtodos css资源los enlaces / rutas generados en el escenario (y finalmente insertados en index.html) y cdn的path数据(cdn的url)fusionar los tipos css que deben configurarse ;
  • 4.6 Devuelve la función de devolución de llamada, el propósito es indicar webpackque la operación se ha completado y puedes pasar al siguiente paso;

5, configurando webpackel外部扩展externals

  Antes de applyejecutar el método, hay un paso más que se debe completar: cdn的参数configurarlo 外部扩展externals; puede compiler.options.externalsobtener directamente el atributo externals en el paquete web y configurar los datos en la configuración de cdn después de la operación.

6 、callback

  Devuelva la devolución de llamada para indicar que se completó el CdnPluginInjectcomplemento del paquete web ;

// 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;

  ¡En este punto, CdnPluginInjectse ha desarrollado un complemento de paquete web completo ! Intentémoslo a continuación.

Cuatro, uso de plug-in optimizado cdn

vue.config.jsIntroducir y utilizar   en el archivo del proyecto vue CdnPluginInject:

archivo de configuración 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"
  },
  ···
];

Configurar en 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
        })
      )
    }
  }
  ···
}

Configuración en 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
        })
      )
    }
  }
  ···
}

  Utilizando CdnPluginInject:

  • 1. Realizar la gestión y el mantenimiento de la optimización CDN a través de la configuración;
  • 2. Realizar la optimización de la configuración de CDN para diferentes entornos (el entorno de desarrollo utiliza directamente las dependencias de instalación local para la depuración, y el entorno de producción se adapta al modo CDN para optimizar la carga);

V. Resumen

  Después de leerlo, debe haber webpackalgunas dudas por parte de los grandes, ¡este complemento no es la versión mendiga de webpack-cdn-plugin ! CdnPluginInjectEs solo que webpack-cdn-pluginaprendo del código fuente, combinado con la versión de imitación que mi proyecto realmente necesita modificar. En comparación webpack-cdn-plugincon encapsular la generación del enlace cdn, CdnPluginInjectes configurar directamente el enlace cdn , que es más fácil elegir la configuración cdn. Si desea obtener más información, puede consultar webpack-cdn-pluginel código fuente de xdm . Después de la actualización iterativa continua del autor, proporciona parámetros más configurables y funciones más potentes (nuevamente).

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

Referencia del artículo

Supongo que te gusta

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