安装vue vite unplugin-vue-define-options sass
安装css预处理器
pnpm add sass -D -w
pnpm add vite vue unplugin-vue-define-options -D -w
二、模块抽离
1、eslint-config抽离
一般市面上的组件库都会将eslint-config单独抽离出来
在packages下新建一个项目eslint-config
在packages/eslint-config的目录下初始化package.json
将根目录的package.json中的下面这些依赖移入到本项目中,根目录的package.json里的这些依赖就可以移除了,但需要保留eslint
{
"name": "@kunlun-design/eslint-config",
"version": "0.0.1",
"description": "eslint-config",
"main": "index.js",
"files": [
"index.js"
],
"keywords": [
"eslint",
"tslint",
"config",
"kunlun-design"
],
"author": "anixuil",
"license": "ISC",
"dependencies": {
"@typescript-eslint/eslint-plugin": "^5.48.1",
"@typescript-eslint/parser": "^5.48.1",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.9.0"
}
}
packages/eslint-config下创建index.js,将根目录的.eslintrc.js的内容拷贝过来,把root属性删除了
module.exports = {
parser: 'vue-eslint-parser',
extends: [
'eslint:recommended',
'plugin:vue/vue3-essential',
'plugin:@typescript-eslint/recommended',
'prettier',
'plugin:prettier/recommended'
],
env: {
browser: true,
es2021: true,
commonjs: true
},
plugins: ['@typescript-eslint', 'prettier', 'vue'],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
parser: '@typescript-eslint/parser'
},
rules: {
'prettier/prettier': 'error',
'no-extra-semi': 'off',
'no-undef': 'off', //未定义
'vue/multi-word-component-names': 'off', //驼峰
'@typescript-eslint/camelcase': 'off',
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-extra-semi': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-empty-interface': 'off'
}
}
根目录的.eslintrc.js
module.exports = {
root: true,
extends: ['@kunlun-design/eslint-config'],
ignorePatterns: ['!.*', 'node_modules']
}
在根目录的package.json添加@kunlun-design/eslint-config依赖
"@kunlun-design/eslint-config": "workspace:*"
最后在根目录pnpm install安装
2、utils工具包抽离
也不能叫抽离,毕竟还没写呢哈哈哈,先给他预留一个位置
在package里先建一个目录utils,然后初始化package.json
{
"name": "@kunlun-design/utils",
"version": "0.0.1",
"private": true,
"license": "MIT",
"main": "index.ts",
"peerDependencies": {
"vue": "^3.2.45"
}
}
packages/components/utils/component.ts
//组件类名的命名空间
type ClassName = string | undefined | null
type Classes = (ClassName | [any, ClassName, ClassName?])[]
export function createNamespace(name: string) {
const namespace = `kl-${name}`
//命名规范 suffix词尾 后缀
const createBEM = (suffix?: string): string => {
//如果没传那就是组件的名字 比如传个button进来 button是直接调用的createNamespace,所以这个方法就没传参,就直接返回namespace
if (!suffix) return namespace
return suffix.startsWith('--') ? `${namespace}${suffix}` : `${namespace}__${suffix}`
}
const classes = (...classes: Classes): any[] => {
return classes.map(className => {
if (Array.isArray(className)) {
const [condition, truthy, falsy = null] = className
return condition ? truthy : falsy
}
return className
})
}
return {
n: createBEM,
classes
}
}
// type:其作用就是给类型起一个新名字,可以作用于原始值(基本类型),联合类型,元组以及其它任何你需要手写的类型
// 所谓BEM,其实是三个单词的缩写:Block(模块)、Element(元素)、Modifier(修饰符)。
packages/components/utils/install.ts 这个我是照着别人写的,ts学的不行,看不太懂
import type { App, Plugin, AppContext } from 'vue'
export type SFCWithInstall<T> = T & Plugin
export type SFCInstallWithContext<T> = SFCWithInstall<T> & {
_context: AppContext | null
}
export const withInstall = <T, E extends Record<string, any>>(main: T, extra?: E) => {
;(main as any).install = (app: App): void => {
for (const comp of [main, ...Object.values(extra ?? {})]) {
app.component(comp.name, comp)
}
}
if (extra) {
for (const [key, comp] of Object.entries(extra)) {
;(main as any)[key] = comp
}
}
return main as SFCWithInstall<T> & E
}
export const withInstallFunction = <T>(fn: T, name: string) => {
;(fn as SFCWithInstall<T>).install = (app: App) => {
;(fn as SFCInstallWithContext<T>)._context = app._context
app.config.globalProperties[name] = fn
}
return fn as SFCInstallWithContext<T>
}
packages/components/utils/index.ts
export * from './component'
export * from './install'
三、样式统一
为了统一风格和以后便于维护,以及让使用者轻松的实现自定义主题,在开始编写组件之前都会提前定义好一些css变量。我们会使用scss+css变量的方式
但我们首先得把stylelint装上
1、stylelint配置
pnpm add stylelint stylelint-scss stylelint-config-standard-vue stylelint-config-prettier stylelint-config-html stylelint-order postcss-html postcss-scss -D -w
在根目录创建 .stylelint.config.js
module.exports = {
root: true,
plugins: ['stylelint-order', 'stylelint-scss'],
customSyntax: 'postcss-html',
extends: [
'stylelint-config-standard-vue',
'stylelint-config-prettier',
'stylelint-config-html'
],
rules: {
'selector-class-pattern': null,
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['deep', 'global']
}
],
'selector-pseudo-element-no-unknown': [
true,
{
ignorePseudoElements: ['v-deep', 'v-global', 'v-slottted']
}
],
'at-rule-no-unknown': [
true,
{
ignoreAtRules: [
'tailwind',
'apply',
'variants',
'responsive',
'screen',
'function',
'if',
'each',
'include',
'mixin'
]
}
],
'no-empty-source': null,
'named-grid-areas-no-invalid': null,
'unicode-bom': 'never',
'no-descending-specificity': null,
'font-family-no-missing-generic-family-keyword': null,
'declaration-colon-space-after': 'always-single-line',
'declaration-colon-space-before': 'never',
'rule-empty-line-before': [
'always',
{
ignore: ['after-comment', 'first-nested']
}
],
'unit-no-unknown': [true, { ignoreUnits: ['rpx'] }],
'keyframes-name-pattern': null,
'order/order': [
[
'dollar-variables',
'custom-properties',
'at-rules',
'declarations',
{
type: 'at-rule',
name: 'supports'
},
{
type: 'at-rule',
name: 'media'
},
'rules'
],
{ severity: 'warning' }
],
'order/properties-order': [
'position',
'top',
'right',
'bottom',
'left',
'z-index',
'display',
'float',
'width',
'height',
'max-width',
'max-height',
'min-width',
'min-height',
'padding',
'padding-top',
'padding-right',
'padding-bottom',
'padding-left',
'margin',
'margin-top',
'margin-right',
'margin-bottom',
'margin-left',
'margin-collapse',
'margin-top-collapse',
'margin-right-collapse',
'margin-bottom-collapse',
'margin-left-collapse',
'overflow',
'overflow-x',
'overflow-y',
'clip',
'clear',
'font',
'font-family',
'font-size',
'font-smoothing',
'osx-font-smoothing',
'font-style',
'font-weight',
'hyphens',
'src',
'line-height',
'letter-spacing',
'word-spacing',
'color',
'text-align',
'text-decoration',
'text-indent',
'text-overflow',
'text-rendering',
'text-size-adjust',
'text-shadow',
'text-transform',
'word-break',
'word-wrap',
'white-space',
'vertical-align',
'list-style',
'list-style-type',
'list-style-position',
'list-style-image',
'pointer-events',
'cursor',
'background',
'background-attachment',
'background-color',
'background-image',
'background-position',
'background-repeat',
'background-size',
'border',
'border-collapse',
'border-top',
'border-right',
'border-bottom',
'border-left',
'border-color',
'border-image',
'border-top-color',
'border-right-color',
'border-bottom-color',
'border-left-color',
'border-spacing',
'border-style',
'border-top-style',
'border-right-style',
'border-bottom-style',
'border-left-style',
'border-width',
'border-top-width',
'border-right-width',
'border-bottom-width',
'border-left-width',
'border-radius',
'border-top-right-radius',
'border-bottom-right-radius',
'border-bottom-left-radius',
'border-top-left-radius',
'border-radius-topright',
'border-radius-bottomright',
'border-radius-bottomleft',
'border-radius-topleft',
'content',
'quotes',
'outline',
'outline-offset',
'opacity',
'filter',
'visibility',
'size',
'zoom',
'transform',
'box-align',
'box-flex',
'box-orient',
'box-pack',
'box-shadow',
'box-sizing',
'table-layout',
'animation',
'animation-delay',
'animation-duration',
'animation-iteration-count',
'animation-name',
'animation-play-state',
'animation-timing-function',
'animation-fill-mode',
'transition',
'transition-delay',
'transition-duration',
'transition-property',
'transition-timing-function',
'background-clip',
'backface-visibility',
'resize',
'appearance',
'user-select',
'interpolation-mode',
'direction',
'marks',
'page',
'set-link-source',
'unicode-bidi',
'speak'
]
},
ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts']
}
根目录新建一个 .stylelintrc.js
module.exports = {
extends: 'stylelint-config-standard-vue',
customSyntax: 'postcss-html',
rules: {
// 颜色值小写
'color-hex-case': 'lower',
// 注释前无须空行
'comment-empty-line-before': 'never',
// 使用数字或命名的 (可能的情况下) font-weight 值
'font-weight-notation': null,
// 在函数的逗号之后要求有一个换行符或禁止有空白
'function-comma-newline-after': null,
// 在函数的括号内要求有一个换行符或禁止有空白
'function-parentheses-newline-inside': null,
// url使用引号
'function-url-quotes': 'always',
// 字符串使用单引号
'string-quotes': 'single',
// 缩进
indentation: 4,
// 禁止低优先级的选择器出现在高优先级的选择器之后
'no-descending-specificity': null,
// 禁止空源
'no-empty-source': null,
// 禁止缺少文件末尾的换行符
'no-missing-end-of-source-newline': null
}
}
这里可能会遇到报错 有关stylelint-config-standard的,网上有解决方案
根目录创建 .stylelintingore
//这里写你想忽略的,如果没有可删除 比如我最终打包的目录叫lib 我肯定不希望它检查打包好的css文件,就可以把打包目录添加进去
2、样式编写
在packages/compoenents下创建styles目录用于存放样式变量
我的组件样式统一管理实现思路是这样的(button组件举例)
- type.scss 一个专门管理类型样式的地方,用来管理primary,default等这些type的样式
- size.scss 一个专门管理类似 mini small normal等尺寸大小样式的地方
- common.scss 作为一个样式统一出口文件
- var.scss 声明样式变量的地方
- root.scss 根文件,自动配置组件样式的地方
packages/components/styles/root.scss
@import './type.scss';
@import './size.scss';
@import './variables.scss';
$components: button, input, select;
@function typed($type, $key) {
@return map-get($type, $key);
}
@mixin kl-attribute($typeName, $attrKey, $attrName) {
#{$attrKey}: typed($typeName, $attrName);
}
//复用匹配组件
@mixin matchComponent($typeKey) {
@each $comKey, $comValue in $components {
.kl-#{$comKey}--#{$typeKey} {
@content;
}
}
}
:root {
//type类型判断
@each $typeKey, $typeValue in $typeStyle {
@include matchComponent($typeKey) {
// box-sizing: border-box;
border-radius: 5px;
//button
@include kl-attribute($typeValue, '--kl-bg-color', 'kl-bg');
@include kl-attribute($typeValue, '--kl-font-color', 'kl-fontColor');
@include kl-attribute($typeValue, '--kl-border', 'kl-border');
}
}
//size
@each $sizeKey, $sizeValue in $sizeStyle {
@include matchComponent($sizeKey) {
@include kl-attribute($sizeValue, '--kl-size-width', 'kl-width');
@include kl-attribute($sizeValue, '--kl-size-height', 'kl-height');
@include kl-attribute($sizeValue, '--kl-size-font', 'kl-fontSize');
}
}
}
packages/components/styles/type.scss
@import './var.scss';
//type样式管理
$typeStyle: (
'primary': (
kl-bg: $primary-bgColor,
kl-fontColor: $primary-fontColor,
kl-border: $primary-border,
kl-bg-active: $primary-bgColor-active
),
'success': (
kl-bg: $success-bgColor,
kl-fontColor: $success-fontColor,
kl-border: $success-border,
kl-bg-active: $success-bgColor-active
),
'info': (
kl-bg: $info-bgColor,
kl-fontColor: $info-fontColor,
kl-border: $info-border,
kl-bg-active: $info-bgColor-active
),
'warning': (
kl-bg: $warning-bgColor,
kl-fontColor: $warning-fontColor,
kl-border: $warning-border,
kl-bg-active: $warning-bgColor-active
),
'danger': (
kl-bg: $danger-bgColor,
kl-fontColor: $danger-fontColor,
kl-border: $danger-border,
kl-bg-active: $danger-bgColor-active
),
'default': (
kl-bg: $default-bgColor,
kl-fontColor: $default-fontColor,
kl-border: $default-border,
kl-border-active: $default-border-active
)
);
packages/components/styles/size.scss
@import './var.scss';
//size样式管理
$sizeStyle: (
'mini': (
kl-fontSize: $mini-fontSize,
// kl-paddingSize: $mini-paddingSize,
kl-width: $mini-width,
kl-height: $mini-height
),
'small': (
kl-fontSize: $small-fontSize,
// kl-paddingSize: $small-paddingSize,
kl-width: $small-width,
kl-height: $small-height
),
'normal': (
kl-fontSize: $normal-fontSize,
// kl-paddingSize: $normal-paddingSize,
kl-width: $normal-width,
kl-height: $normal-height
),
'large': (
kl-fontSize: $large-fontSize,
// kl-paddingSize: $large-paddingSize,
kl-width: $large-width,
kl-height: $large-height
),
'xlarge': (
kl-fontSize: $xlarge-fontSize,
// kl-paddingSize: $xlarge-paddingSize,
kl-width: $xlarge-width,
kl-height: $xlarge-heigth
)
);
packages/components/styles/var.scss
//样式变量统一声明
//全局通用样式
//字体
$font-color-white: white;
//边框
$border-none: none;
//过渡
$transition-quick: 0.1s ease-in;
$transition-normal: 0.3s ease;
$transition-slow: 0.5s ease;
// type
// primary
$primary-bgColor: #3a7afe;
$primary-bgColor-active: #075aff;
$primary-fontColor: $font-color-white;
$primary-border: $border-none;
//success
$success-bgColor: rgb(103, 194, 58);
$success-bgColor-active: rgb(47, 141, 0);
$success-fontColor: $font-color-white;
$success-border: $border-none;
//info
$info-bgColor: #85878b;
$info-bgColor-active: rgb(117, 118, 121);
$info-fontColor: $font-color-white;
$info-border: $border-none;
//warning
$warning-bgColor: rgb(230, 162, 60);
$warning-bgColor-active: rgb(170, 115, 32);
$warning-fontColor: $font-color-white;
$warning-border: $border-none;
//danger
$danger-bgColor: rgb(245, 108, 108);
$danger-bgColor-active: rgb(190, 62, 62);
$danger-fontColor: $font-color-white;
$danger-border: $border-none;
//default
$default-bgColor: white;
$default-fontColor: rgb(59, 59, 59);
$default-border: 1px solid $default-fontColor;
$default-border-active: 1px solid $primary-bgColor-active;
//size
//mini
$mini-fontSize: 12px;
// $mini-paddingSize: 5px 10px;
$mini-width: 55px;
$mini-height: 26px;
//small
$small-fontSize: 14px;
// $small-paddingSize: 10px 15px;
$small-width: 65px;
$small-height: 36px;
//normal
$normal-fontSize: 16px;
// $normal-paddingSize: 10px 20px;
$normal-width: 86px;
$normal-height: 40px;
//large
$large-fontSize: 18px;
// $large-paddingSize: 10px 20px;
$large-width: 95px;
$large-height: 43px;
//xlarge
$xlarge-fontSize: 20px;
// $xlarge-paddingSize: 10px 20px;
$xlarge-width: 100px;
$xlarge-heigth: 45px;
packages/components/styles/variables.scss
//样式变量声明
@import './var.scss';
:root {
//基本变量
//背景
--kl-bg-color: white;
//字体
--kl-font-color: rgb(59, 59, 59);
//边框
--kl-border-color: rgb(59, 59, 59);
--kl-border-width: 1px;
--kl-border-style: solid;
--kl-border: var(--kl-border-width) var(--kl-border-style) var(--kl-border-color);
//尺寸
--kl-size-width: #{$normal-width};
--kl-size-height: #{$normal-height};
--kl-size-font: #{$normal-fontSize};
}
packages/components/styles/common.scss
@import './root.scss';
packages/components/index.ts
import './styles/common.scss'