最近,陆陆续续搞了一个UniUsingComponentsWebpackPlugin插件(下面介绍),这是自己第三个开源项目,希望大家一起来维护,一起star呀,其它两个:vueokrtree基于Vue2的组织架构树组件地址:https:github。comqq449245884vueokrtreeztjycli团队的一个简易模板初始化脚手架地址:https:github。comqq449245884ztjycliUniUsingComponentsWebpackPlugin地址:https:github。comqq449245884UniUsingComponentsWebpackPlugin配合UniApp,用于集成小程序原生组件配置第三方库后可以自动引入其下的原生组件,而无需手动配置生产构建时可以自动剔除没有使用到的原生组件背景第一个痛点 用uniapp开发小程序的小伙伴应该知道,我们在uniapp中要使用第三方UI库(vantweapp,iViewweapp)的时候,想要在全局中使用,需要在srcpages。json中的usingComponents添加对应的组件声明,如:srcpages。jsonusingComponents:{vanbutton:wxcomponentsvantweappbuttonindex,} 但在开发过程中,我们不太清楚需要哪些组件,所以我们可能会全部声明一遍(PS:这在做公共库的时候更常见),所以我们得一个个的写,做为程序员,我们绝不允许使用这种笨方法。这是第一个痛点。第二个痛点 使用第三方组件,除了在srcpages。json还需要在对应的生产目录下建立wxcomponents,并将第三方的库拷贝至该文件下,这个是uniapp自定义的,详细就见:https:uniapp。dcloud。ioframe?ide79baee5bd95e7bb93e69e84。 这是第二个痛点。第三个痛点 第二痛点,我们将整个UI库拷贝至wxcomponents,但最终发布的时候,我们不太可能全都用到了里面的全局组件,所以就将不必要的组件也发布上去,增加代码的体积。 有的小伙伴就会想到,那你将第三方的库拷贝至wxcomponents时候,可以只拷使用到的就行啦。是这理没错,但组件里面可能还会使用到其它组件,我们还得一个个去看,然后一个个引入,这又回到了第一个痛点了。 有了这三个痛点,必须得有个插件来做这些傻事,处理这三个痛点。于是就有UniUsingComponentsWebpackPlugin插件,这个webpack插件主要解决下面几个问题:配置第三方库后可以自动引入其下的原生组件,而无需手动配置生产构建时可以自动剔除没有使用到的原生组件webpack插件 webpack的插件体系是一种基于Tapable实现的强耦合架构,它在特定时机触发钩子时会附带上足够的上下文信息,插件定义的钩子回调中,能也只能与这些上下文背后的数据结构、接口交互产生sideeffect,进而影响到编译状态和后续流程。 从形态上看,插件通常是一个带有apply函数的类:classSomePlugin{apply(compiler){}} Webpack会在启动后按照注册的顺序逐次调用插件对象的apply函数,同时传入编译器对象compiler,插件开发者可以以此为起点触达到webpack内部定义的任意钩子,例如:classSomePlugin{apply(compiler){compiler。hooks。thisCompilation。tap(SomePlugin,(compilation){})}} 注意观察核心语句compiler。hooks。thisCompilation。tap,其中thisCompilation为tapable仓库提供的钩子对象;tap为订阅函数,用于注册回调。 Webpack的插件体系基于tapable提供的各类钩子展开,所以有必要先熟悉一下tapable提供的钩子类型及各自的特点。 到这里,就不做继续介绍了,关于插件的更多详情可以去官网了解。 这里推荐Tecvan大佬写的《Webpack插件架构深度讲解》实现思路 UniUsingComponentsWebpackPlugin插件主要用到了三个compiler钩子。 第一个钩子是environment:compiler。hooks。environment。tap(UniUsingComponentsWebpackPlugin,async(){todosomeing}); 这个钩子主要用来自动引入其下的原生组件,这样就无需手动配置。解决第一个痛点。 第二个钩子thisCompilation,这个钩子可以获得compilation,能对最终打包的产物进行操作:compiler。hooks。thisCompilation。tap(UniUsingComponentsWebpackPlugin,(compilation){添加资源hookscompilation。hooks。additionalAssets。tapAsync(UniUsingComponentsWebpackPlugin,async(cb){awaitthis。copyUsingComponents(compiler,compilation);cb();});}); 所以这个勾子用来将nodemodules下的第三库拷贝到我们生产dist目录里面的wxcomponents,解决第二个痛点。 ps:这里也可直接用现有的copywebpackplugin插件来实现。 第三个钩子done,表示compilation执行完成:if(process。env。NODEENVproduction){compiler。hooks。done。tapAsync(UniUsingComponentsWebpackPlugin,(stats,callback){this。deleteNoUseComponents();callback();});} 执行完成后,表示我们已经生成dist目录了,可以读取文件内容,分析,获取哪些组件被使用了,然后删除没有使用到组件对应的文件。这样就可以解决我们第三个痛点了。 PS:这里我判断只有在生产环境下才会剔除,开发环境没有,也没太必要。使用 安装npminstalluniusingcomponentswebpackpluginsavedev 然后将插件添加到WebPackConfig中。例如:constUniUsingComponentsWebpackPluginrequire(uniusingcomponentswebpackplugin);module。exports{plugins:〔newUniUsingComponentsWebpackPlugin({patterns:〔{prefix:van,module:vantweapp,},{prefix:i,module:iviewweapp,},〕,})〕,}; 注意:uniusingcomponentswebpackplugin只适用在UniApp开发的小程序。参数 NameTypeDescriptionpatterns{Array}为插件指定相关Patterns moduleprefix模块名组件前缀 module是指package。json里面的name,如使用是Vant对应的module为vantweapp,如果使用是iview,刚对应的module为iviewweapp,具体可看它们各自的package。json。 prefix是指组件的前缀,如Vant使用是van开头的前缀,iview使用是i开头的前缀,具体可看它们各自的官方文档。 PS:这里得吐曹一下vant,叫别人使用van的前缀,然后自己组件里面声明子组件时,却没有使用van前缀,如picker组件,它里面的JSON文件是这么写的:{component:true,usingComponents:{pickercolumn:。。pickercolumnindex,loading:。。loadingindex}} pickercolumn和loading都没有带van前缀,因为这个问题,在做自动剔除功能中,我是根据前缀来判断使用哪些组件的,由于这里的loading,pickercolumn没有加前缀,所以就被会删除,导致最终的picker用不了。为了解决这个问题,增加了不少工作量。 希望Vant官方后面的版本能优化一下。总结 本文通用自定义Webpack插件来实现日常一些技术优化需求。主要为大家介绍了Webpack插件的基本组成和简单架构,通过三个痛点,引出了uniusingcomponentswebpackplugin插件,并介绍了使用方式,实现思路。 最后,关于Webpack插件开发,还有更多知识可以学习,建议多看看官方文档《WritingaPlugin》进行学习。