Howdyjs组件库迁移Vue3设计与总结
January 30, 2021
将个人组件库Howdyjs使用Vue + Typescript进行重构,使用Vite构建开发站点,Rollup进行组件打包并分包发布NPM。
Link
主要变更
- 新版全面采用
Typescript
- Vue组件部分将使用
Vue3
重构,不向下兼容,有Vue2.X需求的请使用旧版 - 因各组件内的关联性不强,新版的组件库将进行分包发布,可便于按需加载
- 旧版多数包都将功能封装成Vue指令,并默认导出的是Vue指令,现在新版将默认导出原生构造函数,便于跨框架或原生使用,但同时保留了Vue指令封装的使用方式
- 使用
lerna
进行分包管理 - 使用
Rollup
进行组件打包 - 展示站点使用
Vite
搭建
架构说明
Vite
综合对比之后,发现采用Vite基本可以实现当前展示站点的所有功能,而且在开发环境下热更新速度极快,所以新版项目采用了Vite构建开发站点。
由于项目有导入.md文件的需求,而Vite并不能直接使用Webpack的markdown-loader,所以暂时自己写一个简单的markdown-plugin在Vite中使用
// vite.config.ts
const markdownPlugin = (options: any) => {
return {
name: 'markdown',
transform(code: string, id: string) {
if (!/\.md/.test(id)) {
return;
}
const result = marked(code, options);
return `export default ${JSON.stringify(result)}`;
}
};
};
export default {
plugins: [
vue(),
markdownPlugin({
highlight: (code: string) => {
if (code.includes('template')) {
return hljs.highlight('html', code).value;
} else if (code.includes('lang="ts"')) {
return hljs.highlight('typescript', code).value;
} else {
return hljs.highlightAuto(code).value;
}
}
})
]
}
Vite新版文档地址: https://vitejs.dev/
Lerna
Lerna是一个项目内包管理工具,虽然当前项目内的组件关联性不强,但也提前先引入了Lerna进行分包管理。
- 执行
npm run bootstrap
命令进行项目初始化. - 执行
npm run publish
命令可快速发包
Rollup打包
组件使用Rollup进行打包,执行npm run build:pkg
打包各Packages,包含cjs、es和其d.ts文件。
使用nodejs执行rollup打包,代码位于/scripts下,build.js为打包初始模板,一个组件会被打包出3种格式:cjs
/esm
/umd
,格式说明参考格式
Vue路由自动生成
使用Vite打包时,Vue路由懒加载是基于Rollup的动态引入插件的,它对我原站点的格式不太适用。而由于展示站点中,各个路由格式是具有一定通用性的,所有采用了一种读取文件目录自动生成路由文件的方式。
// scripts/gen-route.js
// 自动生成路由文件
const fs = require('fs');
const packagesDirs = fs.readdirSync('./src/pages');
const packagesMap = {};
packagesDirs.map(package => {
const exampleDirs = fs.readdirSync(`./src/pages/${package}/example`);
const exampleNum = exampleDirs.length;
packagesMap[package] = exampleNum;
});
const packages = Object.keys(packagesMap).map(key => {
return {
name: key,
exampleNum: packagesMap[key]
};
});
const routes = [
{
path: '/',
name: 'home',
component: 'i(../views/home.vue)'
},
...packages.map(pkg => {
const { name, exampleNum } = pkg;
return {
path: `/${name}`,
name: `${name}`,
redirect: `/${name}/readme`,
component: `i(../pages/${name}/index.vue)`,
children: [
{
path: `/${name}/readme`,
name: `${name}-readme`,
component: 'i(../components/PageReadme.vue)'
},
...Array.from({length: exampleNum}, (_, exampleIndex) => {
return {
path: `/${name}/example${exampleIndex + 1}`,
name: `${name}-example${exampleIndex + 1}`,
component: `i(../pages/${name}/example/example${exampleIndex + 1}.vue)`
};
})
]
};
})
];
const reg = /"i\((.*?)\)"/g;
const routesStr = JSON.stringify(routes, null, 2).replace(reg, (...arg) => `() => import("${arg[1]}")`);
const output = `
/* eslint-disable */
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory('/howdy/'),
routes: ${routesStr}
});
export default router;
`;
fs.writeFileSync('./src/router/index.ts', output);
执行命令npm run gen-router
后,会自动读取/packages下的包文件,然后生成出对应的路由。