1. Vue工程化中创建全局组件
工程化中创建全局组件需要在src/components
文件夹下创建,并且全局组件需要写在 js 文件内。
步骤:
- 例如,我们现在要创建与加载相关的全局组件,创建目录如下:
编写 index.js 文件
import Vue from 'vue' // 工程化中,它没有了template配置 只有 el和render,全局组件不能使用el //创建全局组件 Vue.component('loading', { render: h => { return h('h3', null, '加载中...') } })
编写入口文件 main.js 文件
import Vue from 'vue' import App from './App.vue' // 引入全局组件 import './components/loading' Vue.config.productionTip = false new Vue({ render: h => h(App) }).$mount('#app')
编写父组件 App.vue 文件
<template> <div> <h3 class="title">App组件</h3> <hr /> <!-- 引入全局组件,不需要注册 --> <loading /> </div> </template> <script> export default { data() { return { } } } </script>
在工作中,我们更喜欢下面这种写法:
我们需要在 loading 文件夹下再创建一个 loading.vue
文件,然后修改以下index.js
文件的写法,入口文件main.js
和父组件App.vue
的写法不变。
loading.vue
文件:
<template>
<div>
<h3>我是一个loading</h3>
</div>
</template>
<script>
export default {}
</script>
<style lang="scss" scoped>
h3 {
color: green;
}
</style>
index.js
文件:
import Vue from 'vue'
import loading from './loading.vue'
Vue.component('loading', loading)
2. 使用 extend 创建组件
我们创建Vue实例时,都会有一个el选项,来指定实例的根节点,如果不写el选项,那组件就处于未挂载状态。Vue.extend 的作用,就是基于 Vue 构造器,创建一个‘ 子类 ',它的参数跟new Vue的基本一样,但data要跟组件一样,是个函数,再配合$mount
,就可以渲染组件,并且挂载到任意指定的节点上,比如body(这是单文件组件做不到的)。
通俗一点来讲,工作中我们会有如下应用场景,比如我们需要一个弹框(即组件)显示在页面上,但是又不想让弹框影响整个 html 层级,进而影响页面的CSS样式。所以我们需要弹框不要挂载到 app 节点上,而是挂载在另一个新创建的节点上,这时候就需要用到 extend 创建组件。
我们可以通过 extend 创建一个组件,让这个组件拥有 el 的能力,即能挂载到自定义的 dom 节点上。
步骤:
在公共
index.html
文件中,新建一个弹框节点新建
dialog
文件夹,并在文件夹下创建index.js
和dialog.vue
文件编写
index.js
文件import Vue from 'vue' import dialog from './dialog.vue' // 使用extend继承创建组件 const Cmp = Vue.extend(dialog) new Cmp().$mount('#dialog')
编写
dialog.vue
文件<template> <div>我是一个弹层</div> </template> <script> export default {} </script> <style lang="scss" scoped></style>
编写入口文件
main.js
文件import Vue from 'vue' import App from './App.vue' import './components/dialog' Vue.config.productionTip = false new Vue({ render: h => h(App) }).$mount('#app')
改写一下,我们使用模块化的写法:
修改index.js
文件:
import Vue from 'vue'
import dialog from './dialog.vue'
// 使用extend继承创建组件
const Cmp = Vue.extend(dialog)
export default () => new Cmp().$mount('#dialog')
// new Cmp().$mount('#dialog')
修改main.js
文件:
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
Vue.prototype.$dialog = dialog
new Vue({
render: h => h(App)
}).$mount('#app')
编写App.vue
文件,在页面中显示:
<template>
<div>
<h3 class="title">App组件</h3>
<hr />
<button @click="showDialog">显示</button>
</div>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
showDialog() {
return this.$dialog()
}
}
}
</script>
3. 使用 extends 配置组件
extends和mixins类似,通过暴露一个extends对象到组件中使用。通俗来讲,extends 配置是一种继承机制,并且只能继承 js,不能继承 html 模板,和混入非常相似。我们一般用 extends 配置来对已有的组件进行功能拓展或者微调,即将已有的组件继承过来,进行重写。
父组件:
<template>
<div>
<h3 class="title">App组件</h3>
<hr />
<extendscmp />
</div>
</template>
<script>
import extendscmp from './components/extends'
export default {
components: {
extendscmp
},
data() {
return {
}
},
methods: {
}
}
</script>
<style lang="scss" scoped></style>
子组件(继承于 msg 组件):
<template>
<div>
<h3>extends --- {{ title }}</h3>
<msg></msg>
</div>
</template>
<script>
import msg from './msg'
export default {
components: { msg },
// 继承,它只能继承方法和属性不能继承模板
extends: msg,
created() {
console.log(this.run())
}
}
</script>
<style lang="scss" scoped></style>
msg 组件:
<template>
<div>
<h3>{{ title }}</h3>
</div>
</template>
<script>
export default {
data() {
return {
title: '我是一个msg'
}
},
methods: {
run() {
return 'msg'
}
}
}
</script>
<style lang="scss" scoped></style>
4. 异步组件
使用同步方式导入组件,在进行网络请求时,js 文件会一次性打包到 app 节点中,在服务器端渲染时,网络请求得到结果不会这么快,所以同步方式在某些场景下可能会得不到数据,这时我们需要异步导入组件。
先来看一下同步导入的网络请求状况:
父组件:
<template>
<div>
<h3 class="title">App组件</h3>
<hr />
<child />
</div>
</template>
<script>
// 同步导入
import child from './components/child.vue'
export default {
components: {
child
},
data() {
return {}
},
methods: {}
}
</script>
<style lang="scss" scoped></style>
子组件:
<template>
<div>
<h3>child组件</h3>
</div>
</template>
<script>
export default {};
</script>
<style lang="scss" scoped>
</style>
异步导入组件的好处:
- 异步组件具有滞后性,当组件在某一个环节卡住时,不会影响到其他程序。
- 异步组件使用 import 导入,可以提供代码分割,可以使得入口文件体积变小,网页加载速度变快。
异步导入组件的写法,以及网络请求状况:
父组件写法:
<template>
<div>
<h3 class="title">App组件</h3>
<hr />
<child />
</div>
</template>
<script>
export default {
components: {
// 异步组件
// key标签名:函数,此函数必须要返回一个promise
// import当函数用,返回的是Promise
// 使用此方式最多的
// 这种方式会将js文件拆开发送
child: () => import('./components/child.vue')
// 自定义分包的文件名称
// child: () => import(/*webpackChunkName:'child'*/ './components/child.vue')
},
data() {
return {}
},
methods: {}
}
</script>
<style lang="scss" scoped></style>
由结果可以看出使用异步组件分割代码,可以使得出入口文件(app.js)的体积变小。
异步组件工厂:
父组件:
<template>
<div>
<h3 class="title">App组件</h3>
<hr />
<child />
</div>
</template>
<script>
import loading from './components/loading/loading.vue'
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./components/child.vue'),
// 异步组件加载时使用的组件
loading,
// 加载失败时使用的组件
// error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
export default {
components: {
child: AsyncComponent
},
data() {
return {}
},
methods: {}
}
</script>
<style lang="scss" scoped></style>
上面是正常用法的运行结果,但是由于网络延迟较小,结果演示并不明显,所以为了让异步加载更好的展示出来,我们自定义一个滞后执行的局部组件来运行,修改代码如下:
父组件:
<template>
<div>
<h3 class="title">App组件</h3>
<hr />
<child />
</div>
</template>
<script>
import loading from './components/loading/loading.vue'
const cmp = new Promise(resolve => {
setTimeout(() => {
resolve(import('./components/child.vue'))
}, 3000)
})
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: cmp,
// 异步组件加载时使用的组件
loading,
// 加载失败时使用的组件
// error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
export default {
components: {
// 异步组件工厂
child: AsyncComponent
},
data() {
return {}
},
methods: {}
}
</script>
<style lang="scss" scoped></style>