Vue3.0整理

Vue学习笔记

Vue生命周期执行顺序以及时机

// 当前组件层级

<Parent>

    <Child></Child>

</Parent>



// 初始化阶段

parent beforeCreate

parent created

parent beforeMount

child beforeCreate

child created

child beforeMount

child mounted

parent mounted



// parent组件更新阶段,同时影响子组件变化

parent beforeUpdate

child beforeUpdate

child updated

parent updated



// child组件自身改变,以及parent组件自身变化没有影响到child组件时自身组件beforeUpdate updated方法会执行

// 和react中不同的是,parent组件更新时,child组件没有经过pure处理子组件也会进行更新

列表渲染注意事项 v-for

使用v-for时,为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一的 key

Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:

  • push()

  • pop()

  • shift()

  • unshift()

  • splice()

  • sort()

  • reverse()

对于不修改原数组的API如:filter()concat()slice()可以用新数组替换旧数组。

事件处理

  1. 简易事件触发
<button @click="counter += 1">Add 1</button>



data() {

    return {

      counter: 0

    }

}
  1. 调用method中事件,不含参数
<button @click="click">click</button>



// 默认带有当前点击的click事件

 click(event: Event) {

    console.log(event);

 },
  1. 调用method中事件,含参数
<button @click="click('test', $emit)">click</button>



// 默认带有当前点击的click事件

 click(param: string, event: Event) {

    console.log(string);

    console.log(event);

 },
  1. 多事件处理
<button @click="click1(1, $event), click2($event)">

  Submit

</button>



 click1(param: number, event: Event) {

    console.log(string);

    console.log(event);

 },

 

 click2(event: Event){

    console.log(event);

 }
  1. 子组件调用父组件方法
// index.vue

<demo @click-test="ClickTest"></demo>



ClickTest(payload: { param: string }) {

   console.log(payload. param);

},



// demo.vue

<button @click="click"> ClickTest</button>

 click() {

   this.$emit("ClickTest", {

      param: 'demo' 

   });

 }

Vue自定义事件,在事件触发之前可以验证事件有效性

自定义事件

动态组件 & 异步组件

动态组件、keep-alive

// 动态组件

<component :is="currentTabComponent"></component>



// 结合keep-alive的动态组件

<keep-alive>

  <component :is="currentTabComponent"></component>

</keep-alive>

设置 keep-alive 的组件,会增加两个生命钩子(activated / deactivated)。

首次进入组件:beforeCreate -> created -> beforeMount -> mounted -> activated 离开组件触发

deactivated,因为组件缓存不销毁,所以不会触发 beforeDestroy 和 destroyed 生命钩子。再次进入组件后直接从 activated 生命钩子开始。

keep-alive参数:

  • include - string | RegExp | Array。字符串或正则表达式,名称匹配的组件会被缓存。

  • exclude - string | RegExp | Array。字符串或正则表达式,名称匹配的组件不会被缓存。

  • max - number | string。最多可以缓存多少组件实例。

异步组件

可以实现组件的按需加载,结合webpack的对组件进行分包,理由浏览器对文件并行加载。

import { defineAsyncComponent } from 'vue'



const AsyncComp = defineAsyncComponent(() =>

  import('./components/Demo.vue')

)

同时他还提供相比React.lazy和Suspense更全的用法:

import { defineAsyncComponent } from 'vue'



const AsyncComp = defineAsyncComponent({

  // 工厂函数

  loader: () => import('./Foo.vue'),

  // 加载异步组件时要使用的组件

  loadingComponent: LoadingComponent,

  // 加载失败时要使用的组件

  errorComponent: ErrorComponent,

  // 在显示 loadingComponent 之前的延迟 | 默认值:200(单位 ms)

  delay: 200,

  // 如果提供了 timeout ,并且加载组件的时间超过了设定值,将显示错误组件

  // 默认值:Infinity(即永不超时,单位 ms)

  timeout: 3000,

  // 定义组件是否可挂起 | 默认值:true

  suspensible: false,

  /**

   *

   * @param {*} error 错误信息对象

   * @param {*} retry 一个函数,用于指示当 promise 加载器 reject 时,加载器是否应该重试

   * @param {*} fail  一个函数,指示加载程序结束退出

   * @param {*} attempts 允许的最大重试次数

   */

  onError(error, retry, fail, attempts) {

    if (error.message.match(/fetch/) && attempts <= 3) {

      // 请求发生错误时重试,最多可尝试 3 次

      retry()

    } else {

      // 注意,retry/fail 就像 promise 的 resolve/reject 一样:

      // 必须调用其中一个才能继续错误处理。

      fail()

    }

  }

})

组件通信

父子组件通讯

父组件传值给子组件
// parent.vue

<div>

    <child :lists="lists"></child>

</div>



data() {

    return {

      lists: [1, 2, 3],

    };

}





// child.vue

 <div class="child">

    <ul>

      <li v-for="list in lists">{{list}}</li>//遍历传递过来的值,然后呈现到页面

    </ul>

 </div>

 

 props:{

    lists:{           //这个就是父组件中子标签自定义名字

      type:Array,

      required:true

    }

}
子组件向父组件传值

通过$emit回传给父组件

隔代组件通讯

  • Provide / Inject
// index.vue

 provide: {

    title: 'vue',

    listLength: computed(() => this.lists.length)

 }

 

 // child.vue

 inject: ['title', 'listLength']
  • Vuex

父子、兄弟、隔代组件通讯

  • 抽象出最高的父组件进行个子组件之前通信
  • Vuex

Slot(插槽)

在React中实现自定义组件引入自定义组件:

// index.jsx

<Parent>

    <Child></Child>

</Parent>



// parent.jsx

<div>{children}</div>

在Vue中可以使用Slot实现类似功能:

普通插槽

// index.vue

<slot-demo> <div>slot-child</div> </slot-demo>

  

// slot.vue

<div class="slot">

  <slot></slot>

</div>

具名插槽

// index.vue

<slot-demo>

  <template #header>

    <div>header</div>

  </template>



  <template #body>

     <div>body</div>

  </template>

</slot-demo>



// slot-demo.vue

<div class="slot">

    <slot name="header"></slot>

    <slot name="body"></slot>

</div>

作用域插槽

// index.vue

<slot-demo>

    <template #footer="{item}">

      <span class="green">{{ item }}</span>

    </template>

</slot-demo>



// slot-demo.vue

<ul>

   <li v-for="(item, index) in items" :key="index">

     <slot name="footer" :item="item" :index="index"></slot>

   </li>

</ul>

composition-api

setup

参数:

  1. props: 组件传入的属性
  2. context

setup中接受的props是响应式的, 当传入新的props 时,会及时被更新。由于是响应式的, 所以不可以使用ES6解构,解构会消除它的响应式。

ref

ref我们用来将基本数据类型定义为响应式数据;。

ref并不只是具有对基本数据类型的响应式处理能力,他也是可以处理对象的。

<template>

  <div>

    {{ num }}

  </div>

</template>





export default defineComponent({

  setup() {

    const num = ref(1);

    return { num };

  },

});

reactive

reactive用来将引用类型定义为响应式数据,其本质是基于Proxy实现对象代理

返回一个obj对象可以实现数据的更新

<template>

  <div>

    {{ obj.a }}

  </div>

  <button @click="obj.foo">test</button>

</template>



setup() {

    const obj = reactive({

      a: 1,

      b: 2,



      foo() {

        this.a = 2;

      },

    });



    return { obj };

},

返回解雇赋值时无法更新试图

<template>

  <div>

    {{ a }}

  </div>

  <button @click="foo">test</button>

</template>



setup() {

    const obj = reactive({

      a: 1,

      b: 2,



      foo() {

        this.a = 2;

      },

    });



    return { ...obj };

},

可以通过toRefs做处理

<template>

  <div>

    {{ a }}

  </div>

  <button @click="foo">test</button>

</template>



setup() {

    const obj = reactive({

      a: 1,

      b: 2,



      foo() {

        this.a = 2;

      },

    });



    return { ...toRefs(obj) };

},

toRef

可以用来为源响应式对象上的某个 property 新创建一个 ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接。

const state = reactive({

  foo: 1,

  bar: 2

})



const fooRef = toRef(state, 'foo')



fooRef.value++

console.log(state.foo) // 2



state.foo++

console.log(fooRef.value) // 3

toRefs

toRefs会将我们一个响应式的对象转变为一个普通对象,然后将这个普通对象里的每一个属性变为一个响应式的数据

const state = reactive({

  foo: 1,

  bar: 2

})



const stateAsRefs = toRefs(state)

/*

stateAsRefs 的类型:



{

  foo: Ref<number>,

  bar: Ref<number>

}

*/



// ref 和原始 property 已经“链接”起来了

state.foo++

console.log(stateAsRefs.foo.value) // 2



stateAsRefs.foo.value++

console.log(state.foo) 

computed

接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 ref 对象。

const count = ref(1)

const plusOne = computed(() => count.value + 1)



console.log(plusOne.value) // 2



plusOne.value++ // 错误

watchEffect

立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。

watch

侦听多个数据源

const firstName = ref('')

const lastName = ref('')



watch([firstName, lastName], (newValues, prevValues) => {

  console.log(newValues, prevValues)

})

 

const changeValues = () => {

      firstName.value = 'John';

      lastName.value = 'Smith';

    // 打印 ["John", "Smith"] ["", ""]

};

注意多个同步更改只会触发一次侦听器。

通过更改设置 flush: 'sync',我们可以为每个更改都强制触发侦听器,尽管这通常是不推荐的

watch([firstName, lastName], (newValues, prevValues) => {

  console.log(newValues, prevValues)

}, { flush: 'sync' })

 
  • watchEffect

    比较,

    watch
    

    允许我们:

    • 懒执行副作用;
  • 更具体地说明什么状态应该触发侦听器重新运行;

  • 访问侦听状态变化前后的值。

setUp中执行方法

方式一

<template>

  <button @click="foo">test</button>

</template>



setup() {

    const obj = reactive({

      a: 1,



      foo() {

        this.a = 2;

      },

    });



    return { ...toRefs(obj) };

},

方式二

<template>

  <button @click="foo">test</button>

</template>



setup() {

   const foo = (data) => {

 

   }

   

   return { foo };

},

方式三

<template>

  <button @click="foo">test</button>

</template>



const foo = (data) => {

}

  

setup() {

   return { foo };

},

Vue Router

创建路由

const router = createRouter({

  history: createWebHistory(),

  routes,

});

参数history:

  • createWebHashHistory --> html5 history 实现
  • createWebHistory --> window onhashchange 实现

完整的导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

导航守卫

全局前置守卫

在路由跳转前触发,可在执行 next 方法前做一些身份登录验证的逻辑。

router.beforeEach((to, from, next) => {

  // 必须执行 next 方法来触发路由跳转 

  next() 

})

全局解析守卫

与 beforeEach 类似,也是路由跳转前触发,区别是还需在所有组件内守卫和异步路由组件被解析之后,也就是在组件内 beforeRouteEnter 之后被调用。

router.beforeResolve(

(to, from, next) => {

  // 必须执行 next 方法来触发路由跳转 

  next() 

})

全局后置钩子

和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身。

router.afterEach((to, from) =>{

})

路由独享守卫

可在路由配置上直接定义 beforeEnter

{

    path: '/about',

    name: 'About',

    beforeEnter(to, from, next) {},

    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),

}

组件内的守卫

beforeRouteEnter(to, from, next) {

    // 不能获取组件实例 this

    // 当守卫执行前,组件实例还没被创建

    next();

},



beforeRouteUpdate(to, from, next) {

    next();

    // 当前路由改变,但是组件被复用时调用

    // 可访问实例 this

},

  

beforeRouteLeave(to, from, next) {

    next();

    // 导航离开组件时被调用

},

路由匹配、路由传参、命名路由

路由匹配方式和react相似

命名路由

支持创建路由时给路由设置name属性命名,在跳转时可以指定跳转name进行跳转。

 {

      path: '/user/:userId',

      name: 'user',

      component: User

 }

 

 router.push({ name: 'user', params: { userId: 123 } })

传参

  1. 方式一
router.push({ path: '/detail/${id}'})



// 获取参数

route.params.id
  1. 方式二

和react传递state参数相似,刷新会丢失数据

router.push({ name: 'Detail', params: { id: id } })



// 获取参数

route.params.id
  1. 方式三
router.push({ name: 'Detail', query: { id: id } })



// 获取参数

route.query.id
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值