Vue3 学习

  1. Proxy 简单的响应原理
// 其中的 Reflet 是ES6语法,具体语义用法参考: https://www.bookstack.cn/read/es6-3rd/docs-reflect.md

// reactive.js中
const reactive = target => {
   
    return new Proxy(target, {
   
        get(target, key, receiver) {
   
            const res = Reflect.get(target, key, receiver)
            console.log("数据收集")
            return res            
        },
        set(target, key, receiver) {
   
            const res = Reflect.set(target, key, receiver)
            console.log("数据更新")
            return res                    
        }            
    })
}
const obj = reactive({
   a: 10, b:20})
obj.a = 150
console.log(obj.a)

// 通过控制台执行 node reactive.js
// 控制台能依次打印出
// 数据更新
// 数据收集
// 150
  1. 异步组件 defineAsyncComponent
    组件需要的时候才调用, 可以解决首屏加载缓慢的问题.
    也可以解决A,B两组件循环引用的问题.
// import ComponentB from "./components/ComponentB.vue"
const ComponentB = defineAsyncComponent(() => {
   
	import("./components/ComponentB.vue")
})
  1. 封装倒计时函数
    编写一个函数 useCountDown 可以把秒数格式化为倒计时的显示状态
    formatTime为显示的倒计时时间 2. start是倒计时启动函数,调用时可以设置初始值并且开始倒计时
// composables / useCountDown.js中
// 封装倒计时逻辑函数
import {
    computed, onUnmounted, ref } 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就减1
        time.value = currentTime
        timer = setInterval(() => {
   
            time.value--
        }, 1000)
    }
    // 组件销毁时清除定时器
    onUnmounted(() => {
   
        timer && clearInterval(timer)
    })
    return {
   
        formatTime,
        start,
    }
}



// 组件中使用
import {
    useCountDown } from '@/composables/useCountDown'
const {
    formatTime, start } = useCountDown()
onMounted(() => {
   
    // 初始化倒计时秒数
    start(600) // 传一个600秒
})

<p>
    支付还剩 <span>{
   {
    formatTime }}</span>, 超时后将取消订单.
</p>
  1. 接口请求 token 设置, 401 token 失效处理
import axios from 'axios'
import {
    ElMessage } from 'element-plus'
import {
    useUserStore } from '@/store/userStore'
import router from '@/router'

// axios.create 创建基础实例
const httpInstance = axios.create({
   
    baseURL: 'http://pcapi-xiaotuxian-front-devtest.itheima.net', // 基地址
    timeout: 30000, // 超时时间,30秒
})

// 请求拦截器
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
    },
    (err) => {
   
        Promise.reject(err)
    }
)

// 响应拦截器
httpInstance.interceptors.response.use(
    (res) => {
   
        return res.data
    },
    (err) => {
   
        const userStore = useUserStore()
        // 统一错误提示
        ElMessage({
   
            type: 'warning',
            message: e.response.data.message,
        })
        
        // 401 token 失效处理: (1)清除本地用户数据. (2)跳转到登录页
        if (e.response.data.status == 401) {
   
            userStore.clearUserInfo()
            router.push('/login')
        }

        return Promise.reject(err)
    }
)

export default httpInstance
  1. Pinia数据持久化
需要做持久化的原因:  用户数据中有一个关键的数据叫做Token (用来标识当前用户是否登录),而Token持续一段时间才会过期,
Pinia的存储是基于内存的,刷新就丢失,为了保持登录状态就要做到刷新不丢失,需要配合持久化进行存储.

目的:保持token不丢失,保持登录状态

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

实现步骤:  
	(1) npm install pinia-plugin-persistedstate
	(2) pinia 注册插件
		// store/index.js中
		import {
    createPinia } from 'pinia'
		import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
		const pinia = createPinia()
		// 注册持久化插件
		pinia.use(piniaPluginPersistedstate)
		export default pinia

	(3) 在需要持久化的store中进行配置
		// 管理用户数据相关
		import {
    defineStore } from 'pinia'
		export const useUserStore = defineStore(
		    'user',
		    () => {
   
		       // ... 
		    },
		    {
   
		        persist: true,
		    }
		)
  1. 注册全局组件
// main.js中
// 引入全局组件插件
import {
    componentPlugin } from '@/components'
app.use(componentPlugin)



// components/index.js中
import Sku from './XtxSku/index.vue' // 不同组件单独放进各自的文件夹下
export const componentPlugin = {
   
  install (app) {
   
    // app.component('组件名字',组件配置对象)
    app.component('XtxSku', Sku)
  }
}
  1. 定制路由行为
    在不同路由切换的时候,可以自动滚动到页面的顶部,而不是停留在原先的位置
    配置: vue-router支持 scrollBehavior 配置项,可以指定路由切换时的滚动位置
import {
    createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
   
    history: createWebHistory(import.meta.env.BASE_URL),
    routes: [  ],
    // 路由滚动行为定制
    scrollBehavior() {
   
        return {
   
            top: 0,
        }
    },
})
export default router
  1. 列表无限加载功能
核心实现逻辑:使用elementPlus提供的 v-infinite-scroll 指令监听是否满足触底条件,
满足加载条件时让页数参数加一获取下一页数据,做新老数据拼接渲染

实现步骤:   
(1) 配置 v-infinite-scroll
(2) 页数加一获取下一页数据
(3) 老数据和新数据拼接
(4) 加载完毕结束监听

参考 element-plus 官网:  
https://element-plus.gitee.io/zh-CN/component/infinite-scroll.html
  1. 使用逻辑函数拆分业务,方便代码维护
    实现步骤:
    (1) 按照业务声明以 use 打头的逻辑函数
    (2) 把独立的业务逻辑封装到各个函数内部
    (3) 函数内部把组件中需要用到的数据或者方法return出去
    (4) 在组件中调用函数把数据或者方法组合回来使用

  2. 解决路由缓存问题

原因: 使用带有参数的路由时需要注意的是, 当用户从 /users/johnny 导航到 /users/jolyne 时, 相同的组件实例将被重复调用. 
因为两个路由都渲染同个组件, 比起销毁再创建,复用则显得更加高效.
不过,这也意味着组件的生命周期钩子不会被调用(这时就得用到导航钩子了).

解决思路: 
	方案一:  给 router-view 添加 key
	<!-- 添加key 破坏复用机制 强制销毁重建 -->
	<RouterView :key="$route.fullPath" /

你可能感兴趣的:(学习,vue.js,javascript)