个人JS插件库重构开发记录

407 阅读2分钟

Howdyjs是笔者开源的一个JS插件工具库,旧版封装了一些Vue的指令与常用组件。现在将部分Vue指令抽离出来使用原生JS实现,而Vue组件则使用Vue3重构,同时将Document展示站点使用Vite重构。该文章记录重构开发的相关说明。

🔗Link

🌟主要变更

  1. 新版全面采用Typescript
  2. Vue组件部分将使用Vue3重构,不向下兼容,有Vue2.X需求的请使用旧版
  3. 因各组件内的关联性不强,新版的组件库将进行分包发布,可便于按需加载
  4. 旧版多数包都将功能封装成Vue指令,并默认导出的是Vue指令,现在新版将默认导出原生构造函数,便于跨框架或原生使用,但同时保留了Vue指令封装的使用方式
  5. 使用lerna进行分包管理
  6. 使用Rollup进行组件打包
  7. 展示站点使用Vite搭建

🚀当前包含插件

  1. @howdyjs/resize通过拖拽更改元素尺寸插件
  2. @howdyjs/img-zoom图片放大插件,支持按组浏览下一张图片
  3. @howdyjs/size-observer监听元素尺寸变化插件
  4. @howdyjs/scroll自定义滚动条插件
  5. @howdyjs/to-drag设置Dom可拖动插件
  6. @howdyjs/animation-dialog动画模态框Vue3组件
  7. @howdyjs/standard-tabs移动端标签页Vue3组件
  8. @howdyjs/mouse-menu自定义右键菜单Vue3组件
  9. @howdyjs/to-control拖拽更改元素大小与位置插件

🌈项目架构说明

Vite

综合对比之后,发现采用Vite基本可以实现当前展示站点的所有功能,而且在开发环境下热更新速度极快,所以新版项目采用了Vite构建开发站点。

由于项目有导入.md文件的需求,而Vite并不能直接使用Webpack的markdown-loader,所以暂时自己写一个简单的markdown-plugin在Vite中使用(最新已有成熟的官方提供的plugin)。

// 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文档地址:vitejs.dev/

Lerna

Lerna是一个项目多包管理工具,虽然当前项目内的组件关联性不强,但也提前先引入了Lerna进行分包管理。

  • 执行npm run bootstrap命令进行项目初始化.
  • 执行npm run publish命令可快速发包

更多Lerna的用法可参考:Lerna-如何优雅地管理多个npm包 - 知乎 (zhihu.com)

Rollup打包

组件库中各组件最终会使用Rollup进行打包,执行npm run build:pkg打包各Packages,包含cjs、es和其d.ts文件。

使用nodejs执行rollup打包,代码位于scripts目录下,build.js为打包初始模板,一个组件会被打包出3种格式:cjs/esm/umd,格式说明参考格式

其中类型文件则采用原生typescriptdeclaration生成d.ts文件

打包的时候会自动读取packages目录,各自单独打包出各格纹文件。

自动生成展示站点Vue路由

使用Vite打包时,Vue路由懒加载是基于Rollup的动态引入插件的,它对我原站点的格式不太适用。而由于展示站点中,各个路由格式是具有一定通用性的,所有采用了一种读取文件目录自动生成路由文件的方式。而在Vite中提供了Glob 导入方式,可以读取到文件目录。

// src/router/index.ts
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router';
const isHashRouterMode = import.meta.env.VITE_ROUTER_MODE === 'hash';
const packageMain = import.meta.glob('../pages/*/index.vue');
const exampleMain = import.meta.glob('../pages/*/example/*.vue');
const packageMainMap: Record<string, any> = {};
Object.keys(packageMain).map(key => {
  const reg = /^.*pages/(.*)/index.*$/;
  const pkg = key.replace(reg, (...args) => args[1]);
  packageMainMap[pkg] = packageMain[key];
});
const router = createRouter({
  history: isHashRouterMode ? createWebHashHistory() : createWebHistory('/howdy/'),
  routes: [
    {
      path: '/',
      name: 'home',
      component: () => import('../views/home.vue')
    },
    ...Object.keys(packageMainMap).map(pkg => {
      const examples = Object.keys(exampleMain).filter(p => p.includes(pkg));
      return {
        path: `/${pkg}`,
        name: `${pkg}`,
        redirect: `/${pkg}/readme`,
        component: packageMainMap[pkg],
        children: [
          {
            path: `/${pkg}/readme`,
            name: `${pkg}-readme`,
            component: () => import('../components/PageReadme.vue')
          },
          ...examples.map((example,index) => {
            return {
              path: `/${pkg}/example${index + 1}`,
              name: `${pkg}-example${index + 1}`,
              component: exampleMain[example]
            };
          })
        ]
      };
    })
  ]
});

export default router;

展示Vue文件源代码到页面

展示站点中会将各个Example的Vue源代码文件展示到页面上,再用highlightjs进行代码高亮显示。

在翻阅官方文档后,Vite在import资源文件时写入?raw可以将代码源文件直接读取出字符串,类似于webpack中的raw-loader功能。但再开发完打包部署后才发现会出现读取失败的情况,最后发现好像是rollup动态导入的问题。

相关ISSUES: Click here

最后暂时在开发启动或打包时直接将相关的Vue文件转换出Markdown文件。在项目中执行npm run vue-to-md进行转换。相关代码很简单,只是这种方式实现热更新太麻烦,后面再看下怎么采用更合适的方案吧。

const fs = require('fs');
const glob = require('glob');
const classifys = fs.readdirSync('src/pages');
classifys.map(classify => {
  fs.mkdirSync(`src/code/${classify}`, { recursive: true });
});
glob('src/pages/**/example/example*.vue', (err, files) => {
  if (err) {
    throw err;
  }
  files.map(file => {
    const codeFileName = file.replace('pages', 'code').replace('example/', '').replace('vue', 'md');
    const code = fs.readFileSync(file, 'utf8');
    const output = `\`\`\`vue\n${code}\`\`\``;
    fs.writeFileSync(codeFileName, output);
  });
});

⚡其他说明

  • 新版地址:Howdyjs
  • 旧版地址:Howdyjs-old(兼容Vue2,目前不维护了)
  • 用的是同一个github仓库,新版参考master分支,旧版参考howdy分支
  • 欢迎Star,如果有问题或更好的idea欢迎Issue与PR。