详解vue高级特性(2)

methods: { updateMessage: async function () { this.message = '已更新' //在这里可以看出,message并没有立刻被执行 //要理解页面刷新和代码执行速度的差别 //通常我们在页面上立刻就能看到结果,那是因为一轮队列执行其实很快,感觉不出DOM刷新的过程和所耗费的时间 //但对于代码的执行,属于即刻级别,DOM没更新就是没更新,就是会有问题 console.log(this.$el.textContent) // => '未更新' await this.$nextTick() console.log(this.$el.textContent) // => '已更新' } }

通俗的解释:

1  Vue的DOM刷新机制是个异步队列,并不是你想象中的立刻、马上、即时更新!

2  这个异步队列是一轮一轮的执行并刷新

3  上面带来的问题是,一些依赖DOM更新完毕才能进行的操作(比如对新增加的DOM元素进行事件绑定),无法立刻执行,必须等待一轮队列执行完毕

4  最容易碰到上面问题的地方:created生命周期钩子函数中对DOM进行操作

5  解决办法:使用this.nextTick(回调函数)方法,将对DOM的操作作为它的回调函数使用。

四、函数式组件

因为传统编写模板的能力不足,我们引入了渲染函数createElement。我们又希望获得更多的灵活度,于是引入了JSX。最后,我们发现有些简单的模板可以更简单更小巧的实现,于是引入了函数式组件。Vue总是试图为每一种场景提供不同的能力。

有这么一类组件,它的特点是:

1  比较简单

2  没有管理任何状态,也就是说无状态,没有响应式数据

3  没有监听任何传递给它的状态

4  没有写生命周期方法

5  本质上只是一个接收一些prop的函数

6  没有实例,没有this上下文

那么这个组件可以定义为函数式组件。与普通组件相比,函数式组件是无状态的,无法实例化,没有任何的生命周期和方法,适合只依赖于外部数据的变化而变化的组件,因其轻量,渲染性能会有所提高。

创建函数式组件

以定义全局组件的方式

Vue.component('my-component', { functional: true, // Props 是可选的 props: { // ... }, // 为了弥补缺少的实例 // 提供第二个参数作为上下文 render: function (createElement, context) { // ... } })

注意其中的functional: true,

在 Vue 2.3.0 或以上的版本中,你可以省略 props 选项,所有组件上的 attribute 都会被自动隐式解析为 prop。

当使用函数式组件时,该引用将会是 HTMLElement,因为他们是无状态的也是无实例的。

对于单文件组件,创建函数式组件的方式是在模板标签内,添加functional属性

<template functional> ... </template> <script> ... </script> <style> ... </style>

最重要的context参数

因为无状态,没有this上下文,所以函数式组件需要的一切都是通过 context 参数来传递,它是一个包括如下字段的对象:

props:提供所有 prop 的对象

children:VNode 子节点的数组

slots:一个函数,返回了包含所有插槽的对象

scopedSlots:(2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。

data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件

parent:对父组件的引用

listeners:(2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 data.on 的一个别名。

injections:(2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的 property。

应用场景

函数式组件的一个典型应用场景是作为包装组件,比如当你碰到下面需求时:

程序化地在多个组件中选择一个来代为渲染;

在将 children、props、data 传递给子组件之前操作它们。

下面是一个 smart-list 组件的例子,它能根据传入 prop 的值来代为渲染更具体的组件:

var EmptyList = { /* ... */ } var TableList = { /* ... */ } var OrderedList = { /* ... */ } var UnorderedList = { /* ... */ } Vue.component('smart-list', { functional: true, props: { items: { type: Array, required: true }, isOrdered: Boolean }, render: function (createElement, context) { function appropriateListComponent () { var items = context.props.items if (items.length === 0) return EmptyList if (typeof items[0] === 'object') return TableList if (context.props.isOrdered) return OrderedList return UnorderedList } return createElement( appropriateListComponent(), context.data, context.children ) } })

五、监听子组件的生命周期

假如我们有父组件Parent和子组件Child,如果在父组件中需要监听子组件的mounted这个生命周期函数,并做一些逻辑处理,常规写法可能如下:

// Parent.vue <Child @mounted="doSth" /> //Child.vue mounted(){ this.$emit('mounted'); }

但是,Vue给我们提供了一种更简便的方法,子组件无需做任何处理,只需要在父组件引用子组件时使用@hook事件来监听即可,代码如下:

// Parent.vue <Child @hook:mounted="doSth" /> methods:{ doSth(){ //some codes here } }

核心是@hook:mounted="doSth"的写法!

当然这里不仅仅可以监听mounted,其他生命周期都可以监听,例如created、updated等。

六、样式穿透

我们知道,在单文件组件的style中使用 scoped 属性后,父组件的样式将不会渗透到子组件中。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/773bf3bc7394b41cdab603808ee3ce29.html