yu-el-ui v0.0.5
Vue 3 + TypeScript + Vite + Eslint + prettier
安装pnpm ,node版本16.18.1
npm i -g pnpm
用vite创建vue3+ts项目
pnpm create vite
安装@types/node
pnpm add @types/node -D
vite.config.ts设置别名
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
components: resolve(__dirname, 'src/components'),
},
extensions: ['.js', '.ts', '.jsx', '.tsx', '.json', '.vue', 'mjs'],
},
tsconfig.json设置别名,不然vscode识别不了
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["./src/*"],
"components/*": ["./src/components/*"]
}
}
安装vue-router
pnpm add vue-roter
在src目录创建router文件夹,新建index.ts文件
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import Home from '@/views/index.vue'
const routes: Array = { path: '', name: 'Home页面', component: Home, },
const router = createRouter({ history: createWebHistory(), routes, })
export default router
>在main.ts文件引入
import { createApp } from 'vue' import './style.css' import App from './App.vue' import router from './router'
const app = createApp(App)
// 注册路由 app.use(router) app.mount('#app')
## 安装element-plus
pnpm add element-plus @element-plus/icons-vue
> 在main.ts文件引入
> >1.全局引用
import { createApp } from 'vue' import './style.css' import App from './App.vue' import router from './router' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import locale from 'element-plus/lib/locale/lang/zh-cn' import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
app.use(ElementPlus, { locale, })
for (const key, component of Object.entries(ElementPlusIconsVue)) { app.component(key, component) }
// 注册路由 app.use(router) app.mount('#app')
> 修改主题,创建样式并修改main.ts的引入
/ 只需要重写你需要的即可 / @forward 'element-plus/theme-chalk/src/common/var.scss' with ( $colors: ( 'primary': ( 'base': green, ), ), );
// 如果只是按需导入,则可以忽略以下内容。 // 如果你想导入所有样式: @use "element-plus/theme-chalk/src/index.scss" as *;
## 安装sass
pnpm add sass sass-loader -D
## 安装js-cookies
pnpm add @types/js-cookie js-cookie -D
## 安装axios并封装
pnpm add axios
>新建service文件夹,创建request.ts
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios' import { Result } from './types' import Cookies from 'js-cookie'
class YBRequest { private instance: AxiosInstance private options: AxiosRequestConfig constructor(options: AxiosRequestConfig) { this.options = options this.instance = axios.create(this.options)
this.instance.interceptors.request.use(
(config: any) => {
const token = Cookies.get('token')
if (token) {
config.headers.Authorization = token
}
return config
},
(err) => {
return err
},
)
this.instance.interceptors.response.use(
(res) => {
// 拦截响应的数据
if (res.data.code === 401) {
Cookies.set('token', '')
return res.data
}
return res.data
},
(err) => {
return err
},
)
}
request<T = any>(config: AxiosRequestConfig): Promise { return new Promise((resolve, reject) => { this.instance .request<any, AxiosResponse<Result>>(config) .then((res) => { resolve(res as unknown as Promise) }) .catch((err) => { reject(err) }) }) }
get<T = any>(config: AxiosRequestConfig): Promise { return this.request({ ...config, method: 'GET' }) }
post<T = any>(config: AxiosRequestConfig): Promise { return this.request({ ...config, method: 'POST' }) }
patch<T = any>(config: AxiosRequestConfig): Promise { return this.request({ ...config, method: 'PATCH' }) }
delete<T = any>(config: AxiosRequestConfig): Promise { return this.request({ ...config, method: 'DELETE' }) } }
export default YBRequest
>创建types.d.ts
export interface Result<T = any> { code: number data: T }
>创建index.ts
import YBRequest from './request'
export default new YBRequest({ baseURL: '/api', timeout: 10000, })
>在service建立对应模块的api文件夹进行分类
>>main/login/index.ts
import ybRequest from '@/service' import { GetDepartmentListParams } from './types'
enum DepartmentAPI { DepartmentList = '/department/list', }
export function getDepartmentList(params: GetDepartmentListParams) { return ybRequest.post({ url: DepartmentAPI.DepartmentList, data: params, }) }
>>main/login/types.d.ts
export interface GetDepartmentListParams { offset: number size: number } //可继承 // export interface GetDepartmentListParams extends ParamsPagination {}
## 安装eslint
pnpm add eslint -D
>初始化eslint
>> 执行npx eslint --init命令初始化eslint
You can also run this command directly using 'npm init @eslint/config'.
? How would you like to use ESLint? ... To check syntax and find problems
? What type of modules does your project use? ... JavaScript modules (import/export)
? Which framework does your project use? ... Vue.js
? Does your project use TypeScript? » No / Yes Yes
? Where does your code run? ... (用空格选中两个,回车确定) √ Browser √ Node
? What format do you want your config file to be in? ... JavaScript
The config that you've selected requires the following dependencies: eslint-plugin-vue@latest @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest ? Would you like to install them now? No
>>最后一个提示我们选择No,然后手动npm安装提示的依赖
pnpm add eslint-plugin-vue@latest @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest -D
>>在packeage.json添加命令
"scripts": { // 执行该命令eslint会检测当前项目下所有的.vue,.js,.ts,.jsx,.tsx文件是否符合eslint的代码规范,并尝试自动修复 "lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx --fix" },
>建立.eslintignore文件
.sh node_modules .md .woff .ttf .vscode .idea dist /public /docs .husky .local /bin .eslintrc.js prettier.config.js /src/mock/*
## 安装prettier
pnpm add prettier -D
> 配置.prettier.cjs
/*
- @Author: yubb
- @Date: 2023-04-26 15:37:48
- @LastEditTime: 2023-04-26 15:52:49
- @LastEditors: yubb
- @Description:
- @FilePath: \yubb-el-ui.prettierrc.cjs */ module.exports = { // 一行最多 100 字符 printWidth: 100, // 使用 2 个空格缩进 tabWidth: 2, // 不使用缩进符,而使用空格 useTabs: false, // 行尾不需要有分号 semi: false, // 使用单引号 singleQuote: true, // 对象的 key 仅在必要时用引号 quoteProps: 'as-needed', // jsx 不使用单引号,而使用双引号 jsxSingleQuote: false, // 尾随逗号 trailingComma: 'all', // 大括号内的首尾需要空格 bracketSpacing: true, // jsx 标签的反尖括号需要换行 jsxBracketSameLine: false, // 箭头函数,只有一个参数的时候,也需要括号 arrowParens: 'always', // 每个文件格式化的范围是文件的全部内容 rangeStart: 0, rangeEnd: Infinity, // 不需要写文件开头的 @prettier requirePragma: false, // 不需要自动在文件开头插入 @prettier insertPragma: false, // 使用默认的折行标准 proseWrap: 'preserve', // 根据显示样式决定 html 要不要折行 htmlWhitespaceSensitivity: 'css', // 换行符使用 lf endOfLine: 'lf', }
>建立.prettierignore
/dist/* .local .output.js /node_modules/**
/*.svg /*.sh
/public/*
## vscode安装eslint,prettier插件
{ // vscode默认启用了根据文件类型自动设置tabsize的选项 "editor.detectIndentation": false, // 重新设定tabsize "editor.tabSize": 2, // 每次保存的时候自动格式化 "editor.formatOnSave": true, "editor.codeActionsOnSave": { // 使用eslint来fix,包括格式化会自动fix和代码质量检查会给出错误提示 "source.fixAll.eslint": true }, // 把prettier设置为vscode默认的代码格式化工具 "editor.defaultFormatter": "esbenp.prettier-vscode", // vue文件的默认格式化工具选择prettier "vue": { "editor.defaultFormatter": "esbenp.prettier-vscode" } }
## ESLint与Prettier会有冲突,和vue的一些语法冲突
pnpm add eslint-config-prettier eslint-plugin-prettier vue-eslint-parser -D
>eslint-config-prettier 会关闭ESLint中有关代码格式化的配置
>eslint-plugin-prettier 把Prettier配置成ESLint的一个插件,让其当做一个linter规则来运行
>去掉驼峰写法,index
>修改.eslint.cjs
extends: 'eslint:recommended', 'plugin:vue/vue3-essential', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', , overrides: [], parser: 'vue-eslint-parser', parserOptions: { ecmaVersion: 'latest', sourceType: 'module', parser: '@typescript-eslint/parser', },
rules: { '@typescript-eslint/ban-types': 'error', { extendDefaults: true, types: { '{}': false, }, }, , 'vue/multi-word-component-names': [ 'error', { ignores: 'index', //需要忽略的组件名 }, ], },
## 在运行时,检测
pnpm add vite-plugin-eslint -D
> 修改vite.config.ts配置
import eslintPlugin from 'vite-plugin-eslint' import vueSetupExtend from 'vite-plugin-vue-setup-extend' import { resolve } from 'path' export default defineConfig({ plugins: [ vue(), vueSetupExtend(), eslintPlugin({ include: 'src//*.ts', 'src//.js', 'src/**/.vue', 'src/.ts', 'src/.js', 'src/*.vue', }), ], })
## 加些插件
### vite-plugin-vue-setup-extend
>此插件解决了:使用setup语法糖的时候没办法直接为组件定义name(页面缓存需要name属性)
pnpm add vite-plugin-vue-setup-extend -D
> 在vite.config.ts配置
import vueSetupExtend from 'vite-plugin-vue-setup-extend' plugins: vueSetupExtend ()
> 在页面中使用
### unplugin-auto-import
>自动引入vue等的api
pnpm add unplugin-auto-import -D
>在vite.config.ts配置
import AutoImport from 'unplugin-auto-import/vite'
plugins: [
AutoImport({
imports: 'vue', 'vue-router', 'pinia', // 自动导入vue、vue-router、pinia相关API
dts: 'src/auto-import.d.ts', // 生成 auto-import.d.ts
全局声明
eslintrc: {
enabled: true, // Default false
},
}),
]
>在eslintrc.cjs配置'./.eslintrc-auto-import.json',重启
### vite-plugin-compression
> 静态文件压缩
pnpm install vite-plugin-compression -D
>配置
import viteCompression from 'vite-plugin-compression' plugins: viteCompression({ verbose: true, disable: false, // 不禁用压缩 deleteOriginFile: false, // 压缩后是否删除原文件 threshold: 10240, // 压缩前最小文件大小 algorithm: 'gzip', // 压缩算法 ext: '.gz' // 文件类型 })
### rollup-plugin-visualizer
>打包后的视图文件——生成一个stats.html文件
pnpm install rollup-plugin-visualizer -D
>配置
import {visualizer} from 'rollup-plugin-visualizer' plugins: visualizer({ open:true, //注意这里要设置为true,否则无效 gzipSize:true, brotliSize:true }),
## 安装pinia
pnpm add pinia
>在src目录新建store文件夹,新建index.ts文件,代码如下:
// 因为用了“unplugin-auto-import”插件 可以不需要再import Pinia的API // import { createPinia } from 'pinia' // 创建pinia状态管理对象 const pinia = createPinia() export default pinia
>在main.ts文件中引入,代码如下:
import pinia from './store' app.use(pinia)
>在store文件夹中新建modules文件夹(当然也可以不建,我这是为了区分管理),新建user.ts文件,代码如下:
const useUserStore = defineStore('user', { state: () => { return { token: '', name: '', avatar: '', nickName: '', userId: null, dept: {}, permissions: [], permBtn: [], permCode: [], } }, getters: {}, actions: {}, })
export default useUserStore
>>使用 defineStore() 定义一个 Store ;defineStore() 第一个参数是 storeId (必须要),第二个参数是一个选项对象:中有state(是一个函数返回一个对象)、getters、actions
>>store 是一个用 reactive 包裹的对象,如果直接解构会失去响应性。我们可以使用 storeToRefs() 对其进行解构:
import useUserStore from '@/store/modules/user' const userStore= useUserStore() const { hello: myHello } = storeToRefs(userStore)
## 集成qiankun(微前端)父级项目
pnpm install qiankun
> 修改main.ts
import { registerMicroApps, start } from 'qiankun' registerMicroApps( { name: 'subApp', // 必须与微应用注册名字相同 entry: 'http://192.168.8.50:3301', // 入口路径,开发时为微应用所启本地服务,上线时为微应用线上路径 container: '#sub-app-container', // app.vue 配置的挂载容器 id activeRule: '/subapp', // 当访问路由为 home 时加载微应用 props: { // 主应用向微应用传递参数 }, }, , { //生命周期钩子函数 beforeLoad: async (app) => { console.log('beforeLoad', app) }, beforeMount: async (app) => { console.log('beforeMount ', app) }, afterMount: async (app) => { console.log('afterMount', app) }, beforeUnmount: async (app) => { console.log('beforeUnmount ', app) }, afterUnmount: async (app) => { console.log('afterUnmount', app) }, }, )
// 启动 qiankun start()
## 集成qiankun(微前端)子级项目
> 安装vite-plugin-qiankun
pnpm add vite-plugin-qiankun
>修改vite.config.ts
import qiankun from 'vite-plugin-qiankun' plugins: qiankun('subApp', { // 微应用名字,与主应用注册的微应用名字保持一致 useDevMode: true, }), , server: { headers: { 'Access-Control-Allow-Origin': '*', }, host: '0.0.0.0', port: 3301, },
> 修改router.ts
import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper' const router = createRouter({ history: createWebHistory(qiankunWindow.POWERED_BY_QIANKUN ? '/subApp/' : '/'), routes: routes, })
> 修改main.ts
import { createApp } from 'vue' import './style.css' import App from './App.vue' import pinia from './store' import router from './router' import ElementPlus from 'element-plus' import './style/element/index.scss' import Cookies from 'js-cookie' import locale from 'element-plus/lib/locale/lang/zh-cn' import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper' let instance: any = null function render(props: any = {}) { const { container } = props instance = createApp(App) instance.use(router) instance.use(pinia) // 注册全局api方法 // 注册所有图标 for (const key, component of Object.entries(ElementPlusIconsVue)) { instance.component(key, component) } // 注册ElementPlus instance.use(ElementPlus, { locale, // 语言设置 // size: Cookies.get('size') || 'medium' // 尺寸设置 }) instance?.mount(container ? container.querySelector('#app') : '#app') console.log('开始加载相关内容') } renderWithQiankun({ mount(props: any) { render(props) }, bootstrap() { console.log('%c', 'color:green;', ' ChildOne bootstrap') }, update() { console.log('%c', 'color:green;', ' ChildOne update') }, unmount(props: any) { console.log('unmount', props) instance.unmount() instance._container.innerHTML = '' instance = null }, }) if (!qiankunWindow.POWERED_BY_QIANKUN) { console.log('并不是qiankun渲染') render() }
## 封装自己的组件
> 给各自的组件都加上install
import YuButton from './src/index.vue'
YuButton.install = (App) => { App.component(YuButton.name, YuButton) }
export default YuButton
>统一暴露给外部
import YuButton from './button'
//按需引入 export { YuButton }
const components = YuButton
const YuUI = { install(App) { components.forEach((item) => { App.component(item.name, item) }) }, }
export default YuUI
## 安装vitepress
> 一个md和vue都可以使用的文档
pnpm add vitepress vitepress-theme-demoblock -D
>新建doces文件夹,创建首页
layout: home
title: Yu-el-ui
titleTemplate: 选项卡描述
editLink: true lastUpdated: true hero: name: Yu-el-ui text: vue3基础组件 tagline: Vue3 中基于Element-plus二次封装基础组件文档 image: src: /img/wocwin.jpg alt: Yu-el-ui actions: - theme: brand text: 安装指南 link: /components/ - theme: brand text: 组件预览 link: /components/TSelect/base.md features:
- icon: 🔨 title: 实际项目 details: 实际项目中碰到的疑点、难点,致力于更优的自我。。
- icon: 🧩 title: 基础组件 details: 基于Element-plus二次封装;使用组件 Demo 快速体验交互细节。。
- icon: ✈️ title: Vue驱动。 details: 享受 Vue3 + vite3 的开发体验,在 Markdown 中使用 Vue 组件,同时可以使用 Vue 来开发自定义主题。
>新建components,放对应的组件说明和示例
>新建.vitepress文件夹config.ts
import { defineConfig } from 'vitepress' import { demoBlockPlugin } from 'vitepress-theme-demoblock' export default defineConfig({ title: 'yuui基础组件文档', description: '基于Element-plus基础组件封装使用', lang: 'cn-ZH', base: '/yuui/', lastUpdated: true, themeConfig: { logo: '/favicon.ico', siteTitle: 'yuui基础组件文档', outline: 3, nav: { text: '基础组件', link: '/components/Yubutton/base.md' }, , sidebar:{ '/components': [ // { // text: '常用组件', // items: // { text: '下拉选择组件', link: '/components/TSelect/base.md' }, // { text: '下拉选择表格组件', link: '/components/TSelectTable/base.md' }, // // }, ] } }, markdown: { config(md) { md.use(demoBlockPlugin, { customClass: 'demoblock-custom' }) } } })
>新增命令
"docs:dev": "pnpm run register:components && vitepress dev docs",
"docs:build": "pnpm run register:components && vitepress build docs",
"register:components": "vitepress-rc"
## 通过github预览页面
> 修改config.ts里面的base
base: '/yu-el-ui/',
> 添加命令,deploy.sh
确保脚本抛出遇到的错误
set -e
生成静态文件
pnpm run docs:build
进入生成的文件夹
cd ./docs/.vitepress/dist
git init git add -A git commit -m 'deploy'
如果发布到 https://.github.io/
git push -f https://github.com/onewebstudy/yu-el-ui.git master:gh-pages
cd -
"deploy": "deploy.sh"