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!CdnPluginInject
webpack
plugin
html-webpack-plugin
vue/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) => { 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
JavaScript
función nombrada (usoES6
declass
realización); - 2, se define en el
apply
método del prototipo ; - 3. Especifique un gancho de evento que toque el propio paquete web (el
compilation
gancho 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
(acdn
lascript标签
insertado en elindex.html
medio); - 5. Póngalo en
apply
antes de ejecutar el método ;cdn的参数
webpack
外部扩展externals
- 6. Llamada
webpack
proporcionada después de que se implementa la funcióncallback
;
Pasos de implementación:
1, crea un nombre JavaScript
(usando la función ES6
de la class
realizació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 modules
reciben 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 apply
método del prototipo
El complemento es un constructor (el objeto prototipo tiene este
apply
método de constructor ) instanciado. Esteapply
método de instalación del complemento se llamará una vez que el compilador webpack.apply
El 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.js
el 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 compilation
gancho se toca aquí : después de que se crea la compilación, se ejecuta el complemento.
compilation
Sí compiler
, 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 cdn
la script标签
insertado index.html
en , la forma de lograrlo? En el proyecto vue, webpack realmente usa html-webpack-plugin para generar .html
archivos al empaquetar , por lo que también podemos html-webpack-plugin
insertar 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-plugin
Lahooks
función en la llamada sehtml-webpack-plugin
ejecuta de forma asincrónica antes de que se genere el recurso; aquí estáhtml-webpack-plugin
el autor que se jacta sinceramente de que hahtml-webpack-plugin
creado 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 usarhtml-webpack-plugin
unbeforeAssetTagGeneration
par de html operar; - 4.3
beforeAssetTagGeneration
Obtenga todos los datos de configuración que debe importar cdn en. - 4.4 Integre las referencias js y cdn existentes,
data.assets.js
puede obtenercompilation
todosjs资源
los enlaces / rutas generados en el escenario (y finalmente insertados en index.html), ycdn的path数据(cdn的url)
fusionar los que necesitan ser configurados ; - 4.5 Integre las referencias css y cdn existentes,
data.assets.css
puede obtenercompilation
todoscss资源
los enlaces / rutas generados en el escenario (y finalmente insertados en index.html) ycdn的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
webpack
que la operación se ha completado y puedes pasar al siguiente paso;
5, configurando webpack
el外部扩展externals
Antes de apply
ejecutar el método, hay un paso más que se debe completar: cdn的参数
configurarlo 外部扩展externals
; puede compiler.options.externals
obtener 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 CdnPluginInject
complemento 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, CdnPluginInject
se ha desarrollado un complemento de paquete web completo ! Intentémoslo a continuación.
Cuatro, uso de plug-in optimizado cdn
vue.config.js
Introducir 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 webpack
algunas dudas por parte de los grandes, ¡este complemento no es la versión mendiga de webpack-cdn-plugin ! CdnPluginInject
Es solo que webpack-cdn-plugin
aprendo del código fuente, combinado con la versión de imitación que mi proyecto realmente necesita modificar. En comparación webpack-cdn-plugin
con encapsular la generación del enlace cdn, CdnPluginInject
es 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-plugin
el 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记得 一键三连 哟!