当前位置: 首页 > Web前端 > HTML

如何让自己的组件库支持

时间:2023-03-29 12:45:18 HTML

按需引用Tree-Shaing的引入,就是优化打包体积,剔除无用模块的引用。作为性能优化的重要手段之一,每个合格的组件也应该是Libraries应该包办的。下面以package-demo组件库为例,一步步搭建内部组件库。参考方法可以先判断用户要使用(方法一):import{Button}from'package-demo';而不是需要知道组件的具体位置,手动引入它(方法二):importButtonfrom'package-demo/lib/button.vue';import'package-demo/lib/style/button.css'那里从方法2到方法1有两种实现方式:使用babel插件。比如之前antd提供的babel-plugin-import,该插件通过引入固定路径和组件样式的组件来代替手动摇晃过程。这样也可以确定打包后的文件路径(组件需要lib/xx,样式文件需要lib/xx/style/xx)和文件模块CommonJsimport{Button}from'antd';ReactDOM.render();↓↓↓↓↓var_button=require('antd/lib/button');require('antd/lib/button/style/css');ReactDOM.render(<_button>xxxx);通过webpack启用ES模块的Tree-shaking(当mode=production时默认启用此功能)。webpack将所有导入标记为/harmonyimport/,使用过的导出标记为/harmonyexport/,未使用的导出标记为/unusedharmonyexport/,并配合Uglifyjs等工具删除输出格式example中无用的代码也使用了tree-shaking基于ES模块实现按需引用,那么为什么ESM可以进行tree-shaking,什么是tree-shaking?先理解一下概念:ES模块ES6模块通过import关键字导入模块,通过export关键字导出模块,在编译时输出静态定义的接口。import命令会被js引擎静态分析,会比模块中的其他内容执行得更好,会加载一个输出值,而不是加载整个模块。这种加载称为“编译时加载”。从“模块名称”导入{member1,member2};//导入指定名称的多个成员exportfunctionadd(){};与CommonJs不同的是,整个对象是通过require关键字(module.exports属性)加载的,从对象中读取方法,这种加载称为“运行时加载”。该对象是在脚本加载后生成的,因此无法对其进行静态分析。exports.fs=fs;module.exports=fs;module.exports:module是一个变量,指向一块内存,exports是module中的一个属性,存放在内存中,然后exports属性指向{}模块。你可以使用任何一个。语法上,也可以用=直接赋值。所谓静态分析,不是去执行代码,而是从字面上去分析代码。我们使用require()语法的CommonJS模块规范,它允许代码根据条件动态加载不同的模块。只有在代码运行的时候,我们才能知道引用了哪些模块。.ESM的模块介绍只能作为模块的顶层语句出现在条件之外,后面的写法会导致语法错误。//CommonJsvardynamicModule;if(condition){dynamicModule=require("moduleA");}else{dynamicModule=require("moduleB");}//ESM错误语法if(condition){dynamicModule=import("moduleA");}else{dynamicModule=import("moduleB");}所以总结就是CommonJs导入了整个对象,可以有条件地加载;ESM模块加载时导入一定的输出值,只能静态加载。ESM模块依赖是确定性的,与运行时状态无关,可以进行可靠的静态分析,这也是tree-shaking的基础。tree-shakingTree-Shaking依赖于ES6导入和导出静态分析功能。当一个模块被静态引用时,它会尽量删除只写但不删除只读的变量和函数。未使用的函数和变量定义在两个模块文件moduleA和moduleB//moduleA.jsexportfunctiona1(){console.log('a1')};exportfunctiona2(){console.log('a2')}//moduleB.jsimport{a1}from'moduleA';exportfunctionb1(){a1();};exportfunctionb2(){console.log('b2')}callsmoduleB.b2importinindex.js{b2}from'moudleB';b2();编译后可以看到代码只包含了b2函数,没有用到的b1和moduleA经过tree-shaking后被清空,即使引入了moduleA。'使用严格';函数b2(){console.log('b2');}b2();有副作用当函数修改其自身函数范围之外的资源时,该函数会产生副作用。例如在moduleA中对数组原型链进行修改//moduleAArray.prototype.find=null;编译后可以看到代码中已经引入了模块A。这是因为模块A有副作用,打包工具保留了,如果你只在模块B中引入moduleA.a1而不调用它,你会看到虽然模块A被淘汰了,但是副作用代码Array.prototype.find=null;也会被保留。'usestrict';require('moduleA');functionb2(){console.log('b2');}b2();未使用的类定义函数Util类,在类中定义isPhone方法和isEmail方法//utils.jsexportdefaultclassUtil{isPhone(){return'phone'}isEmail(){return'email'}}importUtilclassinmoduleBimportUtilfrom'./util.js'exportfunctionb2(){letutil=newUtil()letresult1=util.hello()console.log(result1)};同样在index.js中调用b2(),但是发现打包后的代码中有isEmail函数,完成了Util类的打包,并没有按预期删除不用的isEmail函数'usestrict';classUtil{isPhone(){return'phone'}isEmail(){return'email'}}functionb2(){letutil=newUtil();让result1=util.hello();console.log(result1);}b2();由于JS的动态语言特性,如果删除了isEmail方法,当变量(Util['isEmail'])访问Util类上的方法时,会运行出错,所以保守点,webpack和rollup都可以打包时会保留类和对象的内部。webpack打包后,会有很多模块定义的代码。为了方便查看,示例是tree-shaking后rollup的打包结果。tree-shaking虽然不能完全剔除不用的代码,但是可以配合webpack的相关配置(工程管理下的项目一般都搭配webpack),也可以达到我们想要的按需加载的效果。打包工具综上所述,我们需要一个可以输出es模块的打包工具。rollup是一个不错的选择。webpack打包的文件会添加一些自定义的模块加载代码__webpack_require__等工具函数,这些都是libary库所没有的。需要。Rollup不支持动态加载和代码拆分,所以webpack更适合一般的application应用。包配置模块当用户安装组件库时,npminstall命令会根据项目根目录下的package.json配置文件自动下载需要的模块。package.json还定义了项目的版本、名称和脚本。具体可以参考javascript标准参考教程。主字段指定加载的条目文件。早期基于CommonJs规范的npm包以此为切入点,所以在ESM出现后,Rollup使用了另外一个field模块,逐渐被webpack支持。当配置文件中有module字段时,会优先使用。如果没有找到相应的文件,将使用主字段。当然,两者也可以指向同一个入口。"main":"lib/index.js","module":"es/index.js"sideEffects当导入一个package-demo组件库不是像lodash那样纯实用函数的时候,你会发现由于存在ofsideeffects,启用tree-shaking的bundle仍然导入了大部分模块,这时候就需要sideEffects了。当导入的package-demo被标记为sideEffects:false时,webpack就知道这个npm包中的文件没有副作用,只要不引用该组件,整个模块/包就会被彻底移除。(webpackversion>=4可以启用)只要你的package不是用来做polyfill或者shim的,比如修改window上的属性,重写原来的对象方法等,都可以设置sideEffects:false,是否是不是有副作用其实并不重要,对于webpack来说是可以接受的。调试本地组件库在组件库上线之前,可以在package-demo中运行npmlink,组件库会根据package.json(包名package-demo)填写的信息链接到全局。在另一个项目(项目A)中执行npmlinkpackage-demo,可以在其node_modules中找到组件库的快捷方式,映射了组件库。组件库修改时,在项目A中也可以同步看到修改。package.json中的模块,然后用户需要使用webpack。欢迎关注公众号,分享前端知识~