在日常的开发过程中,搭建组件库是必不可少的环节。本文介绍如何构建一个完整的组件库,以解决组件库开发和发布过程中出现的问题。下面的问题:如何快速开发一个依赖最少的Vue组件。如何将所有包放在一个git存储库中。如何一键发布git仓库中的所有包。如何管理所有包的依赖关系并减少包的大小。如何快速创建组件示例。如何打包组件,webpack?快速原型开发组件不同于开发项目。在开发组件的时候,我们希望有一个工具可以快速搭建某个vue文件的开发环境,并在发布的时候打包编译。这时候我们可以使用@vue/cli-service-global。全局安装这个包必须全局安装:npminstall-g@vue/cli-service-global创建一个vue文件在根目录下创建一个App.vue文件:Hello!
/template>start开发服务器在命令行执行:vueserve入口可以是main.js、index.js、App.vue或app.vue之一。也可以显式指定入口文件:vueserveApp.vue执行打包。vuebuild打包完成后,打包结果会放在dist目录下。默认会打包生成一个应用,里面包含html和资源文件。可以作为静态站点直接部署。但通常情况下,我们需要将组件打包成库,发布后才能被项目使用。打包成库需要指定构建目标:vuebuild--targetlib添加构建目标后,执行打包。dist目录包含各种标准化的js文件和演示示例html。至此,vue组件的快速开发就完成了,我们可以愉快的开发各种组件了。但是,当开发的组件逐渐增多时,文件的组织就成为我们需要考虑的问题。你可以想到以下三种组织文件结构的方式:每个组件都是一个单独的仓库。一个仓库包含多个组件vue文件,打包发布。一个仓库包含多个组件包,每个组件包单独发布。在第一种方式中,每个组件都是一个单独的仓库。虽然有利于组件开发,但是维护组件比较麻烦。组件越多,需要维护的仓库就越多。当某些组件依赖lodash需要升级时,我们需要一个一个升级,比较麻烦。第二种方式是将所有组件作为一个包发布。虽然维护起来更方便,但是在发布之后,当别人只想使用其中一个组件时,就需要导入整个组件库。如果不提供按需加载,那么就会导致在项目中引入很多不必要的代码。第三种方法可以参考下文。Monorepo我们查看vue3源码可以看到其存储结构如下:packages├──compiler-core├──_tests_#unittest├──src#sourcefiledirectory├──package.json├──compiler-dom├──_tests_#unittest├──src#源文件目录├──package.jsonpackage.json这是一个典型的monorepo,monorepo是项目代码的一种管理方式,指的是管理多个模块/包裹在仓库里。monorepo追求的是在一个仓库中管理多个模块。每个模块都有一个独立的package.json来管理其依赖关系。同时可以在项目根目录下通过命令安装或升级模块依赖,并提供一个模块共享的node_modules。yarnworkspaceyarnworkspace是一种实现monorepo的方法。使用yarnworkspace需要在根目录的package.json中添加如下属性:{"private":true,"workspaces":["packages/*"]}private属性指定根目录是私有的,不会通过发布工具发布到npm。workspace属性指定组件所在的文件夹,支持通配符。修改package.json后,根据vue-next项目结构在packages文件夹下创建一个输入测试组件。假设自定义输入组件依赖dayjs包,可以在根目录下执行以下命令安装:yarnworkspacem-inputadddayjs其中m-input不是packages下的组件文件夹名称,而是组件文件夹下的包。json中的name属性值。安装完成后,dayjs会自动添加到input组件的package.json中,只是下载包到根目录下的node_modules文件夹下,这样可以更好的管理多组件包的依赖关系。如果当前组件依赖的包版本与其他组件依赖的包版本不同,例如其他组件依赖lodash@4,当前组件依赖lodash@3,则依赖包会被下载到当前组件文件夹下的node_modules。某个组件下的npm脚本可以通过yarn工作空间来执行。比如给input组件添加build命令,可以在根目录下通过如下命令执行build:yarnworkspacem-inputrunbuild对于build命令,几乎所有的组件都是需要的,那么yarnworkspace提供一个快捷命令,可以一键执行所有组件包的构建命令:yarnworkspacesrunbuildstorybook至此,仓库的整体文件结构和组件库的依赖包管理就完成了,可以愉快的开发了组件,当组件的开发完成后,一般开发人员都会编写相应的使用文档,其中包含相应的使用示例。Storybook是一个可视化的组件管理展示平台,支持在隔离开发环境下交互展示组件,支持vue、react等。安装使用:npx-p@storybook/clisbinit--typevueyarnaddvue-Wyarnaddvue-loadervue-template-compiler--dev-W修改配置:安装完成后,在根目录下的.storybook文件夹下存放storybook使用的所有配置文件,修改main.js中的stories属性,指向到包的所有组件下的.stories.js文件。"stories":["../packages/**/*.stories.mdx","../packages/**/*.stories.@(js|jsx|ts|tsx)"]添加组件示例:在输入组件包中添加Input.stories.js文件:importMInputfrom'./index'exportdefault{title:'MInput',component:MInput};exportconstText=()=>({components:{MInput},template:'',});exportconstPassword=()=>({components:{MInput},template:'',});默认导出为storybook页面左侧导航栏,每一个命名的导出都是一个sample。最后执行yarnstorybook,打开站点:lernalerna是babel团队开源的管理多包仓库的工具,也可以用来实现monorepo。安装lerna:npminstalllerna-ginitializelerna:lernainit会在项目根目录下添加lerna.json配置文件。可以使用lerna管理项目依赖:如果当前表单自定义组件依赖于input自定义组件,可以使用:lernaaddinput--scope=form也可以使用import命令导入本地包:lernaimport通过exec执行包中的相关命令,运行lernarun--scopemy-componenttestlernaexec--rm-rf./node_modules通过clean命令清除所有包的node_modules目录:mainlernacleanlearn的功能是一键发布在所有包的npm上:lernapublish将包发布到npm,需要登录,可以通过npmwhoami查看当前登录用户,通过npmlogin登录。单元测试单元测试是组件化开发中安装依赖必不可少的部分:"**/_tests_/**/*.[jt]s?(x)"],"moduleFileExtensions":["js","json",//告诉Jest处理`*.vue`文件"vue"],"transform":{//使用`vue-jest`处理`*.vue`文件".*\\.(vue)$":"vue-jest",//使用`babel-jest`js处理".*\\.(js)$":"babel-jest"}}添加babel配置文件babel.config.jsmodule.exports={presets:[['@babel/preset-env']]}添加测试命令"test":"jest"添加测试文件在组件包的_tests_文件夹下添加相关的js文件,比如在输入包inputfrom'../src/index.js'import下添加input.test.jsimport{mount}from'@vue/test-utils'describe('m-input',()=>{test('input-text',()=>{constwrapper=mount(input)expect(wrapper.html()).toContain('inputtype="text"')})})执行测试命令yarntesttest在命令行可以看到单元测试执行结果:rollup打包rollup是一个基于ESM的模块打包工具,和webpack相比,打包结果更小,适合打包框架或者组件图书馆。安装所需依赖:npmirolluprollup-plugin-terserrollup-plugin-vue@5.1.9vue-template-compiler-D需要注意的是安装vue时需要指定版本,否则会安装vue3。单组件打包和添加配置文件在组件中添加rollup.config.js文件。该文件是rollup打包的配置文件,指定起始文件、输出文件位置和格式、插件。从'rollup-plugin-terser'导入{terser}从'rollup-plugin-vue'模块导入vue.exports={输入:'src/index.js',输出:[{文件:'dist/index.js',format:'es'}],plugins:[vue({css:true,compileTemplate:true}),terser()]}添加可执行命令在package.json文件的scripts属性下添加打包命令:"build":"rollup-c"-c是指使用当前项目目录下的配置文件rollup.config.js执行yarnbuild命令,可以看到打包结果。多组分包装虽然可以采用上述单组分包装的方法对每个组分进行包装,但比较繁琐。您可以通过项目根目录下的配置文件来打包所有组件。这时候需要添加额外的依赖:npmi@rollup/plugin-jsonrollup-plugin-postcss@rollup/plugin-node-resolvecross-env-D指定组件的入口文件,添加到包中各包main和module属性下的.json文件:"main":"dist/cjs/index.js","module":"dist/es/index.js",设置环境变量使用cross-env设置环境变量,区分开发环境和生产环境:"build:prod":"cross-envNODE_ENV=productionrollup-c","build:dev":"cross-envNODE_ENV=developmentrollup-c"添加配置文件添加rollup。配置在项目的根目录下。js文件,会遍历packages文件夹下的所有文件夹并打包:{terser}from'rollup-plugin-terser'importpostcssfrom'rollup-plugin-postcss'import{nodeResolve}from'@rollup/plugin-node-resolve'constisDev=process.env.NODE_ENV!=='production'//公共插件配置constplugins=[vue({css:true,compileTemplate:true}),json(),nodeResolve(),postcss({//将css插入样式//inject:true,//将css放入与js提取相同的目录:true})]//如果不是开发环境,开启压缩isDev||plugins.push(terser())//包文件夹路径constroot=path.resolve(__dirname,'packages')module.exports=fs.readdirSync(root).filter(item=>fs.statSync(path.resolve(root,item)).isDirectory()).map(item=>{//获取每个包的配置文件constpkg=require(path.resolve(root,item,'package.json'))return{输入:path.resolve(root,item,'src/index.js'),output:[{exports:'auto',file:path.resolve(root,item,pkg.main),format:'cjs'},{exports:'auto',file:path.join(root,item,pkg.module),format:'es'},],plugins:插件}})这个一次性执行打包命令,可以一次性打包所有组件包现在有一个问题。每次打包都需要删除上次的打包结果,所以需要添加删除命令:安装依赖包:npmi-Drimraf为每个组件包添加del命令:Add中的"del":"rimrafdist"对根目录的clean命令:"clean":"yarnworkspacesrundel"此时执行yarnclean清除所有包的dist目录。ploptemplate至此,项目的整体架构已经搭建完成,接下来就是没完没了的添加组件,但是考虑到每个组件的初始化都有很多相同的工作需要手动完成,这部分工作可以通过plop交给机器。安装依赖:npmiplop-D创建模板文件在项目中添加plop-template/component文件夹,该文件夹下放置所有创建组件的模板文件。添加plopfile.js,这是plop插件执行的入口文件:module.exports=plop=>{plop.setGenerator('component',{description:'createacustomcomponent',prompts:[{type:'input',name:'name',message:'componentname',default:'MyComponent'}],actions:[{type:'add',path:'packages/{{name}}/src/{{name}}.vue',templateFile:'plop-template/component/src/component.hbs'}]})}为plop添加一个可执行命令,它会询问用户组件的名称,然后将其中的所有文件复制打包相关文件夹的模板。添加脚本命令"plop":"plop"此时在命令行执行yarnplopcomponent创建组件。