这里写目录标题
- 1 创建Vue3项目
- 1.1 相关链接
- 1.2 Vue Router
- 1.3 Element
- 1.4 scss
- 1.5 mitt
- 1.6 axios
- 1.7 echarts
- 1.8 配置vite.config.js
- 2 CSS部分
- 2.1 样式穿透
- 2.2 `:style` :在样式中使用插值语法
- 3. ElementUI
- 3.1 rules: 数据验证
- 3.2 修改element.style中的样式
- 3.2.1 组件中的一个样式对应到浏览器中发生很大变化
- 3.3 分页器
- 3.4 `<el-image>`使用本地图片
- 4. 使用svg图标
- 5. 富文本编辑器
- dialog的Html部分
- js代码部分
- 父组件中部分
- 6.js部分
- 6.1 解构数组,成为一个新数组
- 6.2 Promise
- 与async 、await配合使用
- 6.2 every()和some()
- 6.2.1 every()
- 6.2.2 some()
- 6.2.3 与filter()组合使用
- 7 Vue 部分
- 7.1 `<component :is=''>`
- 7.2 给class动态添加属性
- 7.3 页面全屏
- 7.4 进度条
1 创建Vue3项目
1.1 相关链接
视频链接
视频配套文章
gitee代码
1.2 Vue Router
vue官网关于router的部分
// @/router/index.js
import { createRouter , createWebHashHistory } from "vue-router";
const routes = [
]
const router = createRouter({
history: createWebHashHistory(),
routes: routes,
})
export default router
// @/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from '@/router/index'
const app = createApp(App)
app.use(router)// 注册路由
app.mount('#app')
1.3 Element
官网
组件
npm install element-plus -D -S
图标库
npm install @element-plus/icons-vue -D -S
import { createApp } from 'vue'
import App from './App.vue'
// 导入自定义的路由
import router from '@/router/index'
// 导入element组件
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
// 导入element图标
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
// element组件的国际化
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(router)
app.use(ElementPlus, {
locale: zhCn,
})
app.use(ElementPlus)
app.mount('#app')
1.4 scss
Vue3安装scss教程
npm install sass-loader -S
npm install style-loader -S
npm install sass -S
// 文件在工程项目的根路径下 vite.config.js
export default defineConfig({
plugins: [vue()],
// 这个是配置scss
pluginOptions: {
'style-resources-loader': {
preProcessor: 'scss',
patterns: []
}
},
})
1.5 mitt
Vue3:mitt快速上手
npm install mitt -S -D
// @/utils/mitt.js
import mitt from 'mitt'
export const emitter = mitt()
1.6 axios
npm install axios -S -D
// @/http/index.js
import axios from "axios";
const instance = axios.create({
baseURL: 'http://localhost:5173/',
timeout: 1000,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
});
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response;
}, function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
return Promise.reject(error);
});
export default instance
// 发送请求
const data = {
key1 : value1,
key2 : value2
}
instance.post('/api/register', data)
// 将data发送到http://localhost:5173/api/register
// 服务器端就会接收到post请求,携带data数据
1.7 echarts
官网
npm install echarts -S -D
1.8 配置vite.config.js
// 在项目目录的根路径下,vite.config.js
// 在plugins下增加一下配置
server:{
port: 8080, // 端口号
open: true, // 自动打开浏览器
cors: true, // 允许跨域
}
2 CSS部分
2.1 样式穿透
使用了element-plus中的Tabs 标签页
在style中添加scoped
- 代码中class=’demo-tabs‘在编译后在样式的选择器后面添加属性选择器,实现了组件样式的私有化,里面的元素在编译后很明显没有data-v-2b2fc29这个属性
.el-tabs__item{
color: #333;
font-size:18px
}
- 使用上面的代码给
.el-tabs__item
修改属性,发现它没有data属性,需要使用样式穿透
使用了:deep
样式穿透后,就能修改该元素的属性了
:deep(.el-tabs__item){
color: #333;
font-size:18px
}
2.2 :style
:在样式中使用插值语法
<div :style="{backgroundColor:$a.todo?'red':'blue'}"></div>
3. ElementUI
3.1 rules: 数据验证
model和prop的值是由数据的变量决定的
3.2 修改element.style中的样式
通过修改elementUi组件的样式发现,在官网组件的下面,如果提供了样式的属性则通过该属性修改,如果没有提供,通过style修改,如果通过style无法修改,最好就不要动了,组件会有默认的样式,自己在<style>
中修改可能只修改了表面,有些会冲突,比较麻烦
加上!important
,deep
没有用
.el-menu{
--el-menu-hover-bg-color:#2d333b !important;
}
3.2.1 组件中的一个样式对应到浏览器中发生很大变化
这是一个导航的组件,在el-menu中没有添加颜色等样式时,对应的浏览器中也什么都没有
当在el-menu中添加了3个样式后,浏览器中增加了5项样式
而在<style>
中添加的样式,在组件中的某一部分样式不会起作用,达不到在标签中添加样式的效果,所以如果能在组件中添加的样式就不要在
3.3 分页器
- 分页器的html部分
<template>
<el-pagination
style="margin-top: 10px"
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 30, 40]"
layout=" prev, pager, next, jumper,->,total, sizes"
:total="totalPage"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</template>
style="margin-top: 10px" // 设置与上面表格部分的间隔
v-model:current-page="currentPage" // 当前页页码,发生改变时触发@current-change的回调函数
v-model:page-size="pageSize" // 每页显示条目个数
:page-sizes="[10, 20, 30, 40]" // 每页显示个数选择器的选项设置,发生改变时page-size发生变化,触发@size-change的回调函数
layout=" prev, pager, next, jumper,->,total, sizes" // 设置分页器的布局
:total="totalPage" // 总条目数
@size-change="handleSizeChange" // 每页显示的条目数有变化时触发该方法
@current-change="handleCurrentChange" // 当前页有变化时触发
- 分页器代码部分
<script setup lang="ts">
import { ref } from 'vue'
/**分页器相关 */
const currentPage = ref(1) // 当前页
const pageSize = ref(10) // 每页显示条目个数
const totalPage = ref(0) // 总条数
/**
* @function 页码发生改变时调用该方法,默认跳转到第一页
* @param {number} current 传入跳转到第几页,默认第一页。如要保持页面不变,传入currentPage.value
*/
async function requestPage(current: number = 1) {
// 参数的页码数大于总页数时,跳转到最后一页
if (current > Math.ceil(totalPage.value / pageSize.value)) {
currentPage.value = Math.ceil(totalPage.value / pageSize.value)
} else {
currentPage.value = current
}
// 根据当前页和每页显示多少条,请求数据
const result = await request()//发送请求
if (result.code === 200) {
pageSize.value = result.data.size
totalPage.value = result.data.total
}
}
// 加载该组件时,运行该方法
requestPage()
/**
* @callback sizeChange 每页显示条目个数发生改变时调用该方法
*/
function handleSizeChange() {
requestPage(currentPage.value)
}
/**
* @callback currentChange 页码发生改变时调用该方法
*/
function handleCurrentChange() {
requestPage(currentPage.value)
}
</script>
3.4 <el-image>
使用本地图片
Vue3+TypeScript+Vite如何使用require动态引入类似于图片等静态资源
<el-image :src="url"/>
const url = new URL(
'../images/dataScreen-header-btn-bg-r.png', // 本地图片的路径
import.meta.url,
).href
4. 使用svg图标
vue3使用svg图标 多种方式
-
以阿里巴巴的图标举例
-
复制图标的svg代码
-
新建个存放svg图标的目录,新建
.vue
文件
-
在
<template>
标签内粘贴svg
的代码
<template>
<svg t="1700026328900" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4071" width="200" height="200">
<path d="M512 128a288.64 288.64 0 0 1 288 288c0 132.48-203.52 384-288 465.28-84.48-78.72-288-332.8-288-465.28A288.64 288.64 0 0 1 512 128m0-64a352 352 0 0 0-352 352c0 192 320 544 352 544s352-349.44 352-544A352 352 0 0 0 512 64z" fill="#2C2C2C" p-id="4072"></path>
<path d="M512 576a160 160 0 1 1 160-160A160 160 0 0 1 512 576z m0-256a96 96 0 1 0 96 96A96 96 0 0 0 512 320z" fill="#2C2C2C" p-id="4073"></path>
<path d="M256 896m32 0l448 0q32 0 32 32l0 0q0 32-32 32l-448 0q-32 0-32-32l0 0q0-32 32-32Z" fill="#2C2C2C" p-id="4074"></path>
</svg>
</template>
-
在代码中也可以调整颜色和大小
-
使用
<template>
<-- 假装这里有代码-->
<navigation-icon class="icon"></navigation-icon>
<-- 假装这里也有代码-->
</template>
<--设置样式,调整大小等,这里无法调整颜色-->
.icon {
width: 24px;
height: 24px;
margin-right: 8px;
}
svg
做的图标要独占一行,使用布局将它安排到合适的地方
5. 富文本编辑器
wangEditor官网
可以设置的内容太多,详细看官网
- 安装
npm install @wangeditor/editor --save
npm install @wangeditor/editor-for-vue@next --save
- 配合
dialog
成为弹窗式的编辑器
dialog的Html部分
<template>
<el-dialog
v-model="dialogVisible"
:title="dialogTips"
width="50%"
:before-close="handleClose"
>
<!-- editor 部分-->
<div style="border: 1px solid #ccc">
<Toolbar
style="border-bottom: 1px solid #ccc"
:editor="editorRef"
:defaultConfig="toolbarConfig"
:mode="mode"
/>
<Editor
style="height: 500px; overflow-y: hidden;"
v-model="valueHtml"
:defaultConfig="editorConfig"
:mode="mode"
@onCreated="handleCreated"
/>
</div>
<!-- editor 部分结束-->
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消编辑</el-button>
<el-button type="primary" @click="uploadEditor">
提交
</el-button>
</span>
</template>
</el-dialog>
</template>
js代码部分
- 重点:
valueHtml
这个数据,它就是编辑区的内容 - 一般来说在显示
editor
时,要显示上一次的内容,在编辑完成后,将内容传到某个地方保存 - 虽然代码比较多,关键的地方是
dialog的显示和关闭
,valueHtml的读取和保存
,这四个地方需要编写,其他都是样式的设置等等不重要的东西
<script setup lang="ts">
import '@wangeditor/editor/dist/css/style.css' // 引入 css
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import {onBeforeUnmount, onMounted, ref, shallowRef} from "vue";
// 从这里开始是dialog的代码
import {ElMessage, ElMessageBox} from "element-plus";
// dialog可见/不可见
const dialogVisible = ref(false)
// dialog的title
const dialogTips = ref('我是dialog的标题')
// dialog右上角的'X'
const handleClose = (done: () => void) => {
ElMessageBox.confirm('你确定要关闭编辑器吗?')
.then(() => {
done()
})
.catch(() => {
// catch error
})
}
// 传递给父组件的state
const dilogData = ref()
// 传递给父组件的方法
function dialogVisibleFun() {
dialogVisible.value = true // 显示dialog
}
// 向父组件传递一个方法,这个方法用于父组件调用dialog,也就是dialog显示
defineExpose({
dialogVisibleFun,
dilogData // 这个数据在示例里作用:编辑区显示上次的编辑内容
})
// dialog代码到这里结束
// editor实例,必须用 shallowRef
const editorRef = shallowRef()
// 编辑区的内容,属于editor的部分
const valueHtml = ref()
// 编辑器默认的模式,或'simple'
const mode = ref('default')
// 编辑区可能要显示上一次的编辑内容,这里给个示例
onMounted(() => {
setTimeout(() => {
// 我这里假装内容dilogData是从父组件传来的,在这里内容也可以从数据库查询
valueHtml.value = dilogData
}, 1500)
})
// 工具栏配置,excludeKeys排除某些配置
const toolbarConfig = {excludeKeys: []}
// 工具栏显示的菜单,写菜单组 key 的值即可,写在下面的就是不再工具栏显示的
toolbarConfig.excludeKeys = [
'blockquote',
'bgColor',
'color',
'group-more-style',
'fontFamily',
'bulletedList',
'numberedList',
'lineHeight',
'todo',
'emotion',
'insertLink',
'group-video',
'insertTable',
'codeBlock',
'divider',
'fullScreen',
// 'group-image',
]
// 工具栏中的菜单点击后产生什么效果,都可以在这里编辑,这里演示的是上传图片功能
const editorConfig = { placeholder: '请输入内容...' ,
MENU_CONF : {
// 配置上传图片
'uploadImage':{
server: 'http://127.0.0.1:3007/editor/uploadEditor',
maxFileSize: 1 * 1024 * 1024,
methods: 'post',
fieldName: 'file', //上传文件名
metaWithUrl: true, // 参数拼接到 url 上
customInsert(res, insertFn) {
//insertFn方法一执行,就会给编辑区添加该参数中的内容
insertFn(res.data.url)
},
}
}
}
// 点击确定按钮,向数据库或别的地方提交编辑区的内容,这里也是示例
const uploadEditor = ()=> {
(async ()=>{
if('条件判定,比如服务器返回成功'){
// 提交成功则关闭dialog
dialogVisible.value = false
ElMessage({
message: '提交成功',
type: 'success',
})
}else{
ElMessage({
message: '提交失败,请另存编辑区的内容,以免丢失',
type: 'error',
})
}
})()
}
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
const handleCreated = (editor) => {
editorRef.value = editor // 记录 editor 实例,重要!
}
</script>
父组件中部分
- 在html部分添加组件,设置ref属性,有按钮点击显示dialog
<wangEditor ref="wangEditorVue"></wangEditor>
<el-button class="corporate-name-button" type="primary" plain @click="dialogVisable">编辑</el-button>
- script部分
const wangEditorVue = ref()
const dialogVisable = ()=>{
wangEditorVue.value.dialogVisibleFun()
wangEditorVue.value.dilogData = '编辑区显示上一次的内容'
}
6.js部分
6.1 解构数组,成为一个新数组
<script setup>
const a = [{
"id": 1,
"set_name": "swiper1",
},
{
"id": 2,
"set_name": "swiper2",
},
{
"id": 3,
"set_name": "swiper3",
}]
const [first,...intro] = a
console.log(first)
console.log(intro)
</script>
结果:
6.2 Promise
Promise用于表示一个异步操作的最终完成(或失败)及其结果值。
- resolve和reject是两个回调函数,
resolve()
执行会触发then,reject()
执行会触发catch,从而改变Promise到稳定状态,并且两个函数只能执行一个
<script>
new Promise((resolve, reject) =>{
setTimeout(() =>{
//成功的时候调用resolve
resolve('成功data')
//失败的时候调用reject
//reject('error message')
}, 1000)
}).then((data) =>{
//处理成功后的逻辑
console.log(data);//这个data 是接收的resolve参数--
}).catch((err) =>{
console.log(err);
})
</script>
// 结果:成功data
<script>
new Promise((resolve, reject) =>{
setTimeout(() =>{
//成功的时候调用resolve
//resolve('成功data')
//失败的时候调用reject
reject('error message')
}, 1000)
}).then((data) =>{
//处理成功后的逻辑
console.log(data);//这个data 是接收的resolve参数--
}).catch((err) =>{
console.log(err);
})
</script>
// 结果:error message
- 在一个promise链中,只要任何一个promise被reject,promise链就被破坏了,reject之后的promise都不会再执行,而是直接调用.catch方法。
与async 、await配合使用
- 使用await调用Promise,假如Promise中有then方法,则会执行then方法,返回值为undefined
<script setup lang="ts">
function doubleAfter2seconds(num: number): Promise<number | void> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2 * num)
}, 0)
}).then((result) => {
// resolve会执行then方法,result就是resolve传过来的参数(2 * num)
console.log('doubleAfter2seconds方法中:' + result)
})
}
async function testResult() {
// 不使用await修饰,得到的函数返回值就是Promise对象
let result = doubleAfter2seconds(30)
console.log('testResult方法中不使用await修饰:')
console.log(result)
//使用await修饰,并且Promise中使用then方法,则函数返回值是undefined
let result1 = await doubleAfter2seconds(30)
console.log('使用await修饰后:' + result1)
}
testResult()
</script>
代码执行结果:
- 在Promise中不使用then方法,会返回resolve方法中的参数
<script setup lang="ts">
function doubleAfter2seconds(num: number): Promise<number | void> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2 * num)
}, 0)
})
}
async function testResult() {
// 不使用await修饰,得到的函数返回值就是Promise对象
let result = doubleAfter2seconds(30)
console.log('testResult方法中不使用await修饰:')
console.log(result)
//使用await修饰,并且Promise中不使用then方法,则函数返回值就是Promise的resolve中的参数
let result1 = await doubleAfter2seconds(30)
console.log('使用await修饰后:' + result1)
}
testResult()
</script>
6.2 every()和some()
6.2.1 every()
一个数组中的所有元素 是否都满足给定的条件,只要有一个不满足返回false
const smallList = [0, 1, 2]
const result = smallList.every((item) => {
return item == 0
})
console.log('result', result)
const smallList = [0, 1, 2]
const result = smallList.every((item) => {
return item != 0
})
console.log('result', result)
以上两端代码result的结果都是false
6.2.2 some()
数组中只要有一个元素满足条件,返回true
const smallList = [0, 1, 2]
const result = smallList.some((item) => {
return item != 0
})
console.log('result', result)
const smallList = [0, 1, 2]
const result = smallList.some((item) => {
return item == 0
})
console.log('result', result)
上面两段代码中result的结果都是true
6.2.3 与filter()组合使用
给定两个数组
- 筛选出
bigList
中有,且smallList
中没有的元素 - 筛选出
bigList
中有,且smallList
中也有的元素,交集
const bigList = [0, 1, 2, 3, 4, 5]
const smallList = [0, 1, 2]
- 使用
every()
// 筛选出`bigList`中有,且`smallList`中没有的元素
const list = bigList.filter((item) => {
let a = smallList.every((item1) => {
return item1 != item
})
return a
})
console.log(list)
// [3, 4, 5]
// 筛选出`bigList`中有,且`smallList`中也有的元素,交集
const list = bigList.filter((item) => {
let a = smallList.every((item1) => {
return item1 != item
})
return !a
})
console.log(list)
// [0, 1, 2]
- 使用
some()
时,此处的判断条件不能使用!=
// 筛选出`bigList`中有,且`smallList`中也有的元素,交集
const list = bigList.filter((item) => {
let a = smallList.some((item1) => {
return item1 === item
})
return a
})
console.log(list)
// [0, 1, 2]
// 筛选出`bigList`中有,且`smallList`中没有的元素
const list = bigList.filter((item) => {
let a = smallList.some((item1) => {
return item1 === item
})
return !a
})
console.log(list)
// [3, 4, 5]
7 Vue 部分
7.1 <component :is=''>
- 效果:点击图标,交替更换图标
// html部分
<el-icon @click='changeIcon'>
<component :is="isFold ? 'Fold' : 'Expand'"></component>
</el-icon>
// js部分
import { ref } from 'vue'
const isFold = ref(false)
const changeIcon = () => {
isFold = !isFold
}
7.2 给class动态添加属性
<div
class="layout_slider"
:class="{ Fold: isFold }"
></div>
// 当isFold 位ture时,class中多了Fold属性
<div class="layout_slider Fold"></div>
// 当isFold 位false时,class中没有Fold属性
<div class="layout_slider"></div>
7.3 页面全屏
- 使用dom操作
const fullScreen = () => {
// 全屏则返回ture,不是全屏则返回none
const full = document.fullscreenElement
if (full) {
document.exitFullscreen()
} else {
document.documentElement.requestFullscreen()
}
}
7.4 进度条
- 安装进度条插件
pnpm i nprogress
- 在路由守卫中使用
// 路由鉴权:项目中的路由在什么条件可以访问,什么条件不能访问
import router from '@/router/index.ts'
// 进度条插件,爆红,要在vite-env.d.ts中声明
import nprogress from 'nprogress'
// 进度条样式,在文件node_modules/nprogress/nprogress.css中修改样式
import 'nprogress/nprogress.css'
// 全局守卫:项目当中任意路由切换都会触发的钩子
// 全局前置守卫
router.beforeEach((to, from, next) => {
nprogress.start()
next()
})
// 全局后置守卫
router.afterEach((to, from) => {
nprogress.done()
})
- 作为全局的进度条,该文件需在main中引入
import './permisstion'