自己设计的Vue3的实用项目(内含对项目亮点的实现思路与介绍)
Posted 「零一」
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自己设计的Vue3的实用项目(内含对项目亮点的实现思路与介绍)相关的知识,希望对你有一定的参考价值。
在11月初的时候,我给自己定的目标:了解完 Vue3
,然后做一个小项目
其中,Vue3
是早就学完了的,然后也写了两篇总结或是心得吧,其中有很多都是在做项目中踩出来的坑,所以大家可以看一下,避免之后开发中遇到:
- 快速使用Vue3最新的15个常用API(400+ 个👍)
- 关于Vue3获取当前组件实例的 getCurrentInstance 方法的补充(30+ 个👍)
然后做的 Vue3
项目也是我自己构思出来,因为当时网上的项目也不多或是大部分都是商城项目,之前也写过很多类似的了,所以就还是打算自己写一个,我给它取名叫做 nav-url
,顾名思义就是一个网址导航栏,在我写这篇文章时,项目是已经上线并被我自己以及身边的小伙伴使用了的,下面放上预览链接 👇👇
点击即可预览 👉 项目预览链接
再放上项目源码地址 👉:项目源码链接(欢迎各位 star)
接下来就详细地介绍一下我的项目
设计初衷
我现在也是个非计算机专业的大四在校生,平时前端都是自学的,所以从初学到现在基本上都是通过白嫖网上的视频、买书或从图书馆借书看、逛技术博客长长见识等等。这期间我会看到很多实用的工具网站或一些有趣的网站,我都会把他们收藏下来,生怕之后找不到了,但是随着时间的推移,收藏的网站越来越多,我的浏览器收藏夹可能变成了这样
这些都是我很久之前收藏夹收藏的,要是按照这个势头,我的收藏夹不出半年就爆满了,到时候找网站都不方便,所以我就想做一个我自己的网站导航栏,要求不高 : 简单大方、方便快捷
于是就有了现在这个项目,如下图所示:
项目功能 && 特色
毕竟是个网址导航栏,所以功能非常的简单,但之后我会尽可能地去完善该项目的一些额外的功能
项目的功能:
✅ 标签的添加、修改、删除
✅ 网址的添加、修改、删除
✅ 搜索功能
✅ 配置的导入、导出
项目的特色:
⭐ 基于 Vue3
开发
⭐ 页面简单大方
⭐ 提供网站图标、名称的获取接口
⭐ 标签栏支持多种 icon
选择
⭐ 通过 localStorage
存储,无需配置数据库
⭐ 用 Vue3
封装了 Element UI
的 message
、dialog
、button
、input
、popover
组件
⭐ 通过 Vuex 4
进行状态管理
⭐ 页面的滚动动画
⭐ 支持一键保存导出数据、一键导入数据
项目文件结构
整个项目主要的文件都在 src
文件夹下,结构目录如下:
├── src
├── assets // 存放静态资源
├── components // 各种组件
│ ├── main // 页面主要内容相关组件
│ ├── tabs // 标签栏相关组件
│ └── public // 全局公共组件
├── network // 网络请求
├── store // Vuex
├── utils // 存放自己封装的工具
├── APP.vue
└── main.jsss
重点介绍
对于项目的逻辑代码,你们可以直接查看我的源码,全部都是用的 Vue3
语法写的
在最初做这个项目时,还没找到合适的 Vue3
组件库,所以我就根据自己的需求,封装了 message
、dialog
、input
、button
、popover
这样五个组件,其中重点讲一下 message
和 dialog
吧,另外还有这个项目的亮点:配置导入与导出
Dilog组件
首先是组件内容:
// lp-dialog.vue
<template>
<div class="lp-confirm-container" ref="lpConfirmAlert">
<div class="lp-confirm-box">
<div class="lp-confirm-title">
<span class="lp-confirm-title-txt"> title </span>
<span class="lp-confirm-title-close" @click="closeConfirm">✖</span>
</div>
<div class="lp-confirm-content">
<span class="lp-confirm-content-txt"> content </span>
</div>
<div class="lp-confirm-btn-groups">
<lp-button type="primary" class="lp-confirm-btn" @_click="sureConfirm">确定</lp-button>
<lp-button type="default" class="lp-confirm-btn lp-confirm-btn-cancel" @_click="closeConfirm">取消</lp-button>
</div>
</div>
</div>
</template>
<script>
import lpButton from '../lp-button/lp-button'
import ref from 'vue'
export default
components:
lpButton
,
props:
title:
type: String,
default: '提示'
,
content:
type: String,
default: '确定关闭吗?'
,
setup()
const status = ref(-1) // 存储用户点的状态,-1:未点击;0:取消;1:确定
const lpConfirmAlert = ref(null)
function removeElement()
lpConfirmAlert.value.parentNode.removeChild(lpConfirmAlert.value)
function closeConfirm()
status.value = 0
removeElement()
function sureConfirm()
status.value = 1
removeElement()
return removeElement, closeConfirm, sureConfirm, status, lpConfirmAlert
</script>
<style scoped>
/* 样式见源码,此处省略 */
</style>
这里我在 dialog
组件内设定了一个组件的状态变量 status
,用于确认用户的点击情况
再来看看组件的处理代码:
// lp-dialog.js
import lp_dialog from './lp-dialog.vue'
import defineComponent, createVNode, render, toRef, watch from 'vue'
const confirmConstructor = defineComponent(lp_dialog)
export const createDialog = (options) =>
if(!Object.prototype.toString.call(options) === '[Object Object]')
console.error('Please enter an object as a parameter');
options = options ? options :
// 生成组件实例
const instance = createVNode(
confirmConstructor,
options
)
// 渲染挂载组件
const container = document.createElement('div')
render(instance, container)
document.querySelector('#app').appendChild(instance.el)
// 初始化组件参数
const props = instance.component.props
Object.keys(options).forEach(key =>
props[key] = options[key]
)
// 获取组件的 status 状态变量
const status = toRef(instance.component.setupState, 'status')
// 返回 promise,方便外部调用
return new Promise((resolve, reject) =>
// 监听组件的按钮点击情况
watch(status, (now) =>
if(now == 0) reject();
else if(now == 1) resolve()
)
)
接下来把 dialog
作为一个方法注册到全局中,这个我就把它放在了 App.vue
文件中,通过 Vue3
的 provide
方法暴露在全局
<template>
<div id="app"></div>
</template>
<script>
import provide from 'vue'
import createDialog from './components/public/lp-dialog/lp-dialog.js'
export default
setup()
// 全局暴露创建 dialog 组件的方法
provide('confirm', createDialog)
</script>
然后在别的组件中使用 dialog
组件
<template>
<div class="tabs" @click="btnConfirm"></div>
</template>
<script>
import inject from 'vue'
export default
setup()
// 接收创建 dialog 组件的方法
let $confirm = inject('confirm')
btnConfirm()
// 调用方法
$confirm(
title: '提示', // 确认框的标题
content: '确认关闭吗?', // 消息内容
)
.then(() =>
console.log('确认')
)
.catch(() =>
console.log('取消')
)
return btnConfirm
</script>
这样就实现了一个基于 promise
的链式调用,可以设定用户点击了 确认 或 取消 之后的处理代码
Message组件
首先是组件内容:
// lp-message.vue
<template>
<div class="message_container"
:class="[
'show': isShow,
'hide': !isShow,
'enter': isEnter,
'leave': isLeave,
type
]"
:style="
'top': `$seed * 70px`
">
<div class="content">
<i :class="[
`lp-message-$type`,
'icon',
'fa',
'fa-info-circle': type == 'info',
'fa-check-circle': type == 'success',
'fa-times-circle': type == 'err',
'fa-exclamation-triangle': type == 'warning',
]"/>
<div class="txt"
:class="[`txt_$type`]">
content
</div>
</div>
</div>
</template>
<script>
export default
name: "lp-message",
props:
type:
type: String,
default: 'info'
,
lastTime:
type: Number,
default: 2500
,
content:
type: String,
default: '这是一条提示信息'
,
isShow:
type: Boolean,
default: false
,
isLeave:
type: Boolean,
default: false
,
isEnter:
type: Boolean,
default: false
,
seed:
type: Number,
default: 0
</script>
<style scoped>
/* 样式见源码,此处省略 */
</style>
然后是组件的处理代码:
// lp-message.js
import lp_message from "./lp-message.vue"
import defineComponent, createVNode, render from 'vue'
let MessageConstructor = defineComponent(lp_message)
let instance;
const instances = []
export const createMessage = (options) =>
if(!Object.prototype.toString.call(options) === '[object Object]')
console.error('Please enter an object as a parameter')
options = options ? options :
instance = createVNode(
MessageConstructor,
options
)
//挂载
const container = document.createElement('div')
render(instance, container)
document.querySelector('#app').appendChild(instance.el)
const cpn = instance.component
const el = instance.el
const props = cpn.props
props.seed = instances.length
// 初始化参数
Object.keys(options).forEach(key =>
props[key] = options[key]
)
// 加入到instances中管理
instances.push(instance)
// 消息框出现
setTimeout(() =>
props.isShow = true
props.isEnter = true
, 200)
// 消息框离开
setTimeout(() =>
props.isEnter = false
props.isShow = false
props.isLeave = true
, props.lastTime)
// 移除消息框
setTimeout(() =>
close(el)
, props.lastTime + 200)
// 关闭某个弹框
const close = (el) =>
instances.shift()
instances.forEach((v) =>
v.component.props.seed -= 1
)
document.querySelector('#app').removeChild(el)
这里模仿了 element-ui
的思想,把所有的 message
实力管理在一个数组中
然后我们要把其作为一个方法注册到全局中,这个我就把它放在了 App.vue
文件中,通过 Vue3
的 provide
方法暴露在全局
<template>
<div id="app"></div>
</template>
<script>
import provide from 'vue'
import createMessage from './components/public/lp-message/lp-message.js'
export default
setup()
// 全局暴露创建 message 组件的方法
provide('message', createMessage)
</script>
使用 message
组件,通过 inject
方法获取即可
<template>
<div class="main"></div>
</template>
<script>
import inject from 'vue'
export default
setup()
// 接收创建 message 组件的方法
let $message = inject('message')
// 调用方法
$message(
type: 'success', // 消息框的类型,可选:info | success | err | warning
content: '这是一条成功的消息', // 消息内容
lastTime: 5000 // 消息框持续的时间
)
</script>
Popover组件
这个组件我没有模仿 element-ui
,因为我不太喜欢它的那种调用方式,所以我就根据自己的奇思妙想设计了一下这个组件:既然这个组件是一个气泡框,那么必然需要一个元素来确定这个气泡框的出现位置,因此我想把这个组件做成通过自定义指令 v-popover
来调用
接下来看下我的设计过程哈
首先是组件的内容:
// lp-popover.vue
<template>
<div ref="popover"
:class="['lp-popover-container', position]"
:style="
'top': `$toppx`,
'left': `$leftpx`,
">
<div class="container-proxy">
<div class="lp-popover-title" v-html="title"></div>
<div class="lp-popover-content" v-html="content"></div>
</div>
</div>
</template>
<script>
import ref, onMounted, reactive, toRefs from 'vue'
export default
props:
title:
type: String,
default: '我是标题'
,
content:
type:<以上是关于自己设计的Vue3的实用项目(内含对项目亮点的实现思路与介绍)的主要内容,如果未能解决你的问题,请参考以下文章