D31 Vue2 + Vue3 K104-K123

news2024/9/22 9:23:58

D31.Vue

 F17.打包 + 图片懒加载(K104-K106)

  1.打包
   1)vue.config.js

module.exports = {
  //打包时不生成map文件(用来进行错误提示的文件,很占用空间)
  productionSourceMap: false,
  // 关闭ESLINT校验工具
  lintOnSave: false,
}

     pnpm run build
  2.图片懒加载
   1)安装vue-lazyload插件
     pnpm add vue-lazyload@1.3.3 --save-dev

pnpm add vue-lazyload@1.3.3 --save-dev

   2)在main.js中进行引用

import VueLazyload from "vue-lazyload";

//或者自定义配置插件
Vue.use(VueLazyload, {
preLoad: 1.3,
error: require('@/assets/image/error.png'),
loading: require('@/assets/image/loading.gif'),
attempt: 1
})

   3)各个自定义配置属性含义:
在这里插入图片描述
     使用(将图片设置为懒加载)

<img v-lazy="psdimg" class="psd" />

     注意: 当遇到是v-for循环的时候,或者用div包裹着img的时候,需要在div上面添加 v-lazy-container="{ selector: ‘img’ }"

<div v-lazy-container="{ selector: 'img' }">
  <img data-src="//aaa.com/img1.jpg">
  <img data-src="//aaa.com/img2.jpg">
  <img data-src="//aaa.com/img3.jpg">  
</div>
  
<!--或者这种:-->
 
 <div v-lazy-container="{ selector: 'img' }" v-html="content">
</div>

      如果是这种情况的话,一定要使用 data-src 来指定路径,而不使用v-lazy。因为如果使用的是v-lazy的话,拿到了图片地址也会一直显示不出来

 F18.动态组件 组件注册(K107-K110)

  1.动态组件
  A.基本使用
   1)composition api写法:只适用于vue3

  • 在 < script setup> 中,组件被引用为变量而不是作为 字符串键 来注册
  • 核心点 shallowRef()
  • 虽然ref()也能正常使用,但官方不推荐,会爆warn: "这可能会导致不必要的性能开销" (原因,组件非动态数据,不需要转为proxy)
  • :is 与 component > 设置动态组件的必要条件
  • :is 对应绑定的为字符串值即可,值对应引入的组件名
  • props 数据父传子
  • shallowRef > 声明 :is 绑定的值,值为 import 的组件名
  • <template>
      <component :is='dynamic' :datas='{data1,data2}'></component>
      <button @click='changeComponent'>更改组件</button>
    </template>
    <script setup lang="ts">
    import test1 from './test-components/test1.vue'
    import test2 from './test-components/test2.vue'
    import { ref,shallowRef } from 'vue'
    let dynamic:any = shallowRef(test1)
     
    let state = ref(true)
    function changeComponent() {
      if (state.value) {
        dynamic.value = test2
      }else{
        dynamic.value = test1
      }
      state.value = !state.value
    }
     
    let data1 = ref('')
    let data2 = ref({elPsyKongroo:'怀表'})
    </script>
    

          用 markRaw 也可以,但是不如shallwRef直观与方便
      B.官方案例
       1)官方案例

    <script setup>
    import Home from './Home.vue'
    import Posts from './Posts.vue'
    import Archive from './Archive.vue'
    import { ref } from 'vue'
     
    const currentTab = ref('Home')
    
    const tabs = {
      Home,
      Posts,
      Archive
    }
    </script>
    
    <template>
      <div class="demo">
        <button
           v-for="(tab, index) in tabs"
           :key="index"
           :class="['tab-button', { active: currentTab === index }]"
           @click="currentTab = index"
         >
          {{ tab }}
        </button>
    	  <component :is="tabs[currentTab]" class="tab"></component>
      </div>
    </template>
    <style>
    .tab-button.active {
      background: #e0e0e0;
    }
    </style>
    

    在这里插入图片描述

         上面的例子是通过 Vue 的 元素和特殊的 is attribute 实现的:

    <!-- currentTab 改变时组件也改变 -->
    <component :is="tabs[currentTab]"></component>
    

       2)在上面的例子中,被传给 :is 的值可以是以下几种:

  • 被注册的组件名
  • 导入的组件对象
  •      也可以使用 is attribute 来创建一般的 HTML 元素
          当使用 < component :is=“…”> 来在多个组件间作切换时,被切换掉的组件会被卸载。我们可以通过 `` 组件强制被切换掉的组件仍然保持“存活”的状态
      2.组件注册
      A.全局注册
       1)例如组件使用频率非常高(table,Input,button,等)这些组件 几乎每个页面都在使用便可以封装成全局组件
         我们可以使用 Vue 应用实例的 app.component() 方法,让组件在当前 Vue 应用中全局可用

    import { createApp } from 'vue'
    
    const app = createApp({})
    
    app.component(
      // 注册的名字
      'MyComponent',
      // 组件的实现
      {
        /* ... */
      }
    )
    

       2)如果使用单文件组件,你可以注册被导入的 .vue 文件:

    import MyComponent from './test.vue'
    
    app.component('MyComponent', MyComponent)
    

       3)app.component() 方法可以被链式调用:

    app
      .component('ComponentA', ComponentA)
      .component('ComponentB', ComponentB)
      .component('ComponentC', ComponentC)
    

       4)全局注册的组件可以在此应用的任意组件的模板中使用:

    <!-- 这在当前应用的任意组件中都可用 -->
    <ComponentA/>
    <ComponentB/>
    <ComponentC/>
    

         所有的子组件也可以使用全局注册的组件,这意味着这三个组件也都可以在彼此内部使用
      B.局部注册
       1)全局注册虽然很方便,但有以下几个问题:
        a. 全局注册,但并没有被使用的组件无法在生产打包时被自动移除 (也叫“tree-shaking”)。如果你全局注册了一个组件,即使它并没有被实际使用,它仍然会出现在打包后的 JS 文件中
        b. 全局注册在大型项目中使项目的依赖关系变得不那么明确。在父组件中使用子组件时,不太容易定位子组件的实现。和使用过多的全局变量一样,这可能会影响应用长期的可维护性
         相比之下,局部注册的组件需要在使用它的父组件中显式导入,并且只能在该父组件中使用。它的优点是使组件之间的依赖关系更加明确,并且对 tree-shaking 更加友好
         在使用

    <script setup>
    import ComponentA from './ComponentA.vue'
    </script>
    
    <template>
      <ComponentA />
    </template>
    

       2)如果没有使用 < script setup>,则需要使用 components 选项来显式注册:

    import ComponentA from './ComponentA.js'
    
    export default {
      components: {
        ComponentA
      },
      setup() {
        // ...
      }
    }
    

       3)对于每个 components 对象里的属性,它们的 key 名就是注册的组件名,而值就是相应组件的实现。上面的例子中使用的是 ES2015 的缩写语法,等价于:

    export default {
      components: {
        ComponentA: ComponentA
      }
      // ...
    }
    

         请注意:局部注册的组件在后代组件中并可用。在这个例子中,ComponentA 注册后仅在当前组件可用,而在任何的子组件或更深层的子组件中都不可用
      C.组件名格式
       1)在整个指引中,我们都使用 PascalCase 作为组件名的注册格式,这是因为:
        a. PascalCase 是合法的 JavaScript 标识符。这使得在 JavaScript 中导入和注册组件都很容易,同时 IDE 也能提供较好的自动补全
        b. < PascalCase /> 在模板中更明显地表明了这是一个 Vue 组件,而不是原生 HTML 元素。同时也能够将 Vue 组件和自定义元素 (web components) 区分开来
         在单文件组件和内联字符串模板中,我们都推荐这样做。但是,PascalCase 的标签名在 DOM 模板中是不可用的
          为了方便,Vue 支持将模板中使用 kebab-case 的标签解析为使用 PascalCase 注册的组件。这意味着一个以 MyComponent 为名注册的组件,在模板中可以通过 < MyComponent> 或 < my-component> 引用。这让我们能够使用同样的 JavaScript 组件注册代码来配合不同来源的模板

     F19.TS类型声明 + keepAlive(K110-K116)

      1.Ts 类型声明使用 & 讲解
         核心:大部分都是运用API函数泛型来定义类型
      A.父组件相关的

    <template>
      <el-button @click="onsub">测试</el-button>
      <input type="text" @change="handleChange" />
      <child ref='childRef' :child='2' :strData='"1"' :arrFor="[]" @elPsyKongroo='onsub'></child>
    </template>
    <script lang='ts' setup>
    import child from './child.vue'
    import { ref,Ref,reactive,computed,customRef,watch,provide } from "vue";
     
    //> ref
    // interface Ref<T> {
    //   value: T
    // }
    // function ref<T>(value: T): Ref<T>
    const year = ref<string | number>('2020')
    // 如果泛型的类型未知,则建议将 ref 转换为 Ref<T>:
    function useState<State extends string>(initial: State) {
      const state = ref(initial) as Ref<State> // state.value -> State extends string
      return state
    }
     
    //> reactive
    interface Book {
      title: string
      year?: number
    }
    const book = reactive<Book>({title:'唉,真有氏的怀表怎么停了!'})
    // function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
     
    //> customRef
    // function customRef<T>(factory: CustomRefFactory<T>): Ref<T>
    // type CustomRefFactory<T> = (
    //   track: () => void,
    //   trigger: () => void
    // ) => {
    //   get: () => T
    //   set: (value: T) => void
    // }
    function useDebouncedRef(value:string, delay = 200) {
      return customRef<string>((track, trigger) => {
        return {
          get() {
            return value
          },
          set(newValue) {
            value = newValue
          }
        }
      })
    }
    let a23 = useDebouncedRef('heelo1')
    a23.value = '123'
     
     
    //> provide 与 inject   // inject示例在同级child.vue里
    // interface InjectionKey<T> extends Symbol {}
    // function provide<T>(key: InjectionKey<T> | string, value: T): void
    provide('static',year)
    provide('pbook',book)
    provide('changeFn',onsub)
    //! 有时候可能需要在子组件修改响应式的数据,此时provide一个方法给子组件调用
     
     
     
    //> computed
    let count = ref(0)
    const doubleCount = computed<number>(() => count.value + 2)
     
    //> watch
    watch<number>(count,()=>{})
    // watch<Ref<number>>(count,()=>{}) // 也可以
    interface ReactiveData2{
      content2: {
        count2: number
      }
    }
    let refData = ref(1)
    let reactiveData = reactive({content:{count:110}})
    let reactiveData2 = reactive<ReactiveData2>({content2:{count2:1}})
    watch<[Ref<number>,() => number,ReactiveData2]>([refData, ()=>reactiveData.content.count,reactiveData2], ([a,b,c], oldValue) => {
      console.log(a,b,c, oldValue)
    })
     
     
    // defineExpose 暴露的内容
    // let childRef = ref()
    // setTimeout(() => {
    //   console.log(childRef.value.ex1); // 如果是子组件的ref对象数据,会自动解包 .value
    // }, 1000);
     
    function handleChange(el) {
      console.log((el.target as HTMLInputElement).value)
      console.log(el.target.value)
    }
    function onsub(val) {
      console.log(val);
      year.value = 2036
      book.title = '掌管未来女神的作战计划 El psy kongroo'
    }
    </script>
    

      B.子组件相关的

    <template>
      <div class="studyContent">
        <div>{{a}}</div>
        <div>{{a2}}</div>
        <div>{{pbook.title}}</div>
      </div>
    </template>
    <script lang='ts' setup>
    import { inject,ref,Ref } from "vue";
     
    //! defineProps 或 defineEmits 只能是要么使用`运行时声明`,要么使用`类型声明`。同时使用两种声明方式会导致编译报错。
     
    //> defineProps
    // 仅限类型的 defineProps 声明的不足之处在于,它没有可以给 props 提供默认值的方式。为了解决这个问题,提供了 withDefaults 编译器宏:
    //? 运行时声明 的方式只能限制参数类型,无法限制是否必传、和默认值
    // const props = defineProps({
    //   child: String,
    //   sda: String, //undefined
    //   strData: String,
    //   arrFor: Array
    // })
     
    //? 类型声明 的方式1:能限制是否必传 > defineProps 单独使用该api
    // interface arrfor {
    //   name:string,
    //   children?:Array<arrfor>
    // }
    // const props = defineProps<{
    //   child?: string|number,
    //   sda?: string, //undefined
    //   strData?: string,
    //   arrFor: []
    // }>();
    // console.log(props);
     
    //? 类型声明 的方式2:能限制是否必传,以及默认值
    interface Props {
      child: string|number,
      sda?: string, // 未设置默认值,为 undefined
      strData: string,
      msg?: string
      labels?: string[],
      obj?:{a:number}
    }
    const props = withDefaults(defineProps<Props>(), {
      msg: 'hello',
      labels: () => ['one', 'two'],
      obj: () => { return {a:2} }
    })
    console.log(props,props.msg);
     
     
    //> defineEmits
    // // 等效this.$emit('eventName','data')
    // const emits = defineEmits(['update', 'delete'])
    // emits('delete','测试')
     
    // emits的类型声明写法,()的e id只是形参名字,不影响其他。
    const emit = defineEmits<{
      (e: 'elPsyKongroo', id: number): void
      (e: 'update', value: string): void
    }>()
    setTimeout(() => {
      // emit('elPsyKongroo', 2)
    }, 1000*2);
     
    //> defineExpose
    interface exFace {
      ex1:Ref<string>,
      ex2?:number
    }
    let ex1 = ref('1')
    let exObj:exFace = {
      ex1,
    }
    // 源码类型: const defineExpose: (exposed?: Record<string, any>) => void
    defineExpose(exObj)
     
     
    //> inject
    // interface InjectionKey<T> extends Symbol {}
    // // 没有默认值
    // function inject<T>(key: InjectionKey<T> | string): T | undefined
    // // 有默认值
    // function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T
     
    // // 有工厂函数
    // function inject<T>(
    //   key: InjectionKey<T> | string,
    //   defaultValue: () => T,
    //   treatDefaultAsFactory: true
    // ): T
    let defaultFn = inject('ab12',()=>'雏见泽'+'棉流',true) 
    console.log(defaultFn);
     
     
    interface Book {
      title: string
      year?: number
    }
    let a = inject<Ref>('static') // 无默认值
      //! 即使在子组件可以直接修改,但最好不要这么做,将会影响到provide的父组件以及其他所有inject的子组件。
      //! 这会导致 溯源 非常麻烦,所以修改方式统一为在父组件provide一个方法,子组件调用进行修改!
      // a.value = '直接作死修改'
    let pbook = inject<Book>('pbook') // 无默认值
    let changeFn:(val:string)=>void = inject('changeFn') // 无默认值
    let a2 = inject('static2','????') // 有默认值
    let a3 = inject('static3') // 无默认值且未找到则为 undefined
    let globalGuide = inject('guide') // 访问全局的
     
    setTimeout(() => {
      changeFn('injectFn传参')
    }, 5000);
    </script>
    

      2.keep-alive
      A.基本介绍
       1)有时候我们不希望组件被重新渲染影响使用体验;或者处于性能考虑,避免多次重复渲染降低性能。而是希望组件可以缓存下来,维持当前的状态。这时候就需要用到keep-alive组件
         开启keep-alive 生命周期的变化

  • 初次进入时: onMounted> onActivated
  • 初次进入时: onMounted> onActivated
  • 再次进入:
  • 只会触发 onActivated
  • 事件挂载的方法等,只执行一次的放在 onMounted中;组件每次进去执行的方法放在 onActivated中
  •  <keep-alive :include="" :exclude="" :max=""></keep-alive>
    

       2)Props:

  • include - string | RegExp | Array。只有名称匹配的组件会被缓存
  • exclude - string | RegExp | Array。任何名称匹配的组件都不会被缓存
  • max - number | string。最多可以缓存多少组件实例
  •    3)用法:
         < keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 相似, 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中
       4)主要用于保留组件状态或避免重新渲染
         注意事项:
        a. 优先级: 如果 exclude 和 include 内都有同个组件名, exclude 优先级高于 include
        b. 缓存销毁: max 当已缓存的组件数量超过 max 值,在新实例被创建之前,已缓存组件中最久没有被访问的实例会被销毁掉
        c. 生命周期: 被缓存的组件才会调用 activated 这些缓存相关的生命周期 ,换句话说就是 exclude 内的组件不会触发 activated!!
        d. 离开组件、再度激活: 当组件在 < keep-alive> 内被切换时,它的 mounted 和 unmounted 生命周期钩子不会被调用,取而代之的是 activated 和 deactivated。(这会运用在 的直接子节点及其所有子孙节点。)
      B.使用案例

    <template>
      <keep-alive :exclude="['test1']" :include="['test1','test2']" max='1'>
        <component :is='dynamic'></component>
      </keep-alive>
      <el-button @click='changeComponent'>更改组件</el-button>
    </template>
    <script setup lang="ts">
    import test1 from './test-components/test1.vue'
    import test2 from './test-components/test2.vue'
    import { ref,shallowRef } from 'vue'
    let dynamic:any = shallowRef(test1)
    let state = ref(true)
    function changeComponent() {
      if (state.value) {
        dynamic.value = test2
      }else{
        dynamic.value = test1
      }
      state.value = !state.value
    }
    </script>
    

       1)include 和 exclude
         include 和 exclude prop 允许组件有条件地缓存。二者都可以用逗号分隔字符串、正则表达式或一个数组来表示:

    <!-- 逗号分隔字符串 -->
    <keep-alive include="a,b">
      <component :is="view"></component>
    </keep-alive>
     
    <!-- regex (使用 `v-bind`) -->
    <keep-alive :include="/a|b/">
      <component :is="view"></component>
    </keep-alive>
     
    <!-- Array (使用 `v-bind`) -->
    <keep-alive :include="['a', 'b']">
      <component :is="view"></component>
    </keep-alive>
    

       2)使用了 include / exclude 后,必须显式声明组件的 name !!! 这样才能与缓存组件匹配、生效
          在 3.2.34 或以上的版本中, 使用

         注意,< keep-alive> 是用在其一个直属的子组件被切换的情形。如果你在其中有 v-for 则不会工作。如果有上述的多个条件性的子元素,< keep-alive> 要求同时只有一个子元素被渲染
      C.缓存实例的生命周期
       1)当一个组件实例从 DOM 上移除但因为被 < KeepAlive> 缓存而仍作为组件树的一部分时,它将变为不活跃状态而不是被卸载。当一个组件实例作为缓存树的一部分插入到 DOM 中时,它将重新被激活
         一个持续存在的组件可以通过 onActivated() 和 onDeactivated() 注册相应的两个状态的生命周期钩子:

    <script setup>
    import { onActivated, onDeactivated } from 'vue'
    
    onActivated(() => {
      // 调用时机为首次挂载
      // 以及每次从缓存中被重新插入时
    })
    
    onDeactivated(() => {
      // 在从 DOM 上移除、进入缓存
      // 以及组件卸载时调用
    })
    </script>
    

        注:
          onActivated 在组件挂载时也会调用,并且 onDeactivated 在组件卸载时也会调用
          这两个钩子不仅适用于 < KeepAlive> 缓存的根组件,也适用于缓存树中的后代组件

     F20.Router 4(K117-K123)

         使用Vue3 安装对应的router4版本

    npm install vue-router@4
    

      1.路由配置
       1)与之前版本区别:
        a. 由 createRouter() 替换 new Router()
        b. 路由模式由 createWebHistory() 替换 mode: ‘history’
        c. main.js中由 .use(router) 替换 new Vue({ router })
       2)以前版本写法:

    // router/index.js
    import Vue from 'vue';
    import Router from 'vue-router';
    import routes from './routes'
    
    Vue.use(Router);
    
    const router = new Router({  // 区别1
    	mode: 'history',  // 区别2
    	routes
    });
    
    export default router;
    
    
    // main.js
    import Vue from 'vue'
    import router from './router'
    // ...
    new Vue({
    	el: '#app',
    	router,   // 区别3
    	components: { App },
    	template: '<App/>'
    })
    

       3)vue-router 4.x 版本写法:

    // router/index.js
    import { createRouter, createWebHistory } from 'vue-router';
    import routes from './routes'
    
    const router = createRouter({ // 区别1
    	history: createWebHistory(process.env.BASE_URL),  // 区别2
    	routes 
    })
    
    export default router 
    
    
    // main.js
    import { createApp } from 'vue'
    import App from './App.vue'
    import router from './router'
    
    const app = createApp(App);
    app.use(router).mount('#app');  // 区别3
    

       4)路由模式区别:
    在这里插入图片描述

         在src目录下面新建router 文件 然后在router 文件夹下面新建 index.ts

    //引入路由对象
    import { createRouter, createWebHistory, createWebHashHistory, createMemoryHistory, RouteRecordRaw } from 'vue-router'
     
    //vue2 mode history vue3 createWebHistory
    //vue2 mode  hash  vue3  createWebHashHistory
    //vue2 mode abstact vue3  createMemoryHistory
     
    //路由数组的类型 RouteRecordRaw
    // 定义一些路由
    // 每个路由都需要映射到一个组件。
    const routes: Array<RouteRecordRaw> = [{
        path: '/',
        component: () => import('../components/a.vue')
    },{
        path: '/register',
        component: () => import('../components/b.vue')
    }]
     
     
     
    const router = createRouter({
        history: createWebHistory(),
        routes
    })
     
    //导出router
    export default router
    

         最后在main.ts 挂载

    import { createApp } from 'vue'
    import App from './App.vue'
    import router from './router'
    createApp(App).use(router).mount('#app')
    

      2.路由跳转及参数
      A.router: 是VueRouter的一个全局对象,通过Vue.use(VueRouter)和VueRouter构造函数得到的一个实例对象,包含了所有路由包含了许多关键的对象和属性
      B.route: 是一个跳转路由的局部对象,每个路由都会有一个route对象,可以获取对应的name、path、params、query等
       1)以上在vue2.x与vue3.x中是一致的,要注意区分
       2)在vue3.x setup中 , useRouter、useRoute通常用来:
        useRouter:进行路由跳转
        useRoute:获取路由参数

    <script setup>
    	import { useRoute, useRouter } from 'vue-router'
    	const router = useRouter();
    	const route = useRoute();
    
    	console.log(route); // 获取路由参数
    	router.push('/logo'); // 进行路由跳转
    </script>
    

      C.vue-router 3.x中 获取路由参数:
          在组件中: {{ $ route.query.color}} 或 {{ $ route.params.color}}
          在 JS 中: this. $ route.query.color 或 this.$route.params.color
      3.路由(导航)守卫
         路由守卫简单来讲就是监听页面进入,修改,和离开的功能
       1)每个守卫接受三个参数:
          to:即将要进入的路由对象
          from:当前导航正要离开的路由
          next:一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数
       2)关于 next :
        a. next():进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)
        b. next(false):中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址
        c. next(‘/’) 或 next({ path: ‘/’ }):跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航
        d. next(error):(2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
       3)vue-router 3.x中:

    <script type="text/ecmascript-6">
    	export default {
    	
    			beforeRouteEnter(to, from, next){
    			    // 在渲染该组件的对应路由被confirm前调用
    			    // 此时组件实例还没被创建,因此不能获取`this`
    			}
    			
    			beforeRouteUpdate(to, from, next){
    			    // 在当前路由改变,但该组件被复用时调用
    			    // 举例:对于一个带有动态参数的路径`/item/:id`,在`/item/1`和`/item/2`之间跳转的时候,
    			    // 由于会渲染相同的`Item`组件,因此组件实例会被复用,从而触发这个钩子
    			    // 此时可以获取到`this`
    			}
    			
    			beforeRouteLeave(to, from, next){
    			    // 导航离开该组件的对应路由时调用
    			    // 时可以获取到`this`
    			}
    			
    	    }
    	};
    </script>
    

       4)vue-router 4.x中:
    在这里插入图片描述

         在setup中,由于路由已经发生了,因此在setup内部设置beforeRouteEnter没有任何意义,因此并无onBeforeRouteEnter

    import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
    import { ref } from 'vue'
    
    export default {
      setup() {
        // 与 beforeRouteLeave 相同,无法访问 `this`
        onBeforeRouteLeave((to, from) => {
          const answer = window.confirm(
            'Do you really want to leave? you have unsaved changes!'
          )
          // 取消导航并停留在同一页面上
          if (!answer) return false
        })
    
        const userData = ref()
    
        // 与 beforeRouteUpdate 相同,无法访问 `this`
        onBeforeRouteUpdate(async (to, from) => {
          //仅当 id 更改时才获取用户,例如仅 query 或 hash 值已更改
          if (to.params.id !== from.params.id) {
            userData.value = await fetchUser(to.params.id)
          }
        })
      },
    }
    

      4.动态路由
      A.动态路由参数 id:

    <!-- 导航页 -->
    <template>
        <router-link :to="'/news/' + newsId">新闻详情</router-link>
    </template>
    
    <script setup>
    import { ref } from 'vue';
    const newsId = ref('001');
    </script>
    

      B.获取路由参数:

    <!-- 新闻详情页 -->
    <template>
      <div id="news">
          <p>id:{{$route.params.id}}</p>
          <p>{{newsId}}</p>
      </div>
    </template>
    
    
    <script setup>
    import {useRoute} from 'vue-router';
    import {computed} from 'vue';
    const route=useRoute();
    
    const newsId=computed(()=>{
       return route.params.id
    })
    </script>
    

      5.keep-alive
      A.可利用keep-alive的 include 或 exclude 属性(include 和 exclude 包含的name 是组件的name不是router name)来设置缓存:
       1)include 值为字符串或者正则表达式匹配的组件name会被缓存
       2)exclude 值为字符串或正则表达式匹配的组件name不会被缓存
      B.vue2.x写法:

    <keep-alive exclude="Home">  // 缓存除了Home外的所有组件
       <router-view></router-view>
    </keep-alive>
    

      C.vue3.x写法:
       1)将内容传递给路由组件的 < slot>
         之前你可以直接传递一个模板,通过嵌套在 组件下,由路由组件的 来渲染:

    <router-view>
      <p>In Vue Router 3, I render inside the route component</p>
    </router-view>
    

         由于 < router-view> 引入了 v-slot API,你必须使用 v-slot API 将其传递给 < component>:

    <router-view v-slot="{ Component }">
      <component :is="Component">
        <p>In Vue Router 3, I render inside the route component</p>
      </component>
    </router-view>
    
    <template>
    	<router-view v-slot="{ Component }">
    	    <keep-alive :include="includeList">
    	        <component :is="Component" />
    	    </keep-alive>
    	</router-view>
    </template>
    
    <script setup>
    import { ref } from 'vue';
    
    // 需要缓存的组件name值
    const includeList = ref(['About', 'User']); // 缓存About和User组件
    </script>
    

         也可以在router.js中添加meta属性动态判断:

    meta: { title: '缓存页面', keepAlive: true }
    
    import { watch } from 'vue'
    import {useRouter} from 'vue-router'
    
    const router =useRouter()
    const includeList = [];
    
    watch(() => router.currentRoute.value,(val) => {
      // 只要设置了keepAlive属性的,全部加入缓存列表中
      if (val.meta.keepAlive && keepAliveList.indexOf(val.name) === -1) {
        includeList.push(val.name);
      }
    },{immediate: true,deep: true})
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/341681.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学完Scrapy-Splash秒变爬虫大佬

在做爬虫的时候&#xff0c;大多数的网页中会存在数据动态加载的部分&#xff0c;而且多数都是后期渲染上的。正常情况下爬虫程序仅能爬取被渲染过的数据。因此我们看到的数据也许并非是爬虫直接获取来的。 而scrapy-splash担任了一个中间人的角色&#xff0c;程序通过splash服…

吴思进——复杂美创始人首席执行官

杭州复杂美科技有限公司创始人兼CEO, 本科毕业于浙江大学机械专业&#xff0c;辅修过多门管理课程&#xff1b;1997年获经济学硕士学位&#xff0c;有关对冲基金的毕业论文被评为优秀&#xff1b;2008年创办杭州复杂美科技有限公司。 吴思进 中国电子学会区块链委员会专家&…

计算机网络-基本概念

目录 计算机网络-基本概念 互联网 Java的跨平台原理 ​编辑 C\C的跨平台原理 解释性语言的跨平台原理(python,js等) 客户端 vs 服务器 什么是协议&#xff1f; 网络互连模型 请求过程 计算机之间的通信基础 计算机之间的连接方式-网线直连(需要用交叉线&#xff0c;而…

GIS数据经纬度投影坐标转换总结(涵盖几乎全行业的坐标转换方法)

在处理GIS数据的过程中,避免不了要与坐标和坐标系打交道。这篇文章对能够进行地理坐标转换的所有软件框架做一个一次性总结。 软件类: 1.arcgis arcgis能够进行很全面的很方便的坐标处理,无论是经纬度坐标转投影坐标还是投影坐标转经纬度坐标都非常的简单。arcgis能够对导…

Qt编写视频监控系统70-0SD标签和图形信息(支持写入到文件)

一、前言 作为一个完整的视频监控系统&#xff0c;用户还需要自定义一些OSD标签信息显示在对应通道上面&#xff0c;而且不止一个OSD标签信息&#xff0c;位置可以在四个角或者指定坐标显示。最开始本系统设计的时候&#xff0c;由于本人擅长的是painter绘制&#xff0c;所以直…

MySQL视图特性

文章目录MySQL视图特性基本使用准备测试表创建视图修改视图影响基表修改基表影响视图删除视图视图规则和限制MySQL视图特性 视图的概念 视图是一个虚拟表&#xff0c;其内容由查询定义&#xff0c;同真实的表一样&#xff0c;视图包含一系列带有名称的列和行数据。视图中的数据…

opencv读取摄像头和视频数据

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a; lqj_本人的博客_CSDN博客-微信小程序,前端,python领域博主lqj_本人擅长微信小程序,前端,python,等方面的知识https://blog.csdn.net/lbcyllqj?spm1011.2415.3001.5343哔哩哔哩欢迎关注…

TensorRT和DLA(Deep Learning Accelerator)

TensorRT和DLA(Deep Learning Accelerator) 点击此处加入NVIDIA开发者计划 文章目录TensorRT和DLA(Deep Learning Accelerator)12.1. Running On DLA During TensorRT Inference注意&#xff1a;对于任何张量&#xff0c;索引维度的总体积加上请求的批量大小不得超过此函数返回…

Docker 面试知识点

Docker 是什么&#xff1f; 是实现容器技术的一种工具是一个开源的应用容器引擎使用 C/S 架构模式&#xff0c;通过远程API 来管理 (我们本机是 C&#xff0c;docker 引擎是 S,实际的构建过程是在 docker 引擎下完成的)可以打包一个应用及依赖包到一个轻量级、可移植的容器中 …

Hbase 数据迁移

Hbase 数据迁移 可选方案对比 l 已验证方案操作说明&#xff1a; n Export&import u 导出命令及示例 hbase org.apache.hadoop.hbase.mapreduce.Export “表名” 文件路径 导出至本地文件系统&#xff1a; ./bin/hbase org.apache.hadoop.hbase.mapreduce.Export ‘defa…

UnityBurst系统批量计算的插件入门

什麽是Burst系統做一些批量計算需要&#xff0c;比较难用&#xff0c;针无两头利如果不需要“密集”计算&#xff0c;就帧的不需要&#xff0c;到底什么是密集计算呢&#xff0c;for循环不密集么&#xff0c;while循环不密集么&#xff1f;安装Burst因为发现一个不错项目的插件…

Zookeeper实现分布式锁

文章目录ZK节点类型watch监听机制Zookeeper实现分布式锁锁原理创建锁的过程释放锁的过程ZK锁的种类代码实现Zookeeper是一个开源的分布式协调服务&#xff0c;是一个典型的分布式数据一致性解决方案。 分布式应用程序可以基于Zookeeper实现诸如数据发布/订阅&#xff0c;负载均…

【C/C++基础练习题】简单指针与数组使用练习题

&#x1f349;内容专栏&#xff1a;【C/C要打好基础啊】 &#x1f349;本文内容&#xff1a;简单指针与数组练习题&#xff08;复习之前写过的实验报告&#xff09; &#x1f349;本文作者&#xff1a;Melon西西 &#x1f349;发布时间 &#xff1a;2023.2.12 目录 1.vector​编…

async thunk 解决 API 调用的依赖问题

async thunk 解决 API 调用的依赖问题 一句话节省看下面一堆内容的时间就是&#xff1a; async thunk 中可以使用 async/await 锁住其他的 action 操作 一般 API 之间存在三种情况&#xff1a; A 和 B 之间没有依赖关系 这样的情况下&#xff0c;A 和 B 可以各调用各的&#x…

最近大热的 chatGPT 会取代你的工作吗?

ChatGPT 由于其高效的自然语言处理能力&#xff0c;它最容易取代的领域可能是&#xff1a; 文本分类&#xff1a;ChatGPT 可以用作文本分类系统&#xff0c;对文本进行分类 聊天机器人&#xff1a;ChatGPT 可以制作聊天机器人&#xff0c;提供人性化的交互体验 文本生成&…

策略游戏与实践反馈

早上看到time&#xff08;李培楠&#xff09;居然击败maru得了IEM的冠军&#xff0c;加上即时战略游戏的没落&#xff0c;星际2的研发停止&#xff0c;以及最近曾经被大家膜拜的暴雪闹出的各种事情&#xff0c;各种百感交集吧&#xff0c;从2000年上手星际争霸1开始&#xff0c…

【SPSS】数据预处理基础教程(附案例实战)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

2.10、时间片轮转、优先级调度算法、多级反馈队列调度算法

Tips&#xff1a;各种调度算法的学习思路 算法思想 算法规则 这种调度算法是用于作业调度还是进程调度? 抢占式? 非抢占式? 优点和缺点 是否会导致饥饿\color{red}饥饿饥饿 某 进程/作业 长期得不到服务 1、时间片轮转&#xff08;RR, Round-Robin&#xff09; 1.1、例…

别具一格,原创唯美浪漫情人节表白专辑,(复制就可用)(html5,css3,svg)表白爱心代码(2)

别具一格&#xff0c;原创唯美浪漫情人节表白专辑&#xff0c;(复制就可用)&#xff08;html5,css3,svg)表白爱心代码(2) 目录 ​​​​​​​款式二&#xff1a;心形实时显示认识多长时间桃花飞舞&#xff08;爱心&#xff09;款 1、拷贝完整源代码 2、拷贝完整js代码 3、…

漏洞修复 Zookeeper、MySQL、Elasticsearch

漏洞修复 一、HTTP漏洞修复 1.1 漏洞说明 1.2 漏洞修复 1.2.1 升级HTTPD到最新版本&#xff08;2.4.53&#xff09; 1.2.1.1 服务器有网的情况下执行以下操作&#xff1a; 安装CodeIT库 cd /etc/yum.repos.d wget https://repo.codeit.guru/codeit.el7.repo 更新httpd y…