皮皮网
皮皮网

【飞鱼转源码】【hk源码】【WindowsCE源码】HtmlWebpackPlugin源码

来源:如何仿源码 发表时间:2024-12-22 21:16:11

1.webpack使用了什么技术?
2.webpack之plugin详解
3.手摸手写个webpack plugin
4.webpack学习日志-9-自动构建使用glob库
5.webpack5loader和plugin原理解析

HtmlWebpackPlugin源码

webpack使用了什么技术?

       webpack使用HtmlWebpackPlugin进行cdn配置。

在上一篇文章中,我们介绍了cdn的实现原理。现在,让我们来认识一下如何在形式化开发中使用cdn功能。要使用cdn功能,飞鱼转源码我们需要cdn服务提供商。我们可以自己搭建,也可以使用一些知名的服务商。幸运的是,市场上有许多免费的cdn服务提供商,例如:

       其中,BootCDN是Bootstrap中文网支持和维护的前端开源项目免费CDN服务,项目资源与cdnjs仓库同步。界面比较美观,支持搜索功能,可以在线测试cdn是否正常,下面以Bootcdn为例。

       在以前的项目开发中,我们经常使用webpack来构建项目,它有两个主要功能,即

       webpack中cdn的使用是在静态资源打包生成的时候完成的。主要原理是使用html-webpack-plugin动态插入cdn链接。

       这里就不介绍webpack的使用了,以vue-cli2.x生成的默认项目为例。

       Html-webpack-plugin是webpack的一个插件,可以动态创建和编辑Html内容。在html中使用esj语法可以读取配置中的参数,简化了html文件的构造。

       这次我们主要用它来动态插入cdn链接,比如链接标签,脚本标签。

       在线项目地址

       vue-cli2.x

       使用webpack类型创建一个名为webpack-cdn-demo的vue项目。如果安装的vue-cli是3.x版,命令会有所不同。请参见vue-cli3了解详细信息。

       安装依赖性

       启动一个项目

       下面简单介绍一下目录结构。

       build文件夹中的webpack.prod.conf.js是我们的主文件,我们在其中动态设置不需要打包的模块,并构建适当的链接。

       在webpack项目中,引入的第三方资源将被统一打包到vender文件中。我们可以通过webpack的externals属性设置包排除这个模块。详情请参见外部扩展。hk源码

       在前面的步骤中,我们创建了包括vue和vue-router的项目。正式开发中,会有element-ui等ui库。为了演示方便,我们安装了element-ui和axios两个模块,并在构建时实现了这个模块以cdn的形式引入。

       安装时注意记住-S,它的作用是安装后在package.json项目文件中插入记录,后续操作需要读取已安装的模块。

       对于cdn,我们可以自己搭建,也可以使用专业的cdn服务商。在这里,我们使用免费的cdnbootcdn。选择免费的cdn有很多好处,但毕竟有一个隐患,就是服务可能会崩溃。

       依次搜索前面的模块,结果如下

       根据规则,cdn资源路径规则如下

       其他cdn服务商也一样。

       其实使用cdn就是在webpack热启动打包的时候动态插入脚本和样式链接。为了维护方便,我们在build/utils.js文件中增加了几个方法,以后可以在webpack.dev.conf.js和webpack.prod.conf.js上使用。

       如果没有build/utils.js,可以添加到其他文件中,只要后续步骤可以操作即可。

       命名模块名,与package.json相同

       范围模块范围命名

       Jsjs地址

       Csscss地址

       这里要特别注意scope,这是webpack配置的外部参数下的信息。比如vue的作用域名称是Vue,vue-router的作用域名称是VueRouter,element-ui的作用域名称是element。同样,jq的作用域名称是JQuery。具体方法是先引入这个资源,然后在控制台中依次输入近似值,一一匹配(目前还没有找到更好的方法)。

       我们可以在webpack热启动时使用cdn进行本地调试。

       在build/webpack.dev.conf.js中,已经默认引入了utils.js,可以直接调用相关方法。WindowsCE源码如果是自定义文件,记得介绍一下。

       我们可以给它添加一些自定义属性,方便在index.html调用。,现修改如下:

       cdnConfig和onlyCss自定义属性可以通过htmlWebpackPlugin.options以html格式读取..

       更多html-webpack-plugin配置。官网,这里暂时不需要更多。

       打包的时候我们用的是cdn,配置和之前的dev差不多,但是需要多做一步。

       注意这里的externalModules,后面会用到,也就是比dev多几个步骤。

       添加与dev相同的两个配置,但只需要将Css改为true,因为我们在打包时要使用比css更多的内容。

       webpack配置已经完成,相关参数已经添加到html-webpack-plugin中,可以直接在页面上使用。语法是ejs,类似于ASP.NET、jsp和php。

       及格

       webpack配置已经修改,项目需要重启才能生效。

       打包项目

       可见包装体积大大减少。

       Cdn资源在页面上也有正确的介绍。

       最后,git地址:ame的方式接入,将域名cname到cdn,再回源到服务器,设置需要加速的文件类型,例如jscsspng等等

webpack之plugin详解

       plugin在webpack中扮演着核心角色,它们是构建工具的扩展点,用于解决loader无法实现的其他功能。要使用plugin,通常先通过npm安装到本地,然后在配置文件(webpack.config.js)的头部引入,紧接着在plugins那一栏使用new关键字生成插件的实例并注入到webpack中。在webpack构建过程中,plugin会在特定的生命周期函数触发时执行定义的功能。这些生命周期函数可以类比为打包流程中的工序,当执行到特定工序时,源码BFHplugin绑定的事件就会被触发。

       例如,clean-webpack-plugin插件会在webpack重新打包前自动清空输出文件夹,而HtmlWebpackPlugin插件则会在打包结束后根据配置的模板路径自动生成一个html文件,并把打包生成的js路径自动引入到这个html文件中,大大提高了开发效率。

       webpack程序架构中包含compiler和compilation两个核心概念。compiler是webpack的支柱引擎,负责控制程序的执行,其生命周期函数用于定义在不同阶段执行的事件。compiler会从左到右执行每一个生命周期函数定义的事件队列。compilation实例主要负责代码的编译和构建,每次代码编译(例如日常开发时按ctrl + s保存修改后的代码)都会生成一个新的compilation实例来执行构建任务。

       在compilation的生命周期中,compiler进入make阶段后,compilation实例被创建,它依次执行一系列定义的钩子函数,包括加载相应的loader对代码进行编译、解析生成AST语法树、进行依赖分析、优化和封装等步骤。这些步骤构成了webpack完整的打包构建流程。

       Tapable是一个用于事件发布订阅的第三方库,webpack中使用Tapable实现了事件的绑定和触发机制。通过使用Tapable提供的SyncHook和AsyncSeriesHook,可以定义同步和异步事件,这些事件会在特定的生命周期阶段被触发。

       为了开发自定义插件,可以创建一个js文件并实现一个class,其中包含一个固定方法apply。apply函数的第一个参数是compiler,可以在apply函数内部编写插件逻辑。为了在特定生命周期阶段绑定事件,可以使用Tapable提供的hook,例如在emit阶段绑定事件。通过这样的方式,插件可以在webpack执行到特定时刻时执行特定功能。

       使用自定义插件时,首先在webpack配置文件中引入插件,然后在plugins数组中使用new关键字注入插件实例。这样,当webpack执行到相应的事件节点时,插件定义的wangeditor源码监听函数就会被触发。

手摸手写个webpack plugin

       本周的任务是深入了解和编写webpack插件。插件是什么?它实际上是一个具有 apply 方法的JavaScript对象,更准确地说,就是一种函数。它可以是普通的函数,也可以是特殊的函数,用于实现特定功能。

       相比loader,插件可以解决一些loader无法处理的场景,比如在打包开始前进行环境配置、打包结束后发送通知邮件等。例如,HtmlWebpackPlugin插件可以帮助我们在dist文件夹下自动生成index.html文件,并在其中自动引入打包后的css和js脚本文件。安装并配置插件后,运行打包指令,可以看到生成的index.html文件自动引入了生成的main.js脚本文件。

       接下来,我们深入探讨HtmlWebpackPlugin的源码,了解如何编写插件。通过观察源码,我们可以发现插件是通过apply方法在特定生命周期内执行操作。以HtmlWebpackPlugin为例,其在初始化阶段会利用Compiler提供的hook函数,实现生成html文件并引入资源的功能。

       为了更深入理解插件的工作原理,我们可以执行yarn run webpack命令,跟踪webpack的运行过程,了解不同hook函数的调用时机和作用。例如,afterDone钩子函数会在打包完成后触发,用于执行发送通知邮件等操作。通过观察Compiler类的实现,我们可以理解如何在插件中访问和利用Compiler实例对象,以及如何在特定钩子函数的回调中实现自定义逻辑。

       接下来,我们以一句话需求为例,实现一个在webpack打包后发送通知邮件的插件。首先,我们需要安装nodemailer库以实现邮件发送功能。然后,根据需求编写插件代码,利用Compiler提供的hook函数,如afterDone,实现邮件发送逻辑。运行webpack后,我们可以看到通知邮件被成功发送。为了方便他人使用,我们还可以将自定义插件发布到npm仓库。

       在插件开发中,还有一些重要的概念,如SyncHook和AsyncHook,它们分别用于同步和异步操作。另外,了解如何在插件中正确使用这些概念,对于提高插件的可复用性和功能性至关重要。官方文档提供了关于编写插件的详细指导,建议开发者深入学习。

       总之,通过本篇文章的学习,我们不仅掌握了webpack插件的基本概念和使用方法,还深入探讨了其原理和实战应用。希望这些知识能帮助开发者在实际项目中灵活运用插件,提升开发效率。

webpack学习日志-9-自动构建使用glob库

       每天一小步,书接上文

       我们继续深入学习Webpack,实现自动构建的优化。上篇中,我们构建了一个多页面通用打包方案,并通过htmlWebpackPlugin进行简单的HTML页面生成。然而,发现方案并非完全通用智能,需额外配置,比如多入口和每个HTML对应单独插件的设定。

       为解决此问题,我们将引入Node的glob库。该库可实现基于规则的文件匹配,并返回路径,极大地简化配置工作。通过glob库的使用,我们能自动生成entry和htmlWebpackPlugin,简化配置流程。

       实现步骤如下:新建文件`entryAndHTML.js`,使用glob.sync(srcPath)读取src目录下所有index.js文件的路径。通过正则匹配文件名,生成对应entry对象和htmlWebpackPlugin插件数组。

       修改webpack.config.js,注意将生成数组解构,实现自动配置。尽管存在一些小问题,如文件夹变动后需手动重新构建,但通过引入nodemon等工具可进一步优化。

       随意创建文件并保持规范,构建结果展示。此优化显著减少了手动配置工作,节省开发时间。同时,引入clean-webpack-plugin清理dist目录,确保每次构建前目录清空,避免文件覆盖问题。

       关于是否学习Node,答案是肯定的。Node不仅提升前端开发效率,还能实现更高级功能,如SPA中的SSR技术。学习Node,将使你与同级开发者拉开距离,增强技术竞争力。

       今天,我们成功封装了全局自动构建逻辑,同时规范了项目文件结构。安装html-webpack-plugin清理dist目录,提升开发流程效率。下一步,我们将完善开发环境,处理CSS集成、devServer配置、跨域问题、文件指纹等关键要素。

webpack5loader和plugin原理解析

       大家好,今天为大家解析下loader和plugin

一、区别

       loader是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中

       plugin赋予了Webpack各种灵活的功能,例如打包优化、资源管理、环境变量注入等,目的是解决loader无法实现的其他事从整个运行时机上来看,如下图所示:

       可以看到,两者在运行时机上的区别:

       loader运行在打包文件之前plugins在整个编译周期都起作用在Webpack运行的生命周期中会广播出许多事件,Plugin可以监听这些事件,在合适的时机通过Webpack提供的API改变输出结果

       对于loader,实质是一个转换器,将A文件进行编译形成B文件,操作的是文件,比如将A.scss或A.less转变为B.css,单纯的文件转换过程

       下面我们来看看loader和plugin实现的原理

Loader原理loader概念

       帮助webpack将不同类型的文件转换为webpack可识别的模块。

loader执行顺序

       分类

       pre:前置loader

       normal:普通loader

       inline:内联loader

       post:后置loader

       执行顺序

       4类loader的执行优级为:pre>normal>inline>post。

       相同优先级的loader执行顺序为:从右到左,从下到上。

       例如:

//此时loader执行顺序:loader3-loader2-loader1module:{ rules:[{ test:/\.js$/,loader:"loader1",},{ test:/\.js$/,loader:"loader2",},{ test:/\.js$/,loader:"loader3",},],},//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},

       使用loader的方式

       配置方式:在webpack.config.js文件中指定loader。(pre、normal、postloader)

       内联方式:在每个import语句中显式指定loader。(inlineloader)

开发一个loader1.最简单的loader//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};

       它接受要处理的源码作为参数,输出转换后的js代码。

2.loader接受的参数

       content源文件的内容

       mapSourceMap数据

       meta数据,可以是任何内容

loader分类1.同步loadermodule.exports=function(content,map,meta){ returncontent;};

       this.callback方法则更灵活,因为它允许传递多个参数,而不仅仅是content。

module.exports=function(content,map,meta){ //传递map,让source-map不中断//传递meta,让下一个loader接收到其他参数this.callback(null,content,map,meta);return;//当调用callback()函数时,总是返回undefined};2.异步loadermodule.exports=function(content,map,meta){ constcallback=this.async();//进行异步操作setTimeout(()=>{ callback(null,result,map,meta);},);};

       由于同步计算过于耗时,在Node.js这样的单线程环境下进行此操作并不是好的方案,我们建议尽可能地使你的loader异步化。但如果计算量很小,同步loader也是可以的。

3.RawLoader

       默认情况下,资源文件会被转化为UTF-8字符串,然后传给loader。通过设置raw为true,loader可以接收原始的Buffer。

module.exports=function(content){ //content是一个Buffer数据returncontent;};module.exports.raw=true;//开启RawLoader4.PitchingLoadermodule.exports=function(content){ returncontent;};module.exports.pitch=function(remainingRequest,precedingRequest,data){ console.log("dosomethings");};

       webpack会先从左到右执行loader链中的每个loader上的pitch方法(如果有),然后再从右到左执行loader链中的每个loader上的普通loader方法。

       在这个过程中如果任何pitch有返回值,则loader链被阻断。webpack会跳过后面所有的的pitch和loader,直接进入上一个loader。

loaderAPI方法名含义用法this.async异步回调loader。返回this.callbackconstcallback=this.async()this.callback可以同步或者异步调用的并返回多个结果的函数this.callback(err,content,sourceMap?,meta?)this.getOptions(schema)获取loader的optionsthis.getOptions(schema)this.emitFile产生一个文件this.emitFile(name,content,sourceMap)this.utils.contextify返回一个相对路径this.utils.contextify(context,request)this.utils.absolutify返回一个绝对路径this.utils.absolutify(context,request)

       更多文档,请查阅webpack官方loaderapi文档

手写clean-log-loader

       作用:用来清理js代码中的console.log

//loaders/clean-log-loader.jsmodule.exports=functioncleanLogLoader(content){ //将console.log替换为空returncontent.replace(/console\.log\(.*\);?/g,"");};手写banner-loader

       作用:给js代码添加文本注释

       loaders/banner-loader/index.js

constschema=require("./schema.json");module.exports=function(content){ //获取loader的options,同时对options内容进行校验//schema是options的校验规则(符合JSONschema规则)constoptions=this.getOptions(schema);constprefix=`/**Author:${ options.author}*/`;return`${ prefix}\n${ content}`;};

       loaders/banner-loader/schema.json

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},0手写babel-loader

       作用:编译js代码,将ES6+语法编译成ES5-语法。

       下载依赖

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},1

       loaders/babel-loader/index.js

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},2

       loaders/banner-loader/schema.json

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},3手写file-loader

       作用:将文件原封不动输出出去

       下载包

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},4

       loaders/file-loader.js

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},5

       loader配置

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},6手写style-loader

       作用:动态创建style标签,插入js中的样式代码,使样式生效。

       loaders/style-loader.js

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},7Plugin原理Plugin的作用

       通过插件我们可以扩展webpack,加入自定义的构建行为,使webpack可以执行更广泛的任务,拥有更强的构建能力。

Plugin工作原理

       webpack就像一条生产线,要经过一系列处理流程后才能将源文件转换成输出结果。这条生产线上的每个处理流程的职责都是单一的,多个流程之间有存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。插件就像是一个插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。webpack通过Tapable来组织这条复杂的生产线。webpack在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条生产线中,去改变生产线的运作。webpack的事件流机制保证了插件的有序性,使得整个系统扩展性很好。——「深入浅出Webpack」

       站在代码逻辑的角度就是:webpack在编译代码过程中,会触发一系列Tapable钩子事件,插件所做的,就是找到相应的钩子,往上面挂上自己的任务,也就是注册事件,这样,当webpack构建的时候,插件注册的事件就会随着钩子的触发而执行了。

Webpack内部的钩子什么是钩子

       钩子的本质就是:事件。为了方便我们直接介入和控制编译过程,webpack把编译过程中触发的各类关键事件封装成事件接口暴露了出来。这些接口被很形象地称做:hooks(钩子)。开发插件,离不开这些钩子。

Tapable

       Tapable为webpack提供了统一的插件接口(钩子)类型定义,它是webpack的核心功能库。webpack中目前有十种hooks,在Tapable源码中可以看到,他们是:

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},8

       Tapable还统一暴露了三个方法给插件,用于注入不同类型的自定义构建行为:

       tap:可以注册同步钩子和异步钩子。

       tapAsync:回调方式注册异步钩子。

       tapPromise:Promise方式注册异步钩子。

Plugin构建对象Compiler

       compiler对象中保存着完整的Webpack环境配置,每次启动webpack构建时它都是一个独一无二,仅仅会创建一次的对象。

       这个对象会在首次启动Webpack时创建,我们可以通过compiler对象上访问到Webapck的主环境配置,比如loader、plugin等等配置信息。

       它有以下主要属性:

       compiler.options可以访问本次启动webpack时候所有的配置文件,包括但不限于loaders、entry、output、plugin等等完整配置信息。

       compiler.inputFileSystem和compiler.outputFileSystem可以进行文件操作,相当于Nodejs中fs。

       compiler.hooks可以注册tapable的不同种类Hook,从而可以在compiler生命周期中植入不同的逻辑。

       compilerhooks文档

Compilation

       compilation对象代表一次资源的构建,compilation实例能够访问所有的模块和它们的依赖。

       一个compilation对象会对构建依赖图中所有模块,进行编译。在编译阶段,模块会被加载(load)、封存(seal)、优化(optimize)、分块(chunk)、哈希(hash)和重新创建(restore)。

       它有以下主要属性:

       compilation.modules可以访问所有模块,打包的每一个文件都是一个模块。

       compilation.chunkschunk即是多个modules组成而来的一个代码块。入口文件引入的资源组成一个chunk,通过代码分割的模块又是另外的chunk。

       compilation.assets可以访问本次打包生成所有文件的结果。

       compilation.hooks可以注册tapable的不同种类Hook,用于在compilation编译模块阶段进行逻辑添加以及修改。

       compilationhooks文档

生命周期简图开发一个插件最简单的插件

       plugins/test-plugin.js

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},9注册hook//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};0启动调试

       通过调试查看compiler和compilation对象数据情况。

       package.json配置指令

//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};1

       运行指令

//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};2

       此时控制台输出以下内容:

PSC:\Users\\Desktop\source>//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};2>source@1.0.0debug>node--inspect-brk./node_modules/webpack-cli/bin/cli.jsDebuggerlisteningonws://.0.0.1:/ea-7b--a7-fccForhelp,see:/post/

       开发思路:

       我们需要借助html-webpack-plugin来实现

       在html-webpack-plugin输出index.html前将内联runtime注入进去

       删除多余的runtime文件

       如何操作html-webpack-plugin?官方文档

       实现:

//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};7

相关栏目:综合