让多个组件使用同一个挂载点,并动态切换,这就是动态组件。通过使用保留的 <component> 元素,动态地绑定到它的 is 特性,可以实现动态组件。它的应用场景往往应用在路由控制或者 tab 切换中。
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8"/>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<button @click="change">切换页面</button>
<component :is="currentView"></component>
</div>
<script>
new Vue({
el: '#app',
data:{
index:0,
arr:[
{template:'<div>我是主页</div>'},
{template:'<div>我是提交页</div>'},
{template:'<div>我是存档页</div>'}
],
},
computed:{
currentView(){
return this.arr[this.index];
}
},
methods:{
change(){
this.index = (++this.index)%3;
}
}
})
</script>
</body>
</html>
component 标签中 is 属性决定了当前采用的子组件,:is 是 v-bind 的简写,绑定了父组 件中 data 的 currentView 属性。点击按钮时,会更改数组 arr 的索引值,同时也修改了子组 件的内容。
keep-alive
<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们,以便提高提取效率。和 <transition> 相似,<keep-alive> 是一个抽象组件,它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8"/>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<button @click="change">切换页面</button>
<keep-alive>
<component :is="currentView"></component>
</keep-alive>
</div>
<script>
new Vue({
el: '#app',
data:{
index:0,
arr:[
{template:'<div>我是主页</div>'},
{template:'<div>我是提交页</div>'},
{template:'<div>我是存档页</div>'}
],
},
computed:{
currentView(){
return this.arr[this.index];
}
},
methods:{
change(){
/*es6新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。*/
let len = this.arr.length;
this.index = (++this.index)% len;
}
}
})
</script>
</body>
</html>
activated 钩子函数
Vue给组件提供了activated钩子函数,作用于动态组件切换或者静态组件初始化的过程中。activated是和template、data等属性平级的一个属性,形式是一个函数,函数里默认有一个参数,而这个参数是一个函数,执行这个函数时,才会切换组件,即可以延迟执行当前的组件。
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8"/>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<button @click='toShow'>点击显示子组件</button>
<!----或者<component v-bind:is="which_to_show" keep-alive></component>也行----->
<keep-alive>
<component v-bind:is="which_to_show" ></component>
</keep-alive>
</div>
<script>
// 创建根实例
var vm = new Vue({
el: '#app',
data: {
which_to_show: "first"
},
methods: {
toShow: function () { //切换组件显示
var arr = ["first", "second", "third", ""];
var index = arr.indexOf(this.which_to_show);
if (index < 2) {
this.which_to_show = arr[index + 1];
} else {
this.which_to_show = arr[0];
}
console.log(this.$children);
}
},
components: {
first: { //第一个子组件
template: "<div>这里是子组件1</div>"
},
second: { //第二个子组件
template: "<div>这里是子组件2,这里是延迟后的内容:{{hello}}</div>",
data: function () {
return {
hello: ""
}
},
activated: function (done) { //执行这个参数时,才会切换组件
console.log('beixi')
var self = this;
var startTime = new Date().getTime(); // get the current time
//两秒后执行
while (new Date().getTime() < startTime + 2000){
self.hello='我是延迟后的内容';
}
}
},
third: { //第三个子组件
template: "<div>这里是子组件3</div>"
}
}
});
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8"/>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<button @click="change">切换页面</button>
<keep-alive>
<home v-if="index===0"></home>
<posts v-else-if="index===1"></posts>
<archive v-else></archive>
</keep-alive>
</div>
<script>
new Vue({
el: '#app',
components:{
home:{template:'<div>我是主页</div>'},
posts:{template:'<div>我是提交页</div>'},
archive:{template:'<div>我是存档页</div>'},
},
data:{
index:0,
},
methods:{
change(){
// 在data外面定义的属性和方法通过$options可以获取和调用
let len = Object.keys(this.$options.components).length;
this.index = (++this.index)%len;
}
}
})
</script>
</body>
</html>
异步组件
在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从 服务器加载一个模块。为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个 工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染,代码如下:
<div id="app">
<async-example></async-example>
</div>
<script>
Vue.component ( 'async-example ', function (resolve, reject){
setTimeout (function (){
//向resolve 回调传递组件定义
resolve ( {
template: '<div>这是异步渲染的内容!</div>'
})
},1000)
})
new vue ( {
el : '#app'
})
</script>
这个工厂函数会收到一个 resolve 回调,这个回调函数会在你从服务器得到 组件定义的时候被调用。你也可以调用 reject(reason) 来表示加载失败。这里的 setTimeout 是 为了演示异步,如何获取组件取决于你自己。比如把组件配置成一个对象配置,通过 Ajax 来请求,然后调用 reslove 传入配置选项。
在 Vue 中一般很少会用到直接操作 DOM,但不可避免有时候需要用到,这时我们可以 通过 ref 和$refs 来实现:
ref: ref 被用来给元素或子组件注册引用信息, 引用信息将会注册在父组件 的 $refs 对象上,如果是在普通的 DOM 元素上使用,引用指向的就是 DOM 元素,如果是 在子组件上,引用就指向组件的实例。
$refs:$refs 是一个对象,持有已注册过 ref 的所有的子组件。
普通获取 DOM 的方式
先通过 getElementById 方法来获取,代码如下:
<div id="app">
<input type="button" value="获取h3的值"@click="getElement"><h3 id="myh3">我是h3</ h3>
</div>
<script>
var vm=new vue ( {
el : "#app" ,
data : { } ,
methods : {
getElement ( ) {
//通过getElementById方式获取DOM对象
console.log (document. getElementById ( "myh3" ) .innerHTML) ;
}
}
})
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<!--引入vue-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="button" value="获取h3的值" @click="getElement">
<h3 id="myh3" ref="myh3">我是h3</h3>
<hr>
<login ref="mylogin"></login>
</div>
<script>
var login = {
template: "<h3>我是login子组件</h3>",
data(){
return {
msg: "ok"
}
},
methods:{
show(){
console.log("show方法执行了...")
}
}
}
var vm=new Vue({
el:"#app",
data:{},
methods:{
getElement(){
//通过getElementById方式获取DOM对象
// console.log(this.$refs.myh3.innerHTML);
console.log(this.$refs.mylogin.msg);
this.$refs.mylogin.show();
}
},
components:{
login
}
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!--引入vue-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!--引入bootstrap-->
<link rel="stylesheet" href="./lib/bootstrap-3.3.7.css">
</head>
<body>
<div id="app">
<cmt-box @func="loadComments"></cmt-box>
<ul class="list-group">
<li class="list-group-item" v-for="item in list" :key="item.id">
<span class="badge">评论人: {{ item.user }}</span>
{{ item.content }}
</li>
</ul>
</div>
<template id="tmpl">
<div>
<div class="form-group">
<label>评论人:</label>
<input type="text" class="form-control" v-model="user">
</div>
<div class="form-group">
<label>评论内容:</label>
<textarea class="form-control" v-model="content"></textarea>
</div>
<div class="form-group">
<input type="button" value="发表评论" class="btn btn-primary" @click="postComment">
</div>
</div>
</template>
<script>
var commentBox = {
data() {
return {
user: '',
content: ''
}
},
template: '#tmpl',
methods: {
postComment() { // 发表评论的方法
var comment = { id: Date.now(), user: this.user, content: this.content }
// 从 localStorage 中获取所有的评论
var list = JSON.parse(localStorage.getItem('cmts') || '[]')
list.unshift(comment)
// 重新保存最新的 评论数据
localStorage.setItem('cmts', JSON.stringify(list))
this.user = this.content = ''
this.$emit('func')
}
}
}
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
list: [
{ id: Date.now(), user: 'beixi', content: '这是我的网名' },
{ id: Date.now(), user: 'jzj', content: '这是我的真名' },
{ id: Date.now(), user: '贝西奇谈', content: '有任何问题可以关注公众号' }
]
},
beforeCreate(){ /* 注意:这里不能调用 loadComments 方法,因为在执行这个钩子函数的时候,data 和 methods 都还没有被初始化好*/
},
created(){
this.loadComments()
},
methods: {
loadComments() { // 从本地的 localStorage 中,加载评论列表
var list = JSON.parse(localStorage.getItem('cmts') || '[]')
this.list = list
}
},
components: {
'cmt-box': commentBox
}
});
</script>
</body>
</html>
![](https://img2020.cnblogs.com/blog/1020928/202110/1020928-20211017094052820-1131230263.png)