本文已参与「 掘力星计划 」,赢取创作大礼包,挑战创作激励金。
最近在与 村长 老师一起做 直播 ,给大家分享 vue devui 开源组件库的建设,前面三期以 tree 组件为栗子🌰,介绍了如何设计和开发Vue组件:
这次给大家分享组件库工程化相关的内容。
后续的直播也会分成两条线:
欢迎大家持续关注~
上一次直播内容回顾
最终实现的效果:
1 创建基础项目工程
使用yarn创建一个vite工程,并使用
vue-ts
(Vue3+TypeScript)模板。
yarn create vite vite-demo --template vue-ts
# npm init vite@latest vite-demo -- --template vue-ts
$ yarn create vite vite-demo --template vue-ts
yarn create v1.22.10
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 🔨 Building fresh packages...
success Installed "create-vite@2.6.6" with binaries:
- create-vite
- cva
Scaffolding project in /kagol/vite-demo...
Done. Now run:
cd vite-demo
yarn dev
✨ Done in 5.46s.
创建完成之后,非常友好地提示了下一步要做什么:
cd vite-demo
yarn dev
我们按照提示的步骤一步一步把项目先启动起来。
$ yarn
yarn install v1.22.10
warning package.json: No license field
info No lockfile found.
warning vite-demo@0.0.0: No license field
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 🔨 Building fresh packages...
success Saved lockfile.
✨ Done in 7.33s.
本地启动直接执行的是不带任何参数的vite
命令
"dev": "vite",
启动速度非常快,只花了402ms
$ yarn dev
yarn run v1.22.10
warning package.json: No license field
$ vite
Pre-bundling dependencies:
(this will be run only when your dependencies or config have changed)
vite v2.6.5 dev server running at:
> Local: http://localhost:3000/
> Network: use `--host` to expose
ready in 402ms.
浏览器看效果
在浏览器地址栏输入以下链接查看效果:
构建生产包
"build": "vue-tsc --noEmit && vite build",
生产构建的脚本,vue-ts
模板和vue
模板不一样的地方是,vue-ts
模板增加了类型检查的命令:
vue-tsc --noEmit
在构建的过程中,会检查有没有类型错误,并提示出来。
$ yarn build
yarn run v1.22.10
warning package.json: No license field
$ vue-tsc --noEmit && vite build
vite v2.6.5 building for production...
✓ 14 modules transformed.
dist/assets/logo.03d6d6da.png 6.69 KiB
dist/index.html 0.48 KiB
dist/assets/index.31b3d229.js 1.95 KiB / gzip: 1.03 KiB
dist/assets/index.459f8680.css 0.34 KiB / gzip: 0.24 KiB
dist/assets/vendor.2acfe60d.js 49.61 KiB / gzip: 19.93 KiB
✨ Done in 11.09s.
2 一些关键文件
使用tree
命令看下目录结构
$ tree -l 3
/kagol/vite-demo
├── README.md
├── index.html
├── package.json
├── public
| └── favicon.ico
├── src
| ├── App.vue
| ├── assets
| | └── logo.png
| ├── components
| | └── HelloWorld.vue
| ├── env.d.ts // vue-ts模板
| └── main.ts
├── tsconfig.json // vue-ts模板
└── vite.config.ts
directory: 4 file: 11
package.json
一个开源项目,首先关注的是它的package.json
文件,里面有该项目的基本信息、脚本命令和依赖库等信息。
"name": "vite-demo",
"version": "0.0.0",
"scripts": {
"dev": "vite", // 本地启动
"build": "vue-tsc --noEmit && vite build", // 构建生产包,增加了vue-tsc类型检查 vue模板为 vite build
"serve": "vite preview" // 预览生产包效果
"dependencies": {
"vue": "^3.2.16"
"devDependencies": {
"@vitejs/plugin-vue": "^1.9.3", // 提供 Vue 3 单文件组件支持
"typescript": "^4.4.3", // vue-ts模板
"vite": "^2.6.4",
"vue-tsc": "^0.3.0" // volar的子包,vue3的ts类型检查工具(可选) vue-ts模板
一共有5个依赖
运行态依赖:vue
开发态依赖:
@vitejs/plugin-vue 提供 Vue 3 单文件组件支持的vite插件
typescript
vue-tsc vue3的类型检查工具,可选
vite.config.ts
这个是vite的配置文件,里面主要引入了一个@vitejs/plugin-vue
插件,用来为vue3单文件组件提供支持。
后续我们要支持jsx
等其他功能,都可以通过配置相应的插件来提供支持。
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()]
tsconfig.json
这个文件是vue-ts
模板才有的,用来提供TypeScript支持。主要指定了一些ts的编译选项和需要编译的文件/文件夹。
// 编译选项 https://www.tslang.cn/docs/handbook/compiler-options.html
"compilerOptions": {
"target": "esnext", // 目标语言的版本
"useDefineForClassFields": true, // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#the-usedefineforclassfields-flag-and-the-declare-property-modifier
"module": "esnext", // 指定生成代码的模块标准
"moduleResolution": "node", // 决定如何处理模块
"strict": true, // 启用所有严格类型检查选项
"jsx": "preserve", // 在.tsx文件里支持JSX https://www.tslang.cn/docs/handbook/jsx.html
"sourceMap": true, // 生成目标文件的sourceMap文件
"resolveJsonModule": true, // 为了import json文件方便
"esModuleInterop": true, // 为了import cjs文件方便 https://zhuanlan.zhihu.com/p/148081795
"lib": ["esnext", "dom"] // 编译过程中需要引入的库文件的列表
// 指定编译器需要编译的文件或文件夹
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
index.html
index.html是网站的入口文件,它和一般的html文件不太一样,引入main.ts
文件入口ts文件的<script>
标签是带有type="module"
属性的,用来支持ES6模块。
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
没有加type="module"
的话,浏览器控制台会报错:
Uncaught SyntaxError: Cannot use import statement outside a module
main.ts
main.ts是vue的入口文件,主要创建了一个vue实例,并挂载到dom中。
这里可以用来安装vue插件。
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
App.vue
这个是Vue应用的根组件。
<script setup lang="ts">
// This starter template is using Vue 3 <script setup> SFCs
// Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup
import HelloWorld from './components/HelloWorld.vue'
</script>
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Hello Vue 3 + TypeScript + Vite" />
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
</style>
env.d.ts
vue组件的类型声明,不添加该文件会提示:
找不到模块“./App.vue”或其相应的类型声明。
3 添加 jsx 支持
我们希望能在项目中使用jsx编写的组件,先编写一个最简单的jsx函数式组件:HelloWorld.tsx
export const HelloWorld = () => <div>Hello World jsx</div>
然后在App.vue
中引入该组件。
import { HelloWorld } from './components/HelloWorld'
<HelloWorld />
报错:Uncaught ReferenceError: React is not defined
由于没有引入jsx的支持,意料之中的报错了
Uncaught ReferenceError: React is not defined
at HelloWorld (HelloWorld.tsx:1)
报这个错的原因,村长老师已经在直播间讲得很清楚了,一句话解释:
Babel解析xx.tsx文件时,会把它当成React组件,而咱们项目中又没有引入React,所以报错了
解决的方式就是引入一个@vitejs/plugin-vue-jsx
插件。
引入vite插件:@vitejs/plugin-vue-jsx
yarn add -D @vitejs/plugin-vue-jsx
在vite.config.ts
的plugins
中引入vueJsx插件
import vueJsx from '@vitejs/plugin-vue-jsx'
plugins: [vue(), vueJsx()]
接第一次直播的 tree 组件
在第一次直播中,我和村长一起给大家分享了如何参与开源项目,并给Vue DevUI组件库添加了一个基础版的Tree组件:
这个tree组件就是用jsx的方式写的,我们一起来回顾下:
新建一个tree.tsx
文件:
import { defineComponent, ExtractPropTypes, PropType } from 'vue'
// 定义类型和组件的props,这部分一般会放在一个单独的文件中
interface TreeItem {
label: string
children?: TreeData
type TreeData = Array<TreeItem>
const treeProps = {
data: {
type: Array as PropType<TreeData>,
default: () => [],
type TreeProps = ExtractPropTypes<typeof treeProps>
// vue组件定义,和`.vue`组件中的`<script>`标签中的一样,只是不再需要写`<template>`,setup中可以直接使用
export default defineComponent({
name: 'DTree',
props: treeProps,
setup(props: TreeProps) {
console.log('props:', props, props.data)
return () => {
return <div class="devui-tree">
{ props.data.map(item => <div>{ item.label }</div>) }
</div>
然后在App.vue
中使用 tree 组件,传入data
参数。
import { ref } from 'vue'
import DTree from './components/tree'
const data = ref([{
label: '一级 1',
children: [{
label: '二级 1-1',
children: [{
label: '三级 1-1-1'
}, ...])
<d-tree :data="data"></d-tree>
也欢迎我们之前的几期Vue DevUI组件库建设的内容:
如何给 tree 组件增加展开/收起功能
Vue DevUI开源指南02:实现一个能渲染多层节点的Tree组件
Vue DevUI开源指南01:提交我的第一次pr
欢迎参与devui开源项目
我们 DevUI
团队有多个开源项目,现在都在招募contributor
,欢迎大家一起参与开源中来!(感兴趣的小伙伴可以添加DevUI
小助手的微信:devui-official
,将你拉到我们的核心开发群)
Ng DevUI: github.com/DevCloudFE/…
Vue DevUI: gitee.com/devui/vue-d…
DevUI Admin github.com/DevCloudFE/…
DevUI
官网:devui.design/