Vue中的动态组件和异步组件
1.动态组件
1.1 动态组件的用法
如果在开发过程中想要实现动态组件,比如在不同的条件下显示不同的组件,可以使用<component>
标签结合:is
属性。
定义组件:
假设你有两个组件:ComponentA.vue
和 ComponentB.vue
。
//ComponentA.vue
<template>
<div>这是组件A</div>
</template>
<script>
export default {
name: 'ComponentA'
}
</script>
//ComponentB.vue
<template>
<div>这是组件B</div>
</template>
<script>
export default {
name: 'ComponentB'
}
</script>
使用动态组件:
在父组件中,你可以这样使用<component :is="currentComponent">
来动态切换组件。
//ParentComponent.vue
<template>
<div>
<button @click="changeComponent('ComponentA')">切换到组件A</button>
<button @click="changeComponent('ComponentB')">切换到组件B</button>
<component :is="currentComponent"></component>
</div>
</template>
<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';
export default {
data() {
return {
currentComponent: 'ComponentA' // 默认显示ComponentA
}
},
methods: {
changeComponent(componentName) {
this.currentComponent = componentName; // 切换组件名称
}
},
components: {
ComponentA,
ComponentB
}
}
</script>
1.2 is属性的其他作用
有些 HTML 元素,诸如<ul>
、<ol>
、<table>
和 <select>
,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 <li>
、<tr>
和 <option>
,只能出现在其它某些特定的元素内部。
这会导致我们使用这些有约束条件的元素时遇到一些问题。例如:
<table>
<blog-post-row></blog-post-row>
</table>
这个自定义组件 <blog-post-row>
会被作为无效的内容提升到外部,并导致最终渲染结果出错。幸好这个特殊的 is
attribute 给了我们一个变通的办法:
<table>
<tr is="blog-post-row"></tr>
</table>
2.异步组件
2.1 什么是异步组件
当我们的项目达到一定的规模时,对于某些组件来说,我们并不希望一开始全部加载,而是需要的时候进行加载。这样做可以很好的提高用户体验,加快页面的载入速度,为此Vue 提供了异步组件的性能优化方案。
Vue 异步组件:是指通过异步方式加载的组件。
2.2 组件的加载 VS 组件渲染
- 组件加载:是组件的引入和注册,不走组件的生命周期函数
- 组件渲染:是生成组件实例的过程,走生命周期函数
2.3 异步组件的注册
(1)基础使用
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// 向 `resolve` 回调传递组件定义
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
工厂函数会收到一个 resolve
回调,这个回调函数会在你从服务器得到组件定义的时候被调用。你也可以调用 reject(reason)
来表示加载失败。这里的 setTimeout
是为了演示用的,如何获取组件取决于你自己。
(2)require引入组件
异步组件和webpack
的code-splitting
功能一起配合使用:
Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 `require` 语法将会告诉 webpack
// 自动将你的构建代码切割成多个包,这些包
// 会通过 Ajax 请求加载
require(['./my-async-component'], resolve)
})
(3)import引入组件
你也可以在工厂函数中返回一个 Promise
,import()
会返回一个 Promise
。
Vue.component(
'async-webpack-example',
// 这个动态导入会返回一个 `Promise` 对象。
() => import('./my-async-component')
)
(4)局部注册
//vue文件中
components: {
// 方式一: import
AsyncComponent: () => import("./components/asyncComponent.vue"),
// 方式二: require
AsyncComponent: (resolve) => require(["./components/asyncComponent.vue"], resolve),
},
2.4 处理加载状态
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
全局注册:
Vue.component('async-webpack-example', AsyncComponent)
局部注册:
components: {
AsyncComponent: AsyncComponent),
},
下面再来列举一个处理加载状态的详细例子来进行说明:
<template>
<div class="index-area">
<button @click="show=!show">点击</button>
<div v-if="show">
<asyncCompsList></asyncCompsList>
</div>
</div>
</template>
<script>
import loadingComp from "@/components/asyncComps/loading.vue";
import errorComp from "@/components/asyncComps/error.vue";
const asyncCompsList = () => ({
component: import(/* webpackChunkName: 'asyncCompsList' */"@/components/asyncComps/list.vue"),
loading:loadingComp,
error:errorComp,
delay:200,
timeout:3000,
})
export default {
name: "index",
components: {
//这里注释掉,用下面那种方法可以处理加载状态
// list:()=>import(/* webpackChunkName: 'asyncCompsList' */"@/components/asyncComps/list.vue")
asyncCompsList
},
data() {
return {
show: false,
}
}
}
</script>
<style scoped>
</style>
2.5 异步组件 VS 普通组件
- 异步组件: 只有在template模板使用的时候,才会加载和渲染组件
- 同步组件: 在引入和注册的时候已经加载完成组件,在template模板使用的时候,开始渲染组件
下面来看一个例子:
如下,在App.vue
主页面中、分别用同步和异步的方式引入组件syncComponent
和asyncComponent
,点击按钮可以控制两个组件的渲染:
//App.vue
<template>
<div id="app">
<button @click="shwoAsync = true">展示异步组件</button>
<button @click="shwoSync = true">展示同步组件</button>
<!-- 异步组件 -->
<AsyncComponent v-if="shwoAsync" />
<!-- 同步组件 -->
<SyncComponent v-if="shwoSync" />
</div>
</template>
<script>
import SyncComponent from "./components/syncComponent.vue";
export default {
name: "App",
components: {
AsyncComponent: () => import("./components/asyncComponent.vue"),
SyncComponent,
},
data() {
return {
// 是否展示异步组件
shwoAsync: false,
// 是否展示同步组件
shwoSync: false,
};
},
};
</script>
asyncComponent
异步组件内容如下:
<script>
/*
* 异步组件:当第一次切换到当前组件时候,才会去后台请求异步组件渲染函数,请求完了之后,
* 下面这句代码才会执行,然后根据这个渲染函数创建组件实例
*/
console.log("异步组件加载----");
export default {
name: "AsyncComponent",
};
</script>
syncComponent
同步组件内容如下:
<script>
/* 这个在没有渲染到该组件时, 就已经被引入执行了,只会执行一次 */
console.log("同步组件加载----");
export default {
name: "syncComponent",
};
</script>
运行项目,打开控制台,在进入App.vue
的时候、同步组件syncComponent
的js代码已经开始执行。
但是,只有点击展示异步组件按钮的时候,异步组件的js代码才开始执行。