【青训营】- 跟着月影学JavaScript学习笔记(番外) 开发自己的Vue3组件

447 阅读3分钟

在培训结束后, 通过对轮播图组件的重写, 掌握了vue 3 组件库的开发过程, 特写此片文章总结

1 技术栈

简述下选择的技术栈及选择它们的理由

  • vite: dev启动速度极快, 非常适合开发调试
  • rollup: 更优秀的打包工具, 不过只适用于纯js/ts文件, 对于有静态资源文件的, 还是使用webpack打包更好
  • Vue 3: 组合式api真的很好用
  • TypeScript: 强类型/把一部分错误在编译器暴露出来, 一定是优势大于麻烦
  • TSX: 暂时不明, 但可以脱离template, 使用render函数

2 创建组件库模板

使用vite创建项目, 官方文档

利用vue-ts模板:

pnpx create-vite vue-component-template --template vue-ts

如果需要更复杂的模板, 例如使用vuex/vue-router, 需要使用degit工具, 从社区维护模板中寻找合适的项目, 具体内容参照官方文档

创建完成后, 需要对项目进行一些修改:

2.1 增加eslint和prettier进行代码检查

package.jsondevDependencies中增加如下依赖:

{
    "eslint": "^7.32.0",
    "eslint-plugin-import": "^2.24.1",
    "eslint-plugin-vue": "~7.16.0",
    "@typescript-eslint/eslint-plugin": "~4.29.3",
    "@typescript-eslint/parser": "~4.29.3",
    "@vue/eslint-config-standard": "~6.1.0",
    "@vue/eslint-config-typescript": "~7.0.0",
    "prettier": "^2.3.2"
}

在根目录增加eslint配置文件.eslintrc.js:

module.exports = {
    root: true,
    parser: "@typescript-eslint/parser",
    plugins: ["@typescript-eslint"],
    env: {
        node: true,
    },
    parserOptions: {
        ecmaVersion: 2020,
    },
    rules: {
        'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
        'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
        'space-before-function-paren': ['error', 'never'],
        'comma-dangle': ['error', 'only-multiline'],
    }
}

在根目录增加eslint忽略文件.eslintignore:

*.sh
node_modules
*.md
*.woff
*.ttf
.vscode
.idea
dist
/public
/docs
.husky
.local
/bin
.eslintrc.js
prettier.config.js
/src/mock/*
/lib

在根目录增加prettier配置文件.prettierrc:

{
  "trailingComma": "none",
  "tabWidth": 2,
  "semi": true,
  "singleQuote": true,
  "printWidth": 100
}

在根目录增加prettier忽略文件.prettierignore:

/dist/*
.local
.output.js
/node_modules/**
​
**/*.svg
**/*.sh
​
/public/*
/lib/**

2.2 修改目录结构

src修改为examples, 用它作为组件的示例工程

修改index.html, 将/src/main.ts调整为/examples/main.ts:

<body>
  <div id="app"></div>
  <script type="module" src="/examples/main.ts"></script>
</body>

新建目录packages, 作为组件的根目录

修改tsconfig.json, 调整ts的编译文件:

{
  "include": ["packages/**/*.ts", "packages/**/*.d.ts", "packages/**/*.tsx", "packages/**/*.vue"]
}

2.3 增加TSX支持

vite默认不支持tsx, 需安装@vitejs/plugin-vue-jsx:

pnpm add -D @vitejs/plugin-vue-jsx

在vite.config.js中使用:

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
​
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue(), vueJsx()]
});

2.4 使用rollup打包

package.jsondevDependencies中增加如下依赖:

{
    "@babel/core": "^7.15.0",
    "@rollup/plugin-babel": "^5.3.0",
    "@rollup/plugin-node-resolve": "^13.0.4",
    "@rollup/plugin-typescript": "^8.2.5",
    "@vue/babel-plugin-jsx": "^1.0.6"
}

使用babel处理tsx打包的问题, 增加配置文件.babelrc:

{
  "plugins": [["@vue/babel-plugin-jsx"]]
}

在根目录创建rollup.config.js:

import typescript from '@rollup/plugin-typescript';
import babel from '@rollup/plugin-babel';
import { name } from './package.json';
​
export default {
  input: './packages/index.tsx',
  output: {
    name,
    dir: 'lib',
    format: 'es',
    globals: {
      vue: 'Vue'
    }
  },
  external: ['vue'],
  plugins: [
    typescript(),
    babel({ babelHelpers: 'bundled', extensions: ['.ts', '.js', '.tsx'] })
  ]
};

修改package.json中的相关配置:

{
    "main": "lib/index.js",
    "types": "lib/index.d.ts",
    "scripts": {
        "dev": "vite",
        "build": "rollup -c",
        "serve": "vite preview"
    }
}

执行pnpm build即可在lib目录下看到打包后的文件

3 小坑

3.1 TypeScript的类型声明文件

需要修改tsconfig.json:

{
    "compilerOptions": {
        "declaration": true,
        "declarationDir": "lib"
    }
}

3.2 组件全局注册时的类型问题

编写tsx组件时, 最后的类型声明文件很可能不存在install方法的定义, 增加了一个处理工具类componentUtil.ts:

import { App, Plugin } from 'vue';
​
const withInstall = <T>(comp: T): T & Plugin => {
  const c = comp as any;
  c.install = function(app: App) {
    app.component(c.name, comp);
  };
​
  return comp as T & Plugin;
};
​
export default {
  withInstall
};

修改组件的index.tsx:

import { defineComponent } from 'vue';
import ComponentUtil from './utils/ComponentUtil';
​
export default ComponentUtil.withInstall(
  defineComponent({
    setup() {
      return () => <div>Vue 3 Custom Component Template</div>;
    }
  })
);