theme: nico

前言

随着前端技术的发展,业界涌现出了许多的UI组件库。例如我们熟知的ElementUI,Vant,AntDesign等等。但是作为一个前端开发者,你知道一个UI组件库是如何被打造出来的吗?

读完这篇文章你将学会:

  • 如何使用pnpm搭建出一个menorepo环境
  • 如何使用vite搭建一个基本的Vue3脚手架项目
  • 如何开发调试一个自己的UI组件库
  • 如何使用vite打包并发布自己的UI组件库

作为一个前端拥有一个属于自己的UI组件库是一件非常酷的事情。它不仅能让我们对组件的原理有更深的理解,还能在找工作的时候为自己增色不少。试问有哪个前端不想拥有一套属于自己的UI组件库呢?

本文将使用Vue3和TypeScript来编写一个组件库,使用Vite+Vue3来对这个组件库中的组件进行调试,最后使用vite来对组件库进行打包并且发布到npm上。最终的产物是一个名为kitty-ui的组件库。

话不多说~ 接下来让我们开始搭建属于我们自己的UI组件库吧

menorepo环境

首先我们要了解什么是menorepo及它是如何搭建的吧

就是指在一个大的项目仓库中,管理多个模块/包(package),这种类型的项目大都在项目根目录下有一个packages文件夹,分多个项目管理。大概结构如下:

-- packages
-- pkg1
--package.json
-- pkg2
--package.json
--package.json

简单来说就是单仓库 多项目

目前很多我们熟知的项目都是采用这种模式,如Vant,ElementUI,Vue3等。打造一个menorepo环境的工具有很多,如:lerna、pnpm、yarn等,这里我们将使用pnpm来开发我们的UI组件库。

为什么要使用pnpm?

因为它简单高效,它没有太多杂乱的配置,它相比于lerna操作起来方便太多

好了,下面我们就开始用pnpm来进行我们的组件库搭建吧

使用pnpm

安装

npm install pnpm -g

初始化package.json

pnpm init

新建配置文件 .npmrc

shamefully-hoist = true

这里简单说下为什么要配置shamefully-hoist

如果某些工具仅在根目录的node_modules时才有效,可以将其设置为true来提升那些不在根目录的node_modules,就是将你安装的依赖包的依赖包的依赖包的...都放到同一级别(扁平化)。说白了就是不设置为true有些包就有可能会出问题。

安装对应依赖

我们开发环境中的依赖一般全部安装在整个项目根目录下,方便下面我们每个包都可以引用,所以在安装的时候需要加个 -w

pnpm i vue@next typescript less -D -w

因为我们开发的是vue3组件, 所以需要安装vue3,当然ts肯定是必不可少的(当然如果你想要js开发也是可以的,甚至可以省略到很多配置和写法。但是ts可以为我们组件加上类型,并且使我们的组件有代码提示功能,未来ts也将成为主流);less为了我们写样式方便,以及使用它的命名空间(这个暂时这里没用到,后面有时间再补

  • 配置tsconfit.json

这里的配置就不细说了,可以自行搜索都是代表什么意思。或者你可以先直接复制

npx tsc --init

tsconfig.json:

{
"compilerOptions": {
"baseUrl": ".",
"jsx": "preserve",
"strict": true,
"target": "ES2015",
"module": "ESNext",
"skipLibCheck": true,
"esModuleInterop": true,
"moduleResolution": "Node",
"lib": ["esnext", "dom"]
}
}

monorepo的实现

接下就是pnpm如何实现monorepo的了。

为了我们各个项目之间能够互相引用我们要新建一个pnpm-workspace.yaml文件将我们的包关联起来

packages:
- 'packages/**'
- 'examples'

这样就能将我们项目下的packages目录和examples目录关联起来了,当然如果你想关联更多目录你只需要往里面添加即可。根据上面的目录结构很显然你在根目录下新packages和examples文件夹,packages文件夹存放我们开发的包,examples用来调试我们的组件

examples文件夹就是接下来我们要使用vite搭建一个基本的Vue3脚手架项目的地方

手动搭建一个基于vite的vue3项目

其实搭建一个vite+vue3项目是非常容易的,因为vite已经帮我们做了大部分事情

初始化仓库

进入examples文件夹,执行

pnpm init

安装vite和@vitejs/plugin-vue

@vitejs/plugin-vue用来支持.vue文件的转译

pnpm install vite @vitejs/plugin-vue -D -w

这里安装的插件都放在根目录下

配置vite.config.ts

新建vite.config.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue' export default defineConfig({
plugins:[vue()]
})

新建html文件

@vitejs/plugin-vue 会默认加载examples下的index.html

新建index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script src="main.ts" type="module"></script>
</body>
</html>

注意:

vite 是基于esmodule的 所以type="module"

新建app.vue模板

<template>
<div>
启动测试
</div>
</template>

新建main.ts

import {createApp} from 'vue'
import App from './app.vue' const app = createApp(App) app.mount('#app')

此时会发现编译器会提示个错误:找不到模块“./app.vue”或其相应的类型声明

因为直接引入.vue文件 TS会找不到对应的类型声明;所以需要新建typings(命名没有明确规定,TS会自动寻找.d.ts文件)文件夹来专门放这些声明文件。

typings/vue-shim.d.ts

TypeScriptTS默认只认ES 模块。如果你要导入.vue文件就要declare module把他们声明出来。

declare module '*.vue' {
import type { DefineComponent } from "vue";
const component:DefineComponent<{},{},any>
}

配置脚本启动项目

最后在package.json文件中配置scripts脚本

...
"scripts": {
"dev": "vite"
},
...

然后终端输入我们熟悉的命令:pnpm run dev

vite启动默认端口为3000;在浏览器中打开localhost:3000 就会看我们的“启动测试”页面。

本地调试

新建包文件

本节可能和目前组件的开发关联不大,但是未来组件需要引入一些工具方法的时候会用到

接下来就是要往我们的packages文件夹冲填充内容了。

  • utils包

一般packages要有utils包来存放我们公共方法,工具函数等

既然它是一个包,所以我们新建utils目录后就需要初始化它,让它变成一个包;终端进入utils文件夹执行:pnpm init 然后会生成一个package.json文件;这里需要改一下包名,我这里将name改成@kitty-ui/utils表示这个utils包是属于kitty-ui这个组织下的。所以记住发布之前要登录npm新建一个组织;例如kitty-ui

{
"name": "@kitty-ui/utils",
"version": "1.0.0",
"description": "",
"main": "index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

因为我们使用ts写的,所以需要将入口文件index.js改为index.ts,并新建index.ts文件:(先导出一个简单的加法函数)

export const testfun = (a:number,b:number):number=>{
return a + b
}
  • 组件库包(这里命名为kitty-ui)

components是我们用来存放各种UI组件的包

新建components文件夹并执行 pnpm init 生成package.json

{
"name": "kitty-ui",
"version": "1.0.0",
"description": "",
"main": "index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

新建index.ts入口文件并引入utils包

import {testfun} from '@kitty-ui/utils'

const result = testfun (1,1)

console.log(result)
  • esno

由于组件库是基于ts的,所以需要安装esno来执行ts文件便于测试组件之间的引入情况

控制台输入esno xxx.ts即可执行ts文件

npm i esno -g

包之间本地调试

进入components文件夹执行

pnpm install @kitty-ui/utils

你会发现pnpm会自动创建个软链接直接指向我们的utils包;此时components下的packages:

{
"name": "kitty-ui",
"version": "1.0.0",
"description": "",
"main": "src/index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@kitty-ui/utils": "workspace:^1.0.1"
}
}

你会发现它的依赖@kitty-ui/utils对应的版本为:workspace:^1.0.0;因为pnpm是由workspace管理的,所以有一个前缀workspace可以指向utils下的工作空间从而方便本地调试各个包直接的关联引用。

到这里基本开发方法我们已经知道啦;接下来就要进入正题了,开发一个button组件

试着开发一个button组件

在components文件夹下新建src,同时在src下新建button组件目录和icon组件目录(新建icon为了便于调试);此时components文件目录如下

-- components
-- src
-- button
-- icon
-- index.ts
-- package.json

让我们先测试一下我们的button组件能否在我们搭建的examples下的vue3项目本引用~

首先在button下新建一个简单的button.vue

<template>
<button>测试按钮</button>
</template>

然后在button/index.ts将其导出

import Button from './src/button.vue'

export default Button

因为我们开发组件库的时候不可能只有button,所以我们需要一个components/index.ts将我们开发的组件一个个的集中导出

import Button from './button'

export {
Button
}

好了,一个组件的大体目录差不多就是这样了,接下来请进入我们的examples来看看能否引入我们的button组件

vue3项目使用button

上面已经说过执行在workspace执行 pnpm i xxx的时候pnpm会自动创建个软链接直接指向我们的xxx包。

所以这里我们直接在examples执行:pnpm i kitty-ui

此时你就会发现packages.json的依赖多了个

"kitty-ui": "workspace:^1.0.0"

这时候我们就能直接在我们的测试项目下引入我们本地的components组件库了,启动我们的测试项目,来到我们的 examples/app.vue 直接引入Button

<template>
<div>
<Button />
</div>
</template>
<script lang="ts" setup>
import { Button } from 'kitty-ui'
</script>

不出意外的话你的页面就会展示我们刚刚写的button组件了

好了万事具...(其实还差个打包,这个后面再说~);接下来的工作就是专注于组件的开发了;让我们回到我们的button组件目录下(测试页面不用关,此时我们已经可以边开发边调试边看效果了)

因为我们的button组件是需要接收很多属性的,如type、size等等,所以我们要新建个types.ts文件来规范这些属性

在button目录下新建types.ts



import { ExtractPropTypes } from 'vue'

export const ButtonType = ['default', 'primary', 'success', 'warning', 'danger']

export const ButtonSize = ['large', 'normal', 'small', 'mini'];

export const buttonProps = {
type: {
type: String,
values: ButtonType
},
size: {
type: String,
values: ButtonSize
}
} export type ButtonProps = ExtractPropTypes<typeof buttonProps>

TIPS

import type 表示只导入类型;ExtractPropTypes是vue3中内置的类型声明,它的作用是接收一个类型,然后把对应的vue3所接收的props类型提供出来,后面有需要可以直接使用

很多时候我们在vue中使用一个组件会用的app.use 将组件挂载到全局。要使用app.use函数的话我们需要让我们的每个组件都提供一个install方法,app.use()的时候就会调用这个方法;

我们将button/index.ts调整为

import button from './button.vue'
import type {App,Plugin} from "vue"
type SFCWithInstall<T> = T&Plugin
const withInstall = <T>(comp:T) => {
(comp as SFCWithInstall<T>).install = (app:App)=>{
//注册组件
app.component((comp as any).name,comp)
}
return comp as SFCWithInstall<T>
}
const Button = withInstall(button)
export default Button

此时我们就可以使用app.use来挂载我们的组件啦

其实withInstall方法可以做个公共方法放到工具库里,因为后续每个组件都会用到,这里等后面开发组件的时候再调整

到这里组件开发的基本配置已经完成,最后我们对我们的组件库以及工具库进行打包,打包之前如果要发公共包的话记得将我们的各个包的协议改为MIT开源协议

...
"license": "MIT",
...

vite打包

配置文件

打包们这里选择vite,它有一个库模式专门为我们来打包这种库组件的。

前面已经安装过vite了,所以这里直接在components下直接新建vite.config.ts(配置参数文件中已经注释):


import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue"
export default defineConfig(
{
build: {
target: 'modules',
//打包文件目录
outDir: "es",
//压缩
minify: false,
//css分离
//cssCodeSplit: true,
rollupOptions: {
//忽略打包vue文件
external: ['vue'],
input: ['src/index.ts'],
output: [
{
format: 'es',
//不用打包成.es.js,这里我们想把它打包成.js
entryFileNames: '[name].js',
//让打包目录和我们目录对应
preserveModules: true,
//配置打包根目录
dir: 'es',
preserveModulesRoot: 'src'
},
{
format: 'cjs',
entryFileNames: '[name].js',
//让打包目录和我们目录对应
preserveModules: true,
//配置打包根目录
dir: 'lib',
preserveModulesRoot: 'src'
}
]
},
lib: {
entry: './index.ts',
formats: ['es', 'cjs']
}
},
plugins: [
vue()
]
}
)

这里我们选择打包cjs(CommonJS)和esm(ESModule)两种形式,cjs模式主要用于服务端引用(ssr),而esm就是我们现在经常使用的方式,它本身自带treeShaking而不需要额外配置按需引入(前提是你将模块分别导出),非常好用~

其实到这里就已经可以直接打包了;components下执行: pnpm run build你就会发现打包了es和lib两个目录

到这里其实打包的组件库只能给js项目使用,在ts项目下运行会出现一些错误,而且使用的时候还会失去代码提示功能,这样的话我们就失去了用ts开发组件库的意义了。所以我们需要在打包的库里加入声明文件(.d.ts)。

那么如何向打包后的库里加入声明文件呢? 其实很简单,只需要引入vite-plugin-dts

pnpm i vite-plugin-dts -D -w

然后修改一下我们的vite.config.ts引入这个插件

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue"
import dts from 'vite-plugin-dts' export default defineConfig(
{
build: {...},
plugins: [
vue(),
dts({
//指定使用的tsconfig.json为我们整个项目根目录下掉,如果不配置,你也可以在components下新建tsconfig.json
tsConfigFilePath: '../../tsconfig.json'
}),
//因为这个插件默认打包到es下,我们想让lib目录下也生成声明文件需要再配置一个
dts({
outputDir:'lib',
tsConfigFilePath: '../../tsconfig.json'
}) ]
}
)

因为这个插件默认打包到es下,我们想让lib目录下也生成声明文件需要再配置一个dts插件,暂时没有想到其它更好的处理方法~

然后执行打包命令你就会发现你的es和lib下就有了声明文件

其实后面就可以进行发布了,发布之前更改一下我们components下的package.json如下:

{
"name": "kitty-ui",
"version": "1.0.0",
"main": "lib/index.js",
"module":"es/index.js",
"scripts": {
"build": "vite build"
},
"files": [
"es",
"lib"
],
"keywords": [
"kitty-ui",
"vue3组件库"
],
"author": "小月",
"license": "MIT",
"description": "",
"typings": "lib/index.d.ts"
}

解释一下里面部分字段

pkg.module

我们组件库默认入口文件是传统的CommonJS模块,但是如果你的环境支持ESModule的话,构建工具会优先使用我们的module入口

pkg.files

files是指我们1需要发布到npm上的目录,因为不可能components下的所有目录都被发布上去

开始发布

做了那么多终于到发布的阶段了;其实npm发包是很容易的,就拿我们的组件库kitty-ui举例吧

发布之前记得到npm官网注册个账户,如果你要发布@xx/xx这种包的话需要在npm新建个组织组织组织名就是@后面的,比如我建的组织就是kitty-ui,注册完之后你就可以发布了

首先要将我们代码提交到git仓库,不然pnpm发布无法通过,后面每次发版记得在对应包下执行 pnpm version patch你就会发现这个包的版本号patch(版本号第三个数) +1 了,同样的 pnpm version major major和 pnpm version minor 分别对应版本号的第一和第二位增加。

如果你发布的是公共包的话,在对应包下执行

pnpm publish --access public

输入你的账户和密码(记得输入密码的时候是不显示的,不要以为卡了)正常情况下应该是发布成功了

注意

发布的时候要将npm的源切换到npm的官方地址(https://registry.npmjs.org/); 如果你使用了其它镜像源的话

样式问题

引入我们打包后的组件你会发现没有样式,所以你需要在全局引入我们的style.css才行;如 main.ts中需要

import 'kitty-ui/es/style.css';

很显然这种组件库并不是我们想要的,我们需要的组件库是每个css样式放在每个组件其对应目录下,这样就不需要每次都全量导入我们的css样式。

下面就让我们来看下如何把样式拆分打包

处理less文件

首先我们需要做的是将less打包成css然后放到打包后对应的文件目录下,我们在components下新建build文件夹来存放我们的一些打包工具,然后新建buildLess.ts,首先我们需要先安装一些工具cpy和fast-glob

pnpm i cpy fast-glob -D -w
  • cpy

它可以直接复制我们规定的文件并将我们的文件copy到指定目录,比如buildLess.ts:

import cpy from 'cpy'
import { resolve } from 'path' const sourceDir = resolve(__dirname, '../src')
//lib文件
const targetLib = resolve(__dirname, '../lib')
//es文件
const targetEs = resolve(__dirname, '../es')
console.log(sourceDir);
const buildLess = async () => {
await cpy(`${sourceDir}/**/*.less`, targetLib)
await cpy(`${sourceDir}/**/*.less`, targetEs)
}
buildLess()

然后在package.json中新增命令

...
"scripts": {
"build": "vite build",
"build:less": "esno build/buildLess"
},
...

终端执行 pnpm run build:less 你就会发现lib和es文件对应目录下就出现了less文件.

但是我们最终要的并不是less文件而是css文件,所以我们要将less打包成css,所以我们需要用的less模块.在ts中引入less因为它本身没有声明文件所以会出现类型错误,所以我们要先安装它的 @types/less

pnpm i --save-dev @types/less -D -w

buildLess.ts如下(详细注释都在代码中)

import cpy from 'cpy'
import { resolve } from 'path'
import { promises as fs } from "fs"
import less from "less"
import glob from "fast-glob"
const sourceDir = resolve(__dirname, '../src')
//lib文件目录
const targetLib = resolve(__dirname, '../lib')
//es文件目录
const targetEs = resolve(__dirname, '../es') const buildLess = async () => {
//直接将less文件复制到打包后目录
await cpy(`${sourceDir}/**/*.less`, targetLib)
await cpy(`${sourceDir}/**/*.less`, targetEs) //获取打包后.less文件目录(lib和es一样)
const lessFils = await glob("**/*.less", { cwd: targetLib, onlyFiles: true }) //遍历含有less的目录
for (let path in lessFils) {
const lessPathLib = `${targetLib}/${lessFils[path]}`
const lessPathEs = `${targetEs}/${lessFils[path]}` //获取less文件字符串
const lessCode = await fs.readFile(lessPathLib, 'utf-8')
//将less解析成css
const code = await less.render(lessCode) //拿到.css后缀path
const cssPathLib = lessPathLib.replace('.less', '.css')
const cssPathEs = lessPathEs.replace('.less', '.css') //将css写入对应目录
await fs.writeFile(cssPathLib, code.css)
await fs.writeFile(cssPathEs, code.css)
} }
buildLess()

执行打包命令之后你会发现对应文件夹下多了.css文件

现在我已经将css文件放入对应的目录下了,但是我们的相关组件并没有引入这个css文件;所以我们需要的是每个打包后组件的index.js中出现如:

import "xxx/xxx.css"

之类的代码我们的css才会生效;所以我们需要对vite.config.ts进行相关配置

首先我们先将.less文件忽略external: ['vue', /.less/],这时候打包后的文件中如button/index.js就会出现

import "./style/index.less";

然后我们再将打包后代码的.less换成.css就大功告成了

...
plugins: [
... {
name: 'style',
generateBundle(config, bundle) {
//这里可以获取打包后的文件目录以及代码code
const keys = Object.keys(bundle) for (const key of keys) {
const bundler: any = bundle[key as any]
//rollup内置方法,将所有输出文件code中的.less换成.css,因为我们当时没有打包less文件 this.emitFile({
type: 'asset',
fileName: key,//文件名名不变
source: bundler.code.replace(/\.less/g, '.css')
})
}
}
}
...
]
...

我们最终的vite.config.ts如下

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue"
import dts from 'vite-plugin-dts' export default defineConfig(
{
build: {
target: 'modules',
//打包文件目录
outDir: "es",
//压缩
minify: false,
//css分离
//cssCodeSplit: true,
rollupOptions: {
//忽略打包vue和.less文件
external: ['vue', /\.less/],
input: ['src/index.ts'],
output: [
{
format: 'es',
//不用打包成.es.js,这里我们想把它打包成.js
entryFileNames: '[name].js',
//让打包目录和我们目录对应
preserveModules: true,
//配置打包根目录
dir: 'es',
preserveModulesRoot: 'src'
},
{
format: 'cjs',
//不用打包成.mjs
entryFileNames: '[name].js',
//让打包目录和我们目录对应
preserveModules: true,
//配置打包根目录
dir: 'lib',
preserveModulesRoot: 'src'
}
]
},
lib: {
entry: './index.ts',
formats: ['es', 'cjs']
}
}, plugins: [
vue(),
dts({
//指定使用的tsconfig.json为我们整个项目根目录下掉,如果不配置,你也可以在components下新建tsconfig.json
tsConfigFilePath: '../../tsconfig.json'
}),
//因为这个插件默认打包到es下,我们想让lib目录下也生成声明文件需要再配置一个
dts({
outputDir: 'lib',
tsConfigFilePath: '../../tsconfig.json'
}), {
name: 'style',
generateBundle(config, bundle) {
//这里可以获取打包后的文件目录以及代码code
const keys = Object.keys(bundle) for (const key of keys) {
const bundler: any = bundle[key as any]
//rollup内置方法,将所有输出文件code中的.less换成.css,因为我们当时没有打包less文件 this.emitFile({
type: 'asset',
fileName: key,//文件名名不变
source: bundler.code.replace(/\.less/g, '.css')
})
}
}
} ]
}
)

最后我们将打包less与打包组件合成一个命令(package.json):

...
"scripts": {
"build": "vite build & esno build/buildLess"
},
...

后续直接执行pnpm run build 即可完成所有打包啦

直接使用

如果你不想一步步的搭建,想直接使用现成的话,你可以直接把项目clone下来-> kittyui,然后你只需要以下几步便可将其完成

  • 安装pnpm

    npm i pnpm -g
  • 安装esno

    npm i esno -g
  • 安装所有依赖

    pnpm install
  • 本地测试

    进入examples文件夹执行 pnpm run dev 启动vue3项目
  • 打包

    pnpm run build

写在最后

由于作者水平有限,难免会存在一些错误或不妥之处,希望各位能够不吝指出,一定及时修改。如果你对这个项目有更好的想法或者建议也欢迎在评论区提出,不胜感激。

后续我会对一些常用组件进行开发,每个组件的开发都会以文章的形式展现出来以供大家参考。也欢迎大家将项目fork下来,提交自己组件或者对kittyui的修改到kittyui~

创作不易,你的点赞就是我的动力!如果感觉对自己有帮助的话就请点个赞吧,感谢~

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿

Vite+TS带你搭建一个属于自己的Vue3组件库的更多相关文章

  1. 从0搭建Vue3组件库:button组件

    button组件几乎是每个组件库都有的:其实实现一个button组件是很简单的.本篇文章将带你一步一步的实现一个button组件.如果你想了解完整的组件库搭建,你可以先看使用Vite和TypeScri ...

  2. 从0搭建vue3组件库: Input组件

    本篇文章将为我们的组件库添加一个新成员:Input组件.其中Input组件要实现的功能有: 基础用法 禁用状态 尺寸大小 输入长度 可清空 密码框 带Icon的输入框 文本域 自适应文本高度的文本域 ...

  3. 从0搭建vue3组件库:Shake抖动组件

    先看下效果 其实就是个抖动效果组件,实现起来也非常简单.之所以做这样一个组件是为了后面写Form表单的时候会用到它做一个规则校验,比如下面一个简单的登录页面,当点击登录会提示用户哪个信息没输入,当然这 ...

  4. 从0搭建vue3组件库:自动化发布、管理版本号、生成 changelog、tag

    今天看到一篇文章中提到了一个好用的工具release-it.刚好可以用在我正在开发的vue3组件库.纸上得来终觉浅,绝知此事要躬行,说干就干,下面就介绍如何将release-it应用到实际项目中,让组 ...

  5. 带你搭建一个简单的mybatis项目:IDEA+spring+springMVC+mybatis+Mysql

    最近小编有点闲,突发奇想想重温一下mybatis,然后在脑海中搜索了一下,纳尼,居然不太会用了,想到这里都是泪啊!!现在我所呆的的公司使用的是springboot+hebinate,编程都是使用的JP ...

  6. 从0搭建vue3组件库: 如何完整搭建一个前端脚手架?

    相信大家在前端开发中都使用过很多前端脚手架,如vue-cli,create-vite,create-vue等:本篇文章将会为大家详细介绍这些前端脚手架是如何实现的,并且从零实现一个create-kit ...

  7. 一个实用的 vite + vue3 组件库脚手架工具,提升开发效率

    无论是 vue2 全家桶还是 vue3 + vite + TypeScript,组件库的使用几乎大家都会,但自己开发一个独立组件库就不是每个人都掌握的,因为搭建组件库的基础开发环境,就会让很多同学望而 ...

  8. 使用Vue cli3搭建一个用Fetch Api的组件

    系列参考 ,英文原文参考 我的git代码: https://github.com/chentianwei411/Typeahead 目标: 建立一个输入关键字得到相关列表的组件,用Vuejs2和Fet ...

  9. 创建一个自己的Vue UI组件库,并将它发布在npm上

    本文仅限于入门级,没有成规模制作,希望能对你有所帮助. 因为在开发多个项目中可能会用到同一个组件,那么我们通过复制粘贴的形式更新,无异于是笨拙的,我们可以通过上传到npm后,不断迭代npm包来实现更新 ...

随机推荐

  1. python跑机器学习时报错:Process finished with exit code -1073740791 (0xC0000409)

    emmm...第二次遇到这个错误了,好好的好好的卷积神经网络突然就跑不起了.就弹出一堆信息也不报那行代码错了... 记录一下: 两次解决方法相同,删h5py包 Process finished wit ...

  2. Node爬取网站数据

    npm安装cheerio和axios npm isntall cheerio npm install axios 利用cheerio抓取对应网站中的标签根据链接使用axios获取对应页面数据 cons ...

  3. golang md5加密和python md5加密比较

    python md5加密和golang md5加密各有不同,记录于此做备忘 Python 方法 md5 import base64 import hashlib def get_md5_data(bo ...

  4. XCTF练习题---WEB---backup

    XCTF练习题---WEB---backup flag:Cyberpeace{855A1C4B3401294CB6604CCC98BDE334} 解题步骤: 1.观察题目,打开场景 2.打开以后发现是 ...

  5. 等了整整12年!Linux QQ昨天终于更新了!

    一个执着于技术的公众号 前言 2020年4月1日,腾讯QQ Linux版迎来最新版发布,详细版本号为v2.0.0 Beta2.上一个版本v2.3.2发布于2019年10月24日,时隔160天又迎来了更 ...

  6. Spring 源码(11)Spring Bean 的创建过程(2)

    Spring Bean 的创建过程介绍了FactoryBean 的创建方式,那么接下来介绍不是FactoryBean的创建方式,在创建过程中,又会分为单例的Bean的创建,原型类型的Bean的创建等. ...

  7. 实验:Python图形图像处理

    1. 准备一张照片,编写Python程序将该照片进行图像处理,分别输出以下效果的图片:(a)灰度图:(b)轮廓图: (c)变换RGB通道图:(d)旋转45度图. 2. 假设当前文件夹中data.csv ...

  8. Ruby 趣学笔记(二)

    Ruby 趣学笔记(二) 本文写于 2020 年 5 月 7 日 类的继承 之前忘记写了,Ruby 的继承写法是: class IPhone < Phone def initialize(id, ...

  9. 如何生成一个java文档

    如何生成一个java文档 众所周知,一个程序给别人看可能可以看懂,几万行程序就不一定了.在更多的时候,我们并不需要让别人知道我们的程序是怎么写的,只需要告诉他们怎么用的.那么,api文档就发挥了它的作 ...

  10. 资讯:IEEE1

    IEEE 2020 年 12 大技术趋势:边缘计算.量子计算.AI.数字孪生等 2020-02-06 以下是对2020年12大技术趋势的预测.IEEE计算机协会自2015年以来一直在预测技术趋势,其年 ...