VUE中的ref、is和$ref

最近在VUE项目中,需要实现一个Tab页的功能,点击不同的Tab展示不同的页面,子页面中再次点击上下或者左右箭头又可以翻页,而且很多的页面除了数据布局也一致;

看到如上的功能想必正在阅读这篇文章的你心里已经有了解决方案吧!那么我也分享下我的解决方案,如果有需要改进的地方或者更好的实现方案,欢迎随时与我探讨!

首先,需要组建话布局,将公共的页面封装成组建;与此同时,这里重点讲解一下封装组建所用到的重要的属性和对象ref和$ref

ref

ref被用来给元素或子组建注册引用信息。引用信息将会注册在父组件的$refs对象上。如果在普通的DOM元素上使用,引用指向的就是DOM元素;如果用在子组件上,引用就指向组件实例;

1
2
3
4
5
<!-- `vm.$refs.p` will be the DOM node -->
<p ref='p'>hello</p>

<!-- `vm.$refs.child` will be the child component instance -->
<child-component ref="child"></child-component>

v-for用于元素或者组件的时候,引用信息将是包含DOM节点或组件实例的数组。

关于ref注册事件的重要说明: 因为ref本身是作为渲染结果被创建的,在初试渲染的时候你不能访问它们,它们还不存在! $refs也不是响应式的,因此不要在试图用它在模板中做数据绑定。

is

用于动态组件且基于DOM内模版的限制来工作;

For example

1
2
3
4
5
6
7
<!-- 当`currentView`改变时,组件也跟着改变 -->
<component v-bind:is="currentView"></component>

<!-- 这样做是有必要的,因为`<my-row>`放在一个`<table>`内可能无效且被放置到外面 -->
<table>
<tr is="my-row"></tr>
</table>

动态组件

有时候,在不同组件之间进行动态切换时非常有用的,比如:在一个对标签的界面里: A B C点击不同的字母,下面显示不同的界面,上述内容可以通过Vue的元素加一个特殊的is特性来实现:

1
2
<!-- 组件会在`currentTabComponent`改变时改变 -->
<component v-bind:is="currentTabComponent"></component>

在上述示例中,currentTabComponent可以包括:

  • 已注册组件的名字
  • 一个组件的选项对象

For example:

1
2
3
4
5
6
7
8
9
10
<div class="page-main operation-minitor_main">
<ul class="common-nav">
<button v-for="item in tabs" class="common-nav_item"
:class="{'active': tagFlag == item.id}"
@focus="getFocus(item.id)" @click="toggleTab(item.id)>
{{item.name}}
</button>
</ul>
<div ref="currentTabs" :is="currentTab" :authentication="isAuthentication" :orgId="orgId"></div>
</div>

1
2
3
4
5
6
7
8
9
10
11
12
13
toggleTab(tab) {
let self = this;
self.tagFlag = tab;

if(self.currentTab == tab) {
return false;
}
self.currentTab = tab;

self.$nextTick(function() {
self.$refs.currentTabs.initAll();
})
}

如下是两个示例的完成代码,帮忙了解绑定组件选项对象,而不是已注册组件名的示例;

For example:

1
2
3
4
5
6
7
8
9
10
11
<div ref="currentPages" :is="componentName" :orgId="orgCode"></div>

<!-- last page -->
<div class="t-dianosis-down t-dianosis-up" v-if="pageName != 'sammary'">
<button id="arrowUp" class="arrow-up" @click="pullUp"></button>
</div>

<!-- next page -->
<div class="t-dianosis-down" v-if="pageName != "reduce">
<button id="arrowDown" class="arrow-down" @click="pullDown"></button>
</div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pullUp() {
let self = this;

switch(self.pageName) {
case 'reduce':
self.pageName = 'increase';
self.componentName = 'DataTable';
window.pageName = 'increase';

document.getElementById("arrowDown").focus();
document.getElementById("arrowUp").focus();
break;
default:
break;
}

self.$nextTick(function() {
self.$refs.currentPages.initialize();
})
}

动态组件&异步组件

在动态组件上使用keep-alive, 我们之前曾经在一个多标签的界面中使用is特性来切换不同的组件:

1
<component v-bind:is="currentTabComponent"></component>

当在这些组件之间切换的时候, 你有时候会想保持这些组件的状态,以避免反复重渲染导致的问题,接下来我们就说说这个多标签界面:

你会遇见这样的问题,如果在tab A下显示一篇文章, 切换到Tab B下,然后在切换回tab A,是不会继续展示你之前选择的文章的。这是因为你每次切换新标签的时候,Vue都创建了一个新的curentTabComponent实例;

重新创建动态组件的行为通常是非常有用的,但是在这个实例中,我们更希望那些标签的组件实例能够被在它们第一次创建的时候缓存下来。为了解决这个问题,我们可以用一个keep-alive元素将其动态组件包裹起来。

1
2
3
4
<!-- 失活的组件将会被缓存 -->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>

现在这个tab A标签保持了它的状态,甚至当它未被渲染时也是如此;

注意这个<keep-alive>要求被切换到的组件都有自己的名字, 无论是通过组件的name选项还是局部/全局注册;

异步组件

在大型项目中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了简化,Vue允许你以一个工厂函数的方法定义你的组件,这个工厂函数会异步解析你的组件定义。Vue只有在这个组件需要被渲染的时候才会被触发这个工厂函数,且会把结果缓存起来供未来重渲染。例如:

1
2
3
4
5
6
7
8
Vue.component('async-example', function(resolve, reject) {
setTimeout(function() {
// 向`resolve`回调传递组件定义
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})

如你所见,这个工厂函数会受到一个resolve回调,这个回调函数会在你从服务器得到组件定义的时候被调用。你也可以调用reject(reason)来表示加载失败。这里的setTimeout是为了演示用的,如果获取组件取决于你自己,一个推荐的做法是将异步组件和webpack的code-splitting功能一起配合使用:

1
2
3
4
Vue.component('async-webpack-example', function(resolve) {
//这个特殊的`require`语法将会告诉webpack自动将你的构建代码切割成多个包,这些包会通过Ajax请求加载
require(['./my-async-componment'], resolve);
})

你也可以在工厂函数中返回一个promise,所以把webpack 2ES2015语法加在一起,我们可以写成这样:

1
2
3
4
5
Vue.component(
'async-webpack-example',
// 这个`import`函数会返回一个`promise`对象。
() => import('./my-async-component')
)

当使用局部注册的时候,你也可以直接提供一个返回promise的函数:

1
2
3
4
5
new Vue({
components: {
'my-component': () => import('./my-async-component')
}
})

坚持原创技术分享,您的支持将鼓励我继续创作!