HTML5 + CSS3 + Less + ES6+ + Vue3.x + Composition-API + Vite + Gulp + Rollup + Jest
npm init vite@latest ui --template vue
package.json
npm init -y
{
"name": "ui",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
# 简化版本
npm i -S clipboard mockjs vue vue-router
# or 指定版本
npm i -S [email protected] [email protected] [email protected] [email protected]
查看说明
可选
复制到剪切板可选
生成随机数据# 简化版本
npm i -D @rollup/plugin-node-resolve @types/jest @types/mockjs @types/node @typescript-eslint/eslint-plugin @typescript-eslint/parser @vitejs/plugin-vue @vue/compiler-sfc @vue/test-utils autoprefixer del eslint eslint-config-airbnb-base eslint-config-prettier eslint-plugin-import eslint-plugin-jest eslint-plugin-prettier eslint-plugin-vue gulp gulp-autoprefixer gulp-cssmin gulp-less gulp-postcss jest less markdown-it-container postcss-pxtorem prettier rollup rollup-plugin-terser rollup-plugin-typescript2 rollup-plugin-vue ts-jest typescript vite vite-plugin-vuedoc vue-jest vue-tsc
# or 指定版本
npm i -D @rollup/[email protected] @types/[email protected] @types/[email protected] @types/[email protected] @typescript-eslint/[email protected] @typescript-eslint/[email protected] @vitejs/[email protected] @vue/[email protected] @vue/[email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected]
查看说明
可选
转换 rem 单位.d.ts
类型文件tsconfig.json
# 全局安装
npm i -g typescript
# 初始化
tsc --init
UI组件库
examples/assets
:资源目录examples/components
:演示项目的公共组件examples/router
:路由examples/App.vue
:页面入口examples/main.ts
:主入口import { createApp } from "vue";
import router from "./router";
import App from "./App.vue";
const app = createApp(App);
app.use(router);
app.mount("#app");
vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
export default defineConfig({
plugins: [vue()]
});
.eslintrc.js
// 配置信息
const config = {
env: {
browser: true,
es2021: true,
node: true
},
extends: ["plugin:vue/essential", "airbnb-base", "plugin:prettier/recommended", "plugin:jest/recommended"],
parserOptions: {
ecmaVersion: 12,
parser: "@typescript-eslint/parser",
sourceType: "module"
},
plugins: ["vue", "@typescript-eslint"],
settings: {},
rules: {
}
};
module.exports = config;
typings/shims-vue.d.ts
// vue
declare module "*.vue" {
import { App, defineComponent } from "vue";
const component: ReturnType & {
install(app: App): void;
};
export default component;
}
jest.config.js
// 配置
const config = {
moduleFileExtensions: ["vue", "json", "js", "ts"],
preset: "ts-jest",
testEnvironment: "jsdom",
transform: {
"^.+\\.vue$": "vue-jest", // vue 文件用 vue-jest 转换
"^.+\\.ts$": "ts-jest" // ts 文件用 ts-jest 转换
},
testMatch: ["**/tests/unit/*.spec.ts"], // 匹配 tests/unit 目录下的 .ts 文件
verbose: true, // 显示冗余代码,true:显示测试用例,false:显示 console.log
bail: true, // 经历几次失败后停止运行测试
};
module.exports = config;
tests/unit/img.spec.ts
import { mount } from "@vue/test-utils";
import MeImg from "~/MeImg/index.vue"; // 引入主要测试的组件
describe("MeImg", () => {
const src = "http://dummyimage.com/100x100/0079cb/fff"; // 图片地址
// 测试传参 src
test("props src", () => {
// 向组件里传参,获取组件实例
const wrapper = mount(MeImg, {
props: { src }
});
const viewer = wrapper.find(".me-img"); // 获取 DOM
expect(viewer.exists()).toBeTruthy(); // 是否存在
const imgEl = viewer.find("img");
expect(viewer.exists()).toBeTruthy();
expect(imgEl.attributes("src")).toBe(src); // 传入的 src 地址是否在组件里正确
});
});
packages/index.ts
import type { App } from "vue";
/* 基础组件 start */
import MeImg from "./MeImg"; // 图片
/* 基础组件 end */
// 所有组件
const components: any[] = [MeImg];
/**
* 组件注册
* @param {App} app Vue 对象
* @returns {Void}
*/
const install = (app: App) => {
// 注册组件
components.forEach(component => app.component(component.name, component));
};
export { MeImg };
// 全部导出
export default {
install,
...components
};
packages/MeImg/index.vue
packages/MeImg/index.ts
import type { App } from "vue";
import MeImg from "./index.vue";
type SFCWithInstall = T & { install(app: App): void }; // vue 安装
// 安装
MeImg.install = (app: App) => {
app.component(MeImg.name, MeImg);
};
const InMeImg: SFCWithInstall = MeImg; // 增加类型
export default InMeImg;
theme-default/MeImg.less
/**
* @file 图片
*/
.me-img {
.inline-block;
// 相同的样式
.same-style {
display: block;
width: @img-size;
overflow: hidden;
}
img {
.same-style;
height: auto;
}
span {
.same-style;
height: 40px;
}
}
::: tip 完成
开发完成
:::
package.json
设置命令"scripts": {
"start": "npm run dev",
"dev": "vite -m development",
"build": "npm run build:theme && npm run build:package && npm run build:package:dts",
"build:docs": "vite build",
"build:theme": "gulp build -f build/gulpfile.prod.js",
"build:package": "rollup -c build/rollup.config.js",
"build:package:dts": "rollup -c build/rollup.config.dts.js",
"test:unit": "jest -c=jest.config.js --detectOpenHandles"
}
build/rollup.config.js
和 build/rollup.config.dts.js
import nodeResolve from "@rollup/plugin-node-resolve"; // 告诉 Rollup 如何查找外部模块
import typescript from "rollup-plugin-typescript2";
import vue from "rollup-plugin-vue"; // 处理vue文件
import { readdirSync } from "fs"; // 写文件
import { resolve } from "path";
const input = resolve(__dirname, "../packages"); // 入口文件
const output = resolve(__dirname, "../lib"); // 输出文件
const config = readdirSync(input)
.filter(name => !["theme-default", "index.ts", "types.ts"].includes(name))
.map(name => ({
input: `${input}/${name}/index.ts`,
external: ["vue"],
plugins: [
nodeResolve(),
vue(),
typescript({
tsconfigOverride: {
compilerOptions: {
declaration: false
},
exclude: ["node_modules", "examples", "tests"]
},
abortOnError: false,
clean: true
})
],
output: {
name: "index",
file: `${output}/${name}/index.js`,
format: "es"
}
}));
config.push({
input: `${input}/index.ts`,
external: ["vue"],
plugins: [
nodeResolve(),
vue(),
typescript({
tsconfigOverride: {
compilerOptions: {
declaration: false
},
exclude: ["node_modules", "examples", "tests"]
},
abortOnError: false,
clean: true
})
],
output: {
name: "index",
file: `${output}/index.js`,
format: "es"
}
});
export default config;
rollup.config.dts.js
import nodeResolve from "@rollup/plugin-node-resolve"; // 告诉 Rollup 如何查找外部模块
import { terser } from "rollup-plugin-terser";
import typescript from "rollup-plugin-typescript2";
import vue from "rollup-plugin-vue"; // 处理vue文件
import { resolve } from "path";
const input = resolve(__dirname, "../packages"); // 入口文件
const output = resolve(__dirname, "../lib"); // 输出文件
const config = [
{
input: `${input}/index.ts`,
output: {
format: "es",
file: `${output}/index.esm.js`
},
plugins: [
terser(),
nodeResolve(),
vue({
target: "browser",
css: false,
exposeFilename: false
}),
typescript({
useTsconfigDeclarationDir: false,
tsconfigOverride: {
include: ["packages/**/*"],
exclude: ["node_modules", "examples", "tests"]
},
abortOnError: false
})
],
external: ["vue"]
}
];
export default config;
build/gulpfile.base.js
const { src, dest, series, parallel } = require("gulp");
const less = require("gulp-less");
const autoprefixer = require("gulp-autoprefixer");
const cssmin = require("gulp-cssmin");
const del = require("del");
// 打包配置
const config = {
input: "../packages/theme-default/",
output: "../lib/theme-default"
};
// 导出配置项
exports.config = config;
// 复制字体
exports.copyfont = () => src([`${config.input}fonts/*`, `!${config.input}fonts/*.css`]).pipe(dest(`${config.output}/fonts`));
// 压缩font 里的 CSS
exports.minifontCss = () =>
src(`${config.input}fonts/*.css`)
.pipe(cssmin())
.pipe(dest(`${config.output}/fonts`));
// 删除之前css打包文件
exports.clean = done => {
del(
["*.css", "fonts"].map(name => `${config.output}/${name}`),
{ force: true }
);
done();
};
// 编译 LESS
const compile = () =>
src([`${input}*.less`, ...["base", "variable"].map(name => `!${input}${name}.less`)])
.pipe(less())
.pipe(
autoprefixer({
overrideBrowserslist: ["last 2 versions"]
})
)
.pipe(cssmin())
.pipe(dest(output));
exports.build = series(clean, parallel(compile, copyfont, minifontCss));
npm login
npm publish