vue3项目总结

1.Pinia优化重复请求

在项目中,吸顶组件和首页的头部内容是一样的,所以不用发送两次请求。

通过Pinia集中管理数据,再把数据给组件使用。

只需要把请求封装在一个store里,调用即可使用。

2.面板组件的封装

由于项目中的新鲜好物和人气推荐结构一样,内容不同,所以可以通过组件封装来实现两个组件。

核心:把不变的只写一次,把变化的抽象为组件参数(props/插槽)

- 主标题和副标题是纯文本,可以抽象成 prop 传入
- 主体内容是复杂的模版,抽象成 插槽 传入
纯展示类组件通用封装思路总结:
1. 搭建纯静态的部分,不管可变的部分
2. 抽象可变的部分为组件参数 :非复杂的模版抽象成props,复杂的结构模版抽象为插槽。

3. 图片懒加载

当用户进入首页的时候,底部的图片不加载出来,当进入视口区域了,才加载图片。

步骤:

1)使用自定义指令,自定义懒加载指令(v-img-lazy)

//定义全局指令
app.directive('img-lazy',{
    mounted(el,binding){
    //el:指令绑定的那个元素
    //binding:binding.value 指令等于号后面绑定的表达式的值  图片url
    //console.log(el,binding.value)
    }
})

2)使用VueUse中的useIntersectionObserver函数来判断是否进入视口区

//引入vueuse中的useIntersectionObserver函数
import {useIntersectionObserver} from '@vueuse/core'

app.directive('img-lazy',{
    mounted(el,binding){
    console.log(el,binding.value)
    useIntersectionObserver(
    el,
    ([{isIntersecting}])=>{
        // isIntersecting是一个布尔值
        console.log(isIntersecting)
        if(isIntersecting){
            //进入了视口区域,需要发送请求获取图片
            el.src=binding.value
           }
        }
      )
    }
})

3)在组件中使用懒加载指令

4)手动停止监听。

由于 useIntersectionObserver 这个函数会一直监听是否进入视口区域,需要我们手动停止。

使用 useIntersectionObserver 函数中的 stop方法 ,当图片第一次加载完毕后,停止监听。

 4.编写一个插件

在本项目中,把懒加载指令封装为了一个插件。

步骤:

1)在一个文件中定义插件

//定义懒加载插件
import { useIntersectionObserver } from '@vueuse/core'

export const lazyPlugin={
    install(app){
        //懒加载指令逻辑代码
    }
}

2)在main.js中注册懒加载指令

//引入懒加载指令插件
import {lazyPlugin} from '文件'


//注册懒加载插件
const app = createApp(App)
app.use(lazyPlugin)

5.一级分类的轮播图

一级分类轮播图和首页的轮播图一样,只是接口的参数不同。

所以,可以共用一个接口,只需要把封装的接口适配参数即可。

步骤:

1)封装轮播图的接口(一级分类和首页使用)

import httpInstance from "@/utils/http";

export function getBannerAPI(params={}){
    // 首页为1 一级分类为2
    // 默认是首页
    const {distributionSite='1'}=params
    return httpInstance({
        url:'/home/banner',
        params:{distributionSite}
    })
}

2)获取数据(首页轮播图是默认的,所以参数可以不写;一级分类参数{ distributionSite: "2" })

//首页轮播图
<script setup>
import {getBannerAPI} from '@/apis/home'
import {onMounted, ref} from 'vue'

const bannerList=ref([])

const getBanner=async()=>{
    const res=await getBannerAPI()
    console.log(res);
    bannerList.value=res.result
}
onMounted(()=>getBanner())
</script>

//一级分类轮播图
<script setup>
import {getBannerAPI} from '@/apis/home'
import {onMounted, ref} from 'vue'

const bannerList=ref([])

const getBanner=async()=>{
    const res=await getBannerAPI({ distributionSite: "2" })
    console.log(res);
    bannerList.value=res.result
}
onMounted(()=>getBanner())
</script>

3)渲染数据即可

6.路由缓存问题

当路由只有参数变化时,会复用组件,导致生命周期钩子无法执行,所以数据无法更新。

解决方法:

在意性能问题,选择onBeforeUpdate, 精细化控制。
不在意性能问题,选择key,简单粗暴。

7.使用逻辑函数拆分业务

基于逻辑函数拆分业务是指把同一个组件中独立的业务代码通过函数做封装处理,提升代码的可维护性。
实现步骤:
1. 按照业务声明以 `use` 打头 的逻辑函数
2. 把 独立的业务逻辑 封装到各个函数内部
3. 函数内部把组件中需要用到的数据或者方法 return出去
4. 在 组件中调用函数 把数据或者方法组合回来使用
例如本项目中的分类页
把一级分类轮播图的代码封装为一个函数,在组件中调用函数使用。

8.列表无限加载功能实现

使用elementPlus提供的 v-infinite-scroll 指令 监听是否满足触底条件。

满足加载条件时让页数参数加一,获取下一页数据,做新老数据拼接渲染。

//列表的无线加载功能
//结束监听
const disabled= ref (flase)

const load=async()=>{
    //获取下一页的数据
    reqData.value.page++
    const res=await getSubCategoryAPI(reqData.value)
    //新老数据拼接
    goodList.value=[...goodList.value,...res.result.items]
    //当数据加载完毕时,结束监听
    if(res.result.items.length==0){
        disabled.value=true
    }
}


//列表渲染
<div class="body" :v-infinte-scroll="load" :infinte-scroll-disabled="disabled">
    <!-- 商品列表-->
    
</div>

9. 对象多层属性访问

本项目中,详情页在渲染数据时,由于一开始的goods是空对象,对空对象进行读取,返回的结果是undefined,会报错。

解决方法:

方法一:可选链

方法二: 使用 v-if 手动来控制渲染的时机,保证只有数据存在时才渲染。

 10.放大镜效果

步骤:

获取鼠标在盒子中的相对位置--->控制滑块跟随鼠标移动--->实现大图效果--->鼠标移入移出控制显示隐藏

使用useMouseInElement来获取鼠标位置。

1)滑块跟随鼠标移动

获取鼠标相对位置------>控制滑块跟随移动

import {useMouseInElement} from '@vueuse/core'
 
// 1.获取鼠标相对位置
const target=ref(null)
// elementX:相对于盒子左侧的距离
// elementY:相对于盒子顶部的距离
// isOutside:判断盒子是否在盒子内
const {elementX,elementY,isOutside}=useMouseInElement(target)

//2.控制滑块(阴影框)跟随鼠标移动
//top---阴影框距离顶部的距离
//left---阴影框距离左侧的距离
const left= ref(0)
const top= ref(0)

//监听elementX/Y变化,一旦变化,重新设置left/top
watch([elementX,elementY,isOutside],()=>{
    //如果鼠标没有移入到盒子中,就不执行后续的操作
    if(isOutside.value) return 
    
    //如果进入盒子中
    //横向
    if(elementX.value>100 && elementX.value<300){
        left.value=elementX.value-100
    }
    //纵向
    if(elementY.value>100 && elementY.value<300){
        top.value=elementY.value-100
    }

    //处理边界
    if (elementX.value > 300) { left.value = 200 }
    if (elementX.value < 100) { left.value = 0 }
 
    if (elementY.value > 300) { top.value = 200 }
    if (elementY.value < 100) { top.value = 0 }

})

2)大图效果实现

大图的宽高是小图的2倍,移动方向和滑块的相反,并且数值是2倍。

 3)使用v-show和isOutside来控制大图的隐藏和显示

当鼠标移入,大图才显示。

11.使用三方组件

熟悉一个三方组件,首先重点看什么?

答:props和emit, props决定了当前组件接收什么数据,emit决定了会产出什么数据。

验证组件是否成功使用:

答:传入必要数据,是否交互功能正常 ------->点击选择规格,是否正常产出数据。

12.全局注册组件

在项目中的components目录下有可能还会有很多其他通用型组件,有可能在多个业务模块中共享,所有统一进行全局组件注册比较好。

步骤:

1)把components目录下的所有组件进行全局注册。

2)在main.js 中注册插件。

 首先在 componentsindex.js 中进行全局注册

// 把components中的所有组件都进行全局化注册
// 通过插件的方式
import ImageView from './ImageView/index.vue'
import Sku from './XtxSku/index.vue'
export const componentPlugin={
    install(app){
        // app.component('组件的名字',使用的组件)
        app.component('XtxImageView',ImageView)
        app.component('XtxSku',Sku)
    }
}

然后在 main.js 中注册插件

// 引入全局组件插件
import {componentPlugin} from '@/components'
// 注册全局组件
app.use(componentPlugin)

13.表单校验

ElementPlus表单组件内置了表单校验功能,只需要 按照组件要求配置必要参数 即可(直接看文档)

当功能很复杂时,通过多个组件各自负责某个小功能再组合成一个大功能是组件设计中的常用方法。

表单校验步骤:

1. 按照接口字段准备表单对象并绑定

2. 按照产品要求准备规则对象并绑定

3. 指定表单域的校验字段名

4. 把表单对象进行双向绑定

//表单校验(账号名+密码)
import {ref} from 'vue'
//1.准备表单对象
const from = ref({
    account:'',
    password:''
})

//2.准备规则对象
const rules={
  account: [{ required: true, message: "用户名不能为空", trigeer: "blur" }],
  password: [
    { required: true, message: "密码不能为空", trigeer: "blur" },
    { min: 6, max: 14, message: "密码长度为6-14个字符", trigeer: "blur" },
  ]
}

//3.渲染数据

 14. 自定义校验规则

ElementPlus表单组件内置了初始的校验配置,应付简单的校验只需要 通过配置 即可,如果想要定制一些 特殊的校验需求,可以使用自定义校验规则。
在本项目中,给是否同意协议绑定自定义校验。
如果勾选了协议框,通过校验,如果没有勾选,不通过校验。

 

 15. 整个表单的内容验证

在点击登录时,需要对所有需要校验的表单进行统一校验,防止用户还没有输入账号和密码,就直接点击登录。

步骤:

1. 获取form组件实例(通过ref获取)

2. 调用实例方法

 

import { loginAPI } from "@/apis/user";
import { ElMessage } from "element-plus";
import "element-plus/theme-chalk/el-message.css";
import { useRouter } from "vue-router";
// 3. 获取form实例做统一校验
const formRef = ref(null);
const router = useRouter();
const doLogin = () => {
  const { account, password } = form.value;
  // 调用实例方法
  formRef.value.validate(async (valid) => {
    // valid:所有表单都通过校验  才为true
    console.log(valid);
    // 以valid作为判断条件,如果通过校验才执行登录
    if (valid) {
      const res = await loginAPI({ account, password });
      console.log(res);
      //  1.提示用户
      ElMessage({ type: "success", message: "登陆成功" });
      // 2.跳转首页
      router.replace({ path: "/" });
    }
  });
};

当前登录成功后,提示用户并发生跳转。

使用Element组件的ElMessage来提示用户,但是在使用时,样式需要单独导入。

跳转首页:使用useRouter跳转。

16.Pinia用户数据持久化

由于Pinia的存储是基于内存的,刷新就丢失,而token在pinia中存储,所以token会丢失。

在本项目中,为了保持登录状态,就要做到刷新不丢失,需要配合持久化进行存储。

这里我们使用pinia持久化插件:piniaPluginPersistedstate

最终效果:操作state时会自动把用户数据在本地的localStorage也存一份,刷新的时候会从localStorage中先取。

步骤:

1)安装插件包

npm i pinia-plugin-persistedstate

2)pinia注册插件

//main.js
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia=createPinia()
// 注册持久化插件
pinia.use(piniaPluginPersistedstate)
app.use(pinia)

3)进行持久化的配置

添加  persist : true

17. 多模版适配

有几个需要适配的模版就准备几个template片段,通过 条件渲染 控制显示。

 在本项目中,登录页使用了多模板适配。

当登录时,显示登录的模块;未登录时,显示未登录的模块。

 

18.请求拦截器携带Token 


Token作为用户标识,在很多个接口中都需要携带Token 才可以正确获取数据,所以需要在接口调用时携带Token。

另外,为了统一控制,采取请求拦截器携带的方案。

如何配置:

Axios请求拦截器可以在接口正式发起之前对请求参数做一些事情,通常Token数据会被注入到 请求header 中,格式按照后端要求的格式进行拼接处理。

//src/utils/http.js
import { useUserStore } from '@/stores/user'
// axios请求拦截器
httpInstance.interceptors.request.use(config => {
  // 1. 从pinia获取token数据
  const userStore = useUserStore()
  // 2. 按照后端的要求拼接token数据
  const token = userStore.userInfo.token
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
}, e => Promise.reject(e))

19.封装倒计时函数

本项目中存储的倒计时是总共的秒数,我们需要进行格式化。

这里就需要使用 dayjs插件 。

编写一个函数 useCountDown 把秒数格式化为倒计时的显示状态。

函数样例:

1. formatTime为显示的倒计时时间

2. start是倒计时启动函数,调用时可以设置初始值并且开始倒计时

步骤:

1)安装dayjs插件:

npm i dayjs

2)封装倒计时函数

//src/composables/useCountDown.js
// 封装倒计时逻辑函数
import {ref,computed, onUnmounted} from 'vue'
import dayjs from 'dayjs'
export const useCountDown=()=>{
    // 1.响应式数据
    let timer=null
    const time=ref(0)
    // 格式化时间为  xx分xx秒
    const formatTime=computed(()=>dayjs.unix(time.value).format('mm分ss秒'))
    // 2.开启倒计时函数
    const start=(currentTime)=>{
        // 开始倒计时的逻辑
        // 核心逻辑的编写:每隔1s就减一
        time.value=currentTime
        timer=setInterval(()=>{
            time.value--
        },1000)
    }
    // 组件销毁时清除定时器
    onUnmounted(()=>{
        timer&&clearInterval(timer)
    })
    return {
        formatTime,
        start
    }
}

3)在组件中使用

//src/views/Pay/index.vue
<script setup>
....
const {formatTime,start}=useCountDown()
....
</script>
 
<template>
.....
<p>支付还剩 <span>{{ formatTime }}</span>, 超时后将取消订单</p>
.....
</template>

20.分页器

 

//src/views/Member/components/UserOrder.vue
<script setup>
// 补充总条数
const total = ref(0)
const getOrderList = async () => {
  const res = await getUserOrder(params.value)
  // 存入总条数
  total.value = res.result.counts
}
// 页数切换
const pageChange = (page) => {
  params.value.page = page
  getOrderList()
}
</script>
 
<template>
   <el-pagination 
     :total="total" 
     @current-change="pageChange" 
     :page-size="params.pageSize" 
     background
     layout="prev, pager, next" />
</template>

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当对Vue2项目进行重构为Vue3时,需要注意以下几点: 1. 路由语法不兼容:Vue2和Vue3的路由语法是不同的,直接复制Vue2的路由到Vue3项目中会导致报错。需要重新配置路由,按照Vue3的路由语法进行修改。 2. 子菜单问题:在Vue2中,子菜单可能是连着写的,但在Vue3中,需要在子菜单的名称之间加一个"-"才能正确显示下拉列表。所以在重构Vue3项目时,需要注意调整子菜单的命名格式。 3. 页面显示问题:在将Vue2项目迁移到Vue3项目时,可能会遇到页面不显示的情况。首先需要检查路由配置,确保路由设置正确。然后逐个检查各个页面的语法,确保没有错误。如果发现错误,需要及时进行修改,以使页面能够正常显示。 总结: 在重构Vue2项目Vue3时,需要重新配置路由语法,注意子菜单的命名格式,以及检查并修改页面的语法错误,以确保项目能够正常显示。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Vue2.0项目重构到Vue3.0流程](https://blog.csdn.net/Asrty/article/details/129049701)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值