D31 Vue2 + Vue3 K104-K123

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})
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值