0
点赞
收藏
分享

微信扫一扫

Vue3.0整理

minute_5 2022-03-12 阅读 176
前端vue.js

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'

});

}

动态组件 & 异步组件

动态组件、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

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 条评论