基于V3+TS+pinia开发
项目初始化:配置@符号
1.tsconfig.json文件下进行配置:
目的:让v3认识@符号
实现(在"compilerOptions"下添加两项):
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
2.vite.config.ts下进行配置:
目的:配置绝对路径赋予真实意义
实现:const path = require('path') // 导入node下的方法,获取绝对路径
resolve: {
alias: {
"@": path.resolve(__dirname, "./src")
}
}
在.vue文件中自动导入less文件:
在vite.config.ts下进行配置:
css: {
preProcessorOptions: {
less: {
additionalData: `
// 默认.vue文件要导入的.less文件
@import: "@/xxxx.less";
@import: "@/xxxxxx.less"
`
}
}
}
为vue3的构造器添加功能:封装全局组件插件;全局组件类型为any,解决办法:
src目录下创建一个global.d.ts文件:
import components1 from "./xxx/xxx.vue"
declare module 'vue' {
export interface GlobalComponents {
// 导入的全局组件名: typeof 导入的全局组件 // 将该全局组件的类型赋给该组件
components1: typeof components1;
}
}
吸顶效果:
思路,隐藏一个导航栏,在滚动到指定位置淡入,离开淡出,此时主要时获取页面被卷曲的头部,
window.onscroll手写事件代码冗余.
可下载插件:npm i @vueuse/core;
在指定位置:
import { useWindowScroll } from '@vueuse/core'
const { y } = useWindowScroll() // 此时y即被卷曲的头部
泛型设置默认值:
vite.config.ts配置文件下配置:
plugins: [vue({reactivityTransform: true})]
vue3中给标签定义ref属性并使用(忘了):
<img ref="imgRef" src="#">
import { ref } from 'vue'
// const ref名 = ref<标签类型 | null>(null)
const imgRef = ref<HTMLImageElement | null>(null)
图片懒加载:
它是一种常见的性能优化的手段
主要思路:src属性放一个假图片;自定义一个属性放真图片链接;
监听图片是否再可视区域内,是则将真图片替换至src属性上。
关键点:侦听元素标签是否在可视区域内
实现:vue自带的IntersectionObserver接口;
更方便的方法:
导入:import { useIntersectionObserver } from '@vueuse/core'
vueuse插件库的useIntersectionObserver(target, 回调函数)
// const { stop } = useIntersectionObserver(target, 回调函数) // stop()用于停止侦听
// target为要监听的元素;isIntersecting为boolean值true时在可视区
const { stop } = useIntersectionObserver(target, ([{ isIntersecting }]) => {})
当target可见时,触发函数,isIntersecting为true,将图片链接赋给指定元素的src属性,结束监听。
// 此时封装全局自定义指令,通过调用全局自定义指令实现图片懒加载;
// 若是图片链接有误,可使用默认图片替代,用onerror事件进行监听
数据懒加载:
主要思路:当块级区域出现在可视区后,发送ajax请求,对数据实现渲染,若未达到则不发送请求;
给块级区域定义一个ref属性,监听该区域是否在可视区域。
关键点实现:
导入:import { useIntersectionObserver } from '@vueuse/core'
vueuse插件库的useIntersectionObserver(target, 回调函数)
// const { stop } = useIntersectionObserver(target, 回调函数) // stop()用于停止侦听
// target为要监听的元素;isIntersecting为boolean值true时在可视区
const { stop } = useIntersectionObserver(target, ([{ isIntersecting }]) => {})
当target可见时,触发函数,isIntersecting为true,发送ajax,结束监听(stop())。
vue内置组件(transition):
场景:对需要渐变效果的标签使用,默认插槽获取<transition name="ph"> // 渐变效果的块级区域
</transition>内的值,并对其实现渐变效果。
触发条件:v-if、v-show、组件切换时
使用方式:
六个类(name值-xx-xx):ph-enter-active、ph-leave-active;
// 分别在显示过程、隐藏过程生效(全程)
ph-enter-from、ph-enter-to // 分别对应:显示过程开始与完成时的类名
ph-leave-from、ph-leave-to // 分别对应:隐藏过程开始与完成时的类名
transition属性(渐变时间、方式、哪些要渐变)定义在ph-enter-active、ph-leave-active中;
具体的变化写在:ph-xxx-from、ph-xxx-to (xxx: enter、leave)
自定义面包屑组件:
关键点:每一项间的分隔符&每一项的路由路径
1.分隔符:
面包容器内容为默认插槽,分隔符先由父组件绑定动态属性进行传值(可不传值,此时设置默认为 ''),
面包容器接收到该值后通过跨组件传值(provide、inject)实现向每一项面包屑进行(分隔符)传递,
若此时数据为''则使用默认字体图标否则使用传递过来的值。
2.路由路径
面包屑容器内容为默认插槽,故面包屑<Bread><BreadItem /><BreadItem /></Bread>都是以内容
形式存在于容器中,此时路径即在父组件中进行传递,子组件通过defineProps接收后写入
<router-link to="传递过来的值" />中,若未传值则该项默认当作span,显示当前路径,不可跳转。
watch VS watchEffect:
watch使用时相对复杂:
watch(参数1, 参数2, 参数3)
参数1:依赖;即要监听的数据:可以为数组、值、函数
参数2:回调函数:参数有新、旧两个值,函数体可进行相应操作;数据更新是回调函数触发
参数3:对象:两个属性(boolean值) immediate:是否自动触发一次、deep:是否开启深度侦听
watchEffect的特点:
// 1.不关心新旧值,回调函数无参数
// 2.会自动调用一次,并收集自身依赖(依赖可为多个)
// 3.依赖项变化时,自动执行回调
倒计时工具函数的封装(存放至utils目录下,多项目可用):
使用到插件@vueuse/core
import { useIntervalFn } from "@vueuse/core"
// const { pause:暂停, resume:开始 } = useIntervalFn(() => { // 要执行的操作 },
间隔时间, { immediate: 是否直接执行 })
import { ref } from 'vue'
// 倒计时工具
function useCountDown() {
const count = ref(0) // 初始值为0,渲染至页面的倒计时读秒
const { pause, resume } = useIntervalFn(() => {
if(count.value === 0) {
// 停止
pause()
}
// 倒计时效果
count.value--
}, 1000, {immediate: false})
// 开始倒计时 num表示倒计时的时长(秒)
function start(num: number) {
// 从何时开始倒数
count.value = num
// 开始计时
resume()
}
return { count, start } // count为页面显示的动态数字(剩余时间);start为开启计时的函数
}
// 使用
const { count, start } = useCountDown()
start(60) // 开启60秒倒计时
<span>{{ `剩余${count}秒` }}
商品放大镜组件的封装:
关键点:商品图片+遮罩层+等比放大后的图片;放大镜要用到定位(position)
技术:获取到鼠标在当前所在元素的(x, y)
此时可使用@vueuse/core中封装的方法实现
import { useMouseInElement } from '@vueuse/core'
// isOutSide: 是否在元素区域外boolean值;elementX、elementY分别表示在该元素区域的x、y轴
const { isOutside, elementX, elementY } = useMouseInElement(target) // target为当前元素
// position计算属性
const position = computed(() => {
let x = elementX.value - 遮罩层宽度的一半
let y = elementY.value - 遮罩层高度的一半
x = x < 0 ? 0 : (x > (target元素宽度 - 遮罩层宽度的一半) ? 200 : x)
y = y < 0 ? 0 : (y > (target元素高度 - 遮罩层高度的一半) ? 200 : y)
return {x, y}
})
将计算属性值赋给遮罩层的(top、left)并等比放大赋值给大图的(left、top)此时为**负值**(反向移动)
弹出消息框组件:
关键点:不同类型下的主题色,弹出框内容与弹出时机,过渡动画效果(transition标签的使用)
难点:因为在弹出时机上,直接在template标签中使用,此时位置固定,使用限制较大
(无法随时显示,比如拦截报错给提示就比较麻烦)
在组件目录下新建index.ts文件
实现:此时可以使用"vue"的内置api导入:import { h, render } from 'vue'
import Message from './xx/xx.vue' // 导入弹出框组件
const div = document.createElement('div') // 先创建一个标签
div.setAttribute('class', '具体类名') // 为新增的标签绑定类等属性
document.body.appendChild(div) // 将创建的元素添加至body的最后面
export default function message(obj: {type: 'success' | 'error' | 'warning', text: string}) {
// 创建一个虚拟dom;h(type, props:标签属性, children:标签内容)
const vNode = h(Message, {type: obj.type, text: obj.text}) // 此时text作为属性传递给组件并渲染
render(vNode, div) // 将虚拟dom即message组件转为真实dom并渲染至body的最后
}
使用:
import Message from '路径'
将拦截器原本alert()报错提示改为Message({ type: "success", text: "成功" })等
Message.vue组件中接收type与text并动态渲染相应样式及内容
第三方登录:
思路:
1.注册资格
2.点击相应按钮,跳转至第三方登录页(第三方提供)
3.登陆后回到自身页面(该页面与第三方进行约定)
4.在约定页面进行判断(通过特定id):
1.是否之前登陆过,若登陆过直接跳转至首页
2.未登陆过但注册过账号,此时进行绑定,下次在登陆则为效果1
3.未登陆过也未注册过帐号,此时可选择注册,并会将当前第三方账号与当前注册账号绑定,之后效果同1
关键点(难点):在开发时未上线,无法直接访问第三方登录页,无法到达约定页面
解决方式:C:\Windows\System32\drivers\etc的hosts文件添加:127.0.0.1 约定url
原理:DNS域名解析过程中直接在本机将约定url路径解析为127.0.0.1,从而使其正常访问
省市区三项组件的封装:
关键点:首先获取到一个树状结构的省市县数组;创建两个数组接收,一个用于渲染,一个用于在修改完
成后恢复数据(恢复数据原因:使用v-show显示隐藏,故数据缓存),创建一个对象,获取每一次被点击的
name与level,初次(level=0)渲染最外层数据,并在点击其中指定项时获取到item的areaList且为对象的
省类赋值 ==> 当(level=1)重复操作 ==> 当(level=2)时子向父传值,将收集到的省市县名称与level全
部传递,便于父级传递数据渲染到子级,若在点击过程中点击了外部区域,则隐藏省市县区域并恢复数据
若父组件传递了地址,则用父组件的地址。
刚需:后台或其他位置得到一个所有省市县的数组
单选框的封装:
关键点:只有一项能被选中&每项的内容
1.只有一项能被选中
类似面包屑组件,创建一个大项,通过插槽装小项,而大项中的v-model则是获取被选中的
值,此时被选中的值唯一,小项中的数据通过v-for循环渲染出来,借助点击事件监听并传递当
前值(在大项中声明一个函数再将函数传递给小项,小项接收到被点击项的数据再调用大项中的
函数,并将数据作为参数传递给大项,大项用defineEmits传递给父组件,从使被选中项唯一)
2.每项的内容,都是通过默认插槽实现(在小项双标签的内容既是每项的内容)
踩过的坑
- V3中TS+pinia使用时:pinia的计算属性要先确定返回值类型,再return
- try、catch处理过的函数在被调用时,再次使用try、catch时即使出错了也不会走catch因为错误在之前已经被处理了
- function fn() {try { // 执行的操作 } catch(err) {}}
- function fn2() { try { fn() // 此时调用fn } catch(err) {} } // 若此时fn出错,也已经在fn中处理过了,所以fn2中不会捕捉到错误,故会往下执行。
- provide与inject在provide传递数据时,若非响应式的数据(非ref,reactive声明的变量)在inject中接收时均为常量,切无响应式特性,所以要想有动态切换效果,一定要使用到响应式
忘掉的点
- route获取路径时:path不携带参数;fullpath携带参数