前言
咱辛辛苦苦用typescript
写个组件库为了啥?不就是为了方便开发嘛。
- 通过npm实现一键安装,下载即玩✨(bushi
- 允许按需加载组件,打包大小 down down down 💟!!!
- 通过ts轻轻松松获取代码提示,写代码直接tab tab tab 😋...
最后实现的效果是:
-
main.js
引入当然按需引入也是支持的.
-
vue
文件使用:
项目结构
less
sss-ui-plus-master
├─ examples //测试用的文件
│ ├─ App.vue
│ └─ main.ts
├─ packages //组件
│ ├─ SButton (按钮组件)
│ │ ├─ index.ts //导出单个组件,比如这里导出SButton
│ │ └─ src
│ │ ├─ button.less //样式文件
│ │ ├─ button.ts //声明组件要用的props、emits
│ │ └─ button.vue
│ ├─ SIcon (图标组件)
│ ├─ ... //其他组件
│ └─ index.ts //用于导出所有的组件
└─ src //资源文件,前面应该说过了,不重点介绍
├─ hooks
├─ styles
│ ├─ animate.css
│ ├─ global.less
│ ├─ icons
│ └─ variable.less
├─ types
│ └─ index.ts
└─ utils
├─ decorator
└─ managers
我们最后要打包出来的目录结构
typescript
新建文件夹
├─ dist
│ ├─ @types //存放d.ts文件,作为编译器提示的入口文件
│ └─ index.css //所有的样式文件最终会编译在一起
├─ es //ES6打包格式
│ ├─ index.d.ts
│ ├─ index.mjs
│ ├─ node_modules
│ ├─ packages
│ │ ├─ SButton
│ │ ├─ SIcon
│ │ ├─ ...
│ │ ├─ index.d.ts
│ │ └─ index.mjs
│ └─ src
└─ lib //commonJS打包格式
| ├─ index.d.ts
| ├─ index.js
| ├─ node_modules
| ├─ packages
| │ ├─ SButton
| │ ├─ SIcon
| │ ├─ ...
| │ ├─ index.d.ts
| │ └─ index.js
| └─ src
└─ global.d.ts 全局提示
初识vite.config.ts
再用vite开发时,执行vite build
就是以此文件作为配置文件。这个文件实际上是暴露一个json格式的对象。现在介绍一些这个对象的属性。
plugin:[]
这个属性用于配置在打包时用的的插件build:{}
rollupOptions:{}
配置rollup打包时的配置信息(事实上,vite打包用的是rollup, 而不是webpack。简单来说,rollup更适合工具库的打包,webpack更适合web应用的打包)lib: {}
老实说,我没明白他有啥用😫
这次重点不再lib这个配置项中,在前两者,关于rollupOptions:
-
external:[]
用于指定项目中那些包是依赖包。比如我写了:cssimport a from "a"
如果a这个包是外部依赖包而不是自己写的,则需要配置在此, 当然你可以用专门的插件来处理这个问题,这里我用的依赖包暂时不是很多,可以手 动排除
-
input:String | []
指定打包入口,也就是组件库的入口文件 -
ouput:[]
指定打包的输出口,可以根据不同的打包格式,输出到不同的目录中。下面是我的配置项:java[ { format: "es", //指定打包格式 entryFileNames: "[name].mjs", //指定输出文件名(不用改动) preserveModules: true, //保留原目录结构(不动) exports: "named", //(指定导出方式,老实说你删了这个配置项都行) dir: "es", //配置打包根目录 }, { format: "cjs", entryFileNames: "[name].js", preserveModules: true, exports: "named", dir: "lib", }, ],
打包
再不使用插件的情况下打包
也就是执行一下代码:
php
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
import {resolve} from "path";
export default defineConfig({
plugins: [
vue(),
DefineOptions(),
],
build: {
rollupOptions: {
external: ["vue", '@vueuse/core', '@floating-ui/vue'],
input: './index.ts',
output: [
{
format: "es",
entryFileNames: "[name].mjs",
//让打包目录和我们目录对应
preserveModules: true,
exports: "named",
//配置打包根目录
dir: "es",
},
{
//打包格式
format: "cjs",
//打包后文件名
entryFileNames: "[name].js",
//让打包目录和我们目录对应
preserveModules: true,
exports: "named",
//配置打包根目录
dir: "lib",
},
],
},
lib: {
entry: "./index.ts",
name: 'sss-ui-plus',
fileName: 'sss-ui-plus',
formats: ["es", "umd", "cjs"],
}
},
})
最终会形成以下输出目录:
当我们展开子目录会发现,ts代码全都没了,这好么?这不好!
所以我们需要插件来就我们。
vite-plugin-dts
npm i vite-plugin-dts -D
import dts from "vite-plugin-dts";
这个插件会在打包之后为我们生成.d.ts的类型声明文件。有了类型声明文件,编译器才能获取到类型提示。
修改plugin
配置项:
css
plugins: [
vue(),
DefineOptions(),
dts({
outDir: ['es', "lib", 'dist/@types'],
tsConfigFilePath: resolve(__dirname, "tsconfig.json"),
}),
],
-
outDir
是ts文件的输出目录,es,lib两个可有可无,就是我们打包生成的两个目录es,lib,也就是在原目录下生成类型声明文件.dist/@types
则表示单独生成类型声明文件到此处,这也会是我们项目的声明文件入口。 -
tsConfigFilePath
用于执行那些地方的文件需要为其产生d.ts文件,这里我们直接读取tsconfig.json中的配置项:makefile修改其中的include配置项: 需要注意的是,需要根据自身项目的目录结构来修改 "include": [ "src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "packages/**/*.ts", "packages/**/*.d.ts", "packages/**/*.tsx", "packages/**/*.vue", "index.ts" ],
修改完成后vite build
输出如下:
发现有了d.ts文件
rollup-plugin-postcss
npm i rollup-plugin-postcss autoprefixer -D
import postcss from 'rollup-plugin-postcss'import autoprefixer from "autoprefixer"
仔细观察之前生成的项目目录,会发现之后在es下面才会有一个style.css文件,而这显然不是我们自己生成的。
现在我们需要生成一个index.css文件,这个文件储存所有的样式文件。 配置如下:
css
plugins: [
vue(),
DefineOptions(),
dts({
outDir: ['es', "lib", 'dist/@types'],
tsConfigFilePath: resolve(__dirname, "tsconfig.json"),
}),
postcss({
extract: 'index.css',
plugins: [autoprefixer()],
}),
],
extract
指定生成的文件地址plugins
指定生成样式文件是使用的插件,这里我们使用的autoprefixer
用于适配特殊的css属性给不同的浏览器,也就是不需要我们考虑css样式的浏览器兼容性。
最后会在es lib两输出目录下生成index.css文件
rollup-plugin-copy & rollup-plugin-delete
npm i rollup-plugin-copy rollup-plugin-delete -D
import copy from "rollup-plugin-copy"import del from "rollup-plugin-delete"
上面生成的index.css很显然应该是共用的,而不是在两个输出目录下都有一个,并且原本生成的style.css还在
这时候我们需要吧两个index.css保留一个并且移动到dist目录下,并且吧style.css删除掉
rollup-plugin-copy
帮助我们复制文件rollup-plugin-delete
帮助我们删除文件
修改plugin如下:
css
plugins: [
vue(),
DefineOptions(),
dts({
outDir: ['es', "lib", 'dist/@types'],
tsConfigFilePath: resolve(__dirname, "tsconfig.json"),
}),
postcss({
extract: 'index.css',
plugins: [autoprefixer()],
}),
copy({
targets: [
{src: 'es/*.css', dest: 'dist'},
],
verbose: true,
hook: 'generateBundle'
}),
del({
targets: [
// 设置删除规则,删除原来位置的 CSS 文件
'es/*.css',
'lib/*.css',
'dist/style.css',
],
hook: 'closeBundle', // 在 writeBundle 钩子时执行删除操作
}),
],
copy.targets:[]
配置需要复制的文件,src
代表文件原位置,dest
代表文件新位置。copy.verbose:Boolean
代表执行copy插件是要不要打印信息(建议开启)copy.hook
代表创建的执行时期,generateBundle
代表在打包期间执行del.targets:[]
需要删除的文件位置del.hook
执行的时期,closeBundle
代表在打包完成后执行
注意这两插件的hook保持不变
这之后你会得到:
rollup-plugin-terser
npm i rollup-plugin-terser -D
import {terser} from "rollup-plugin-terser";
打包之后需要压缩体积么? 选他就对了!, 这个插件可以帮你自定义压缩代码的规则
但是我们现在不是需要压缩,我们要的是保留某些特定的注释。
请看这样的效果:
可以看到当鼠标移动到timeout上面之后,会有通知的存活时间
这个提示。这是怎么做到的?其实就是一段注释而已:
是的,我们需要保留的便是这些特殊的注释
只需要在plugin中加入:
css
terser({
format: {
comments: 'all', // 保留所有注释(为了简单我全部保留了)
},
}),
完整的配置文件
php
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import DefineOptions from 'unplugin-vue-define-options/vite'
import {resolve} from "path";
import dts from "vite-plugin-dts";
import postcss from 'rollup-plugin-postcss'
import autoprefixer from "autoprefixer"
import copy from "rollup-plugin-copy"
import del from "rollup-plugin-delete"
import {terser} from "rollup-plugin-terser";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
DefineOptions(),
dts({
outDir: ['es', "lib", 'dist/@types'],
tsConfigFilePath: resolve(__dirname, "tsconfig.json"),
}),
postcss({
extract: 'index.css',
plugins: [autoprefixer()],
}),
terser({
format: {
comments: 'all',
},
}),
copy({
targets: [
{src: 'es/*.css', dest: 'dist'},
],
verbose: true,
hook: 'generateBundle'
}),
del({
targets: [
// 设置删除规则,删除原来位置的 CSS 文件
'es/*.css',
'lib/*.css',
'dist/style.css',
],
hook: 'closeBundle', // 在 writeBundle 钩子时执行删除操作
}),
],
build: {
rollupOptions: {
external: ["vue", '@vueuse/core', '@floating-ui/vue'],
input: './index.ts',
output: [
{
format: "es",
entryFileNames: "[name].mjs",
//让打包目录和我们目录对应
preserveModules: true,
exports: "named",
//配置打包根目录
dir: "es",
},
{
//打包格式
format: "cjs",
//打包后文件名
entryFileNames: "[name].js",
//让打包目录和我们目录对应
preserveModules: true,
exports: "named",
//配置打包根目录
dir: "lib",
},
],
},
lib: {
entry: "./index.ts",
name: 'sss-ui-plus',
fileName: 'sss-ui-plus',
formats: ["es", "umd", "cjs"],
}
},
})
.npmignore
在打包好我们的代码之后便是发布啦,再次之前我们需要创建一个.npmignore,代表我们不是所有的文件都需要上传的。 我们需要上传的结构有:
dist
存放了类型声明文件和样式文件es
ES6模式下的打包产出lib
CommonJS模式下的打包产出package.json
项目的描述文件README.md
项目的介绍文件global.d.ts
之后我们会用到
perl
{
"name": "sss-ui-plus", //项目名字,自己来吧
"main": "lib/index.js", //通过commonJS形式引入的入口文件
"module": "es/index.mjs", //通过ES6形式引入的入口文件
"types": "dist/@types/index.d.ts", //类型声明文件入口
"private": false, //是否是私有项目(要发布当然不能是私有)
"version": "0.0.0", //项目版本号(注意每次发布版本号不能是一样的)
"type": "module",
"author": { //作者信息
"name": "laster",
"email": "lasterxin@163.com"
},
"description": "适用于vue3的组件库", //项目描述
"keywords": [ //项目关键词(用于npm官网查找)
"UI",
"Vue3",
"typescript"
],
"license": "MIT", //项目遵循协议
"scripts": {},
"dependencies": {},
"devDependencies": {}
}
再次之后,登录你的npm账号发布就行啦!
global.d.ts
等发布之后你就会发现一个问题,明明我已经在全局引入并注册了组件,为什么在(App.vue)中使用的时候报错没有声明呢?并且只是报错没有声明但是浏览器会正常显示。
当我们使用element-plus并进入某一个组件时会看到:
此时你会看到有一个global.d.ts文件,并且里面对@vue/runtime-core
这个包进行了类型拓展,那么我们可不可以也这样干呢?当然可以!
在项目的根目录下创建global.d.ts文件,写入:
typescript
// GlobalComponents for Volar
declare module '@vue/runtime-core' {
export interface GlobalComponents {
SButton: typeof import('sss-ui-plus')['SButton'],
SInput: typeof import('sss-ui-plus')['SInput'],
SIcon: typeof import('sss-ui-plus')['SIcon'],
SLink: typeof import('sss-ui-plus')['SLink'],
SDialog: typeof import('sss-ui-plus')['SDialog'],
SDrawer: typeof import('sss-ui-plus')['SDrawer'],
}
interface ComponentCustomProperties {
$message: typeof import('sss-ui-plus')['message'],
$notify: typeof import('sss-ui-plus')['notify'],
$confirm: typeof import('sss-ui-plus')['confirm'],
}
}
export {}
之后发布后,就能解决问题啦!
写在最后
首先,这个项目地址是lastertd/sss-ui-plus: 适用于vue3的组件库 (github.com) 在这里求一个star✨
其次,若是在打包发布的过程中遇到什么问题,欢迎在讨论区写下,大伙一起掉头发hhh
最后,感谢看到最后💟💟💟