Como um pequeno desenvolvedor que não está no front end há muito tempo, preciso webpack
bater 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!CdnPluginInject
webpack
plugin
html-webpack-plugin
vue/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) => { 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
JavaScript
função nomeada (usoES6
declass
realização); - 2, é definido no
apply
método de protótipo ; - 3. Especifique um gancho de evento que toque o próprio webpack (o
compilation
gancho é tocado aqui : após a compilação ser criada, o plugin é executado); - 4, em caso de funcionamento do gancho
index.html
(paracdn
oscript标签
inserido noindex.html
centro); - 5. Coloque-o em
apply
antes que o método é executado ;cdn的参数
webpack
外部扩展externals
- 6. Chamada
webpack
fornecida após a função ser implementadacallback
;
Etapas de implementação:
1, crie um nome JavaScript
(usando a função ES6
de class
realizaçã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 modules
recebem 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 apply
método de protótipo
O plug-in é um construtor (o objeto de protótipo tem esse
apply
método de construtor ) instanciado. Esteapply
método de instalação do plug, será chamado uma vez compilador webpack.apply
O 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.js
có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 compilation
gancho é tocado aqui : após a compilação ser criada, o plugin é executado.
compilation
Sim 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 cdn
o script标签
inserida index.html
no ; como consegui-lo? No projeto vue, webpack realmente usa html-webpack-plugin para gerar .html
arquivos ao empacotar , portanto, também podemos html-webpack-plugin
inserir 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-plugin
Ahooks
função na chamada éhtml-webpack-plugin
executada de forma assíncrona antes de o recurso ser gerado; aqui estáhtml-webpack-plugin
o autor se gabando sinceramente , tahtml-webpack-plugin
construiu 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 usarhtml-webpack-plugin
umbeforeAssetTagGeneration
par de operação html; - 4.3
beforeAssetTagGeneration
Obtenha 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.js
você pode obtercompilation
todosjs资源
os links / caminhos gerados no estágio (e finalmente inseridos em index.html), ecdn的path数据(cdn的url)
mesclar aqueles que precisam ser configurados ; - 4.5. Integre as referências css e referências cdn existentes;
data.assets.css
você pode obtercompilation
todoscss资源
os links / caminhos gerados no estágio (e finalmente inseridos em index.html), ecdn的path数据(cdn的url)
mesclar os tipos css que precisam ser configurados ; - 4.6. Retorne a função callback, o objetivo é dizer
webpack
que a operação foi concluída e você pode prosseguir para a próxima etapa;
5, definindo webpack
o外部扩展externals
Antes de o apply
método ser executado, há mais uma etapa que deve ser concluída: cdn的参数
configure 外部扩展externals
-o, você pode compiler.options.externals
obter 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 CdnPluginInject
plug-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 CdnPluginInject
foi desenvolvido! Vamos tentar a seguir.
Quatro, uso de plug-in otimizado para cdn
vue.config.js
Apresente 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 webpack
algumas dúvidas dos marmanjos , este plugin não é a versão mendiga do webpack-cdn-plugin ! CdnPluginInject
É que eu webpack-cdn-plugin
aprendo com o código-fonte, combinado com a versão de imitação que meu projeto realmente precisa modificar.Comparado webpack-cdn-plugin
com 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-plugin
o 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记得 一键三连 哟!