组件 (Component)是 Vue.js 最强大的功能之一,组件是一个自定义元素或称为一个模块,包括所需的模板(HTML)、逻辑(JavaScript)和样式(CSS)。
组件化开发的特点:
组件也是有 全局(component) 与 局部(components) 之分。
在使用组件时需要注意以下几点:
- data:{
- msg: 'hello world'
- }
-
- data: function(){
- return {
- msg: '你好世界'
- }
- }
- <!-- 单个根元素 -->
- <div>
- <ul>
- <li></li>
- </ul>
- <ul>
- <li></li>
- </ul>
- </div>
- <!-- 不符合单个根元素的情况 -->
- <p></p>
- <p></p>
- `${a}`
大驼峰式组件名不能在HTML模板中直接使用,如果需要在HTML模板中使用,需要将其进行特定规则转化:
- 首字母从大写转为小写
- 后续每遇到大写字母都要转化成小写并且在转化后的小写字母前加 -
例如, WoDeZuJian 这个大驼峰组件名在HTML中使用的时候需要写成 wo-de-zu-jian
全局组件注册形式如下:
- // 声明全局组件
- Vue.component(componentName,{
- data: '组件数据',
- template: '组件模版内容'
- })
上述示例中, component() 的第一个参数是 组件名 (实则可以看作是HTML标签名称),第二个参数是一个对象形式的选项,里面存放组件的声明信息。全局组件注册后,任何Vue实例都可以使用。
例如,有以下代码:
- // 声明一个全局的HelloWorld组件
- Vue.component('HelloWorld', {
- data: function(){
- return {
- msg: 'HelloWorld'
- }
- },
- template: '<div>{{msg}}</div>'
- });
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>01 全局注册组件</title>
- </head>
- <body>
- <div id="app">
- <!-- 3.使用组件 -->
- <!-- 像html标签一样使用组件
- a.在纯html模版中不可以使用大驼峰式的标签组件
- b.但是可以使用短横线的方式
- c.在vue的单文件组件中可以使用 大驼峰式的标签组件
- -->
- <my-header></my-header>
- </div>
- </body>
- <script src="lib/vue.js"></script>
- <script>
- // 1.定义组件
- const Header = {
- // 定义组件时没有 el 选项,使用template代替
- template: `
- <div>
- <h1>这里是标题 - {{ title }}</h1>
- <button @click="add">加</button>{{ count }}
- </div>
- `,
- // data在组件中必须是一个函数 --- 作用域
- data () {
- return {
- title: 'VUE 权威指南',
- count: 10
- }
- },
- // 其余选项均保持一致
- methods: {
- add () {
- this.count += 10
- }
- }
- }
- // 2.注册组件 - // 全局注册组件
- // Vue.component(组件名称,组件选项)
- // Vue.component('MyHeader', Header) // 大驼峰式命名
- Vue.component('my-header', Header) // 短横线方式命名
-
- new Vue({
- el: '#app'
- })
- </script>
- </html>
发现组件的 template选项,如果代码过多,代码的可读性很差,所以可以提取模版信息
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>01 全局注册组件</title>
- </head>
- <body>
- <div id="app">
- <!-- 4.使用组件 -->
- <!-- 像html标签一样使用组件
- a.在纯html模版中不可以使用大驼峰式的标签组件
- b.但是可以使用短横线的方式
- c.在vue的单文件组件中可以使用 大驼峰式的标签组件
- -->
- <my-header></my-header>
- </div>
- </body>
- <script src="lib/vue.js"></script>
- <!-- 1.定义组件的模版,通过id指示是哪个组件的
- 组件模版应该写在页面的哪个位置 - 因为 template 是一个空标签
- -->
- <template id="header">
- <div>
- <h1>这里是标题 - {{ title }}</h1>
- <button @click="add">加</button>{{ count }}
- </div>
- </template>
- <script>
- // 2.定义组件
- const Header = {
- // 定义组件时没有 el 选项,使用template代替
- template: '#header',
- // data在组件中必须是一个函数 --- 作用域
- data () {
- return {
- title: 'VUE 权威指南',
- count: 10
- }
- },
- // 其余选项均保持一致
- methods: {
- add () {
- this.count += 10
- }
- }
- }
- // 3.注册组件 - // 全局注册组件
- // Vue.component(组件名称,组件选项)
- // Vue.component('MyHeader', Header) // 大驼峰式命名
- Vue.component('my-header', Header) // 短横线方式命名
-
- new Vue({
- el: '#app'
- })
- </script>
- </html>
1.定义组件的模版 <template id=""></template>
2.定义组件 const Com = { }
3.注册组件: 全局注册和局部注册
4.使用组件
局部组件定义后只能在当前注册它的Vue实例中使用,其是通过某个 Vue 实例/组件的实例选项components 注册。
例如,有以下代码:
- var Child = {
- template: '<div>A custom component!</div>'
- }
- new Vue({
- components: {
- // <my-component> 将只在父组件模板中可用
- 'my-component': Child
- }
- })
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>03 局部注册组件</title>
- </head>
- <body>
- <div id="app">
- <!-- 4.使用组件 -->
- <!-- 像html标签一样使用组件
- a.在纯html模版中不可以使用大驼峰式的标签组件
- b.但是可以使用短横线的方式
- c.在vue的单文件组件中可以使用 大驼峰式的标签组件
- -->
- <my-header></my-header>
- </div>
- </body>
- <script src="lib/vue.js"></script>
- <!-- 1.定义组件的模版,通过id指示是哪个组件的
- 组件模版应该写在页面的哪个位置 - 因为 template 是一个空标签
- -->
- <template id="header">
- <div>
- <h1>这里是标题 - {{ title }}</h1>
- <button @click="add">加</button>{{ count }}
- </div>
- </template>
- <script>
- // 2.定义组件
- const Header = {
- // 定义组件时没有 el 选项,使用template代替
- template: '#header',
- // data在组件中必须是一个函数 --- 作用域
- data () {
- return {
- title: 'VUE 权威指南',
- count: 10
- }
- },
- // 其余选项均保持一致
- methods: {
- add () {
- this.count += 10
- }
- }
- }
- // 3.注册组件 - // 全局注册组件
- // Vue.component(组件名称,组件选项)
- // Vue.component('MyHeader', Header) // 大驼峰式命名
- // Vue.component('my-header', Header) // 短横线方式命名
-
- new Vue({
- el: '#app',
- components: { // 3.局部注册组件
- // MyHeader: Header
- 'my-header': Header
- }
- })
- </script>
- </html>
在HTML模板中,组件以一个自定义标签的形式存在,起到占位符的功能。通过Vue.js的声明式渲染后,占位符将会被替换为实际的内容,下面是一个最简单的模块示例:
- <div id="app">
- <my-component></my-component>
- </div>
也可以在一个组件的组件模板中去使用其他已经注册的组件
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>04 封装swiper</title>
- <link rel="stylesheet" href="lib/swiper-bundle.min.css" />
- <style>
- .swiper-container {
- width: 100%;
- height: 300px;
- }
- </style>
- </head>
- <body>
- <div id="app">
- <!-- 4.使用组件 -->
- <my-swiper></my-swiper>
- </div>
- </body>
- <script src="lib/vue.js"></script>
- <script src="lib/axios.min.js"></script>
- <script src="lib/swiper-bundle.min.js"></script>
- <!-- 1.定义组件的模版 -->
- <template id="mySwiper">
- <div class="swiper-container">
- <div class="swiper-wrapper">
- <div class="swiper-slide" v-for="item of list" :key="item.bannerid">
- <img :src="item.img" alt="">
- </div>
- </div>
- <!-- 如果需要分页器 -->
- <div class="swiper-pagination"></div>
-
- <!-- 如果需要导航按钮 -->
- <div class="swiper-button-prev"></div>
- <div class="swiper-button-next"></div>
-
- <!-- 如果需要滚动条 -->
- <div class="swiper-scrollbar"></div>
- </div>
- </template>
- <script>
- // 2.定义组件
- const SwiperCom = {
- template: '#mySwiper',
- data () {
- return {
- list: []
- }
- },
- mounted () {
- axios.get('http://121.89.205.189/api/banner/list')
- .then(res => {
- console.log(res.data.data)
- this.list = res.data.data
-
- })
- },
- updated() {
- var mySwiper = new Swiper ('.swiper-container', {
- loop: true, // 循环模式选项
-
- // 如果需要分页器
- pagination: {
- el: '.swiper-pagination',
- },
-
- // 如果需要前进后退按钮
- navigation: {
- nextEl: '.swiper-button-next',
- prevEl: '.swiper-button-prev',
- },
-
- // 如果需要滚动条
- scrollbar: {
- el: '.swiper-scrollbar',
- },
- })
- }
- }
- // 3.注册组件 - // 全局注册组件
-
- new Vue({
- el: '#app',
- components: { // 3.局部注册组件
- MySwiper: SwiperCom
- }
- })
- </script>
- </html>
如前面介绍组件时所说,组件有 分治 的特点,每个组件之间具有一定的独立性,但是在实际工作中使用组件的时候有互相之间传递数据的需求,此时就得考虑如何进行 组件间传值 的问题了。
父子组件
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>05 父子组件</title>
- </head>
- <body>
- <div id="app">
- <my-parent></my-parent>
- </div>
- </body>
- <template id="parent">
- <div>
- <h1>父组件</h1>
- <my-child></my-child>
- </div>
- </template>
- <template id="child">
- <div>
- <h3>子组件</h3>
- </div>
- </template>
- <script src="lib/vue.js"></script>
- <script>
- const Child = {
- template: '#child'
- }
- const Parent = {
- template: '#parent',
- components: {
- MyChild: Child
- }
- }
- // 谁用谁注册
- new Vue({
- el: '#app',
- components: {
- MyParent: Parent
- }
- })
- </script>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>06 父组件给子组件传值1</title>
- </head>
- <body>
- <div id="app">
- <my-parent></my-parent>
- </div>
- </body>
- <template id="parent">
- <div>
- <h1>父组件</h1>
- <!-- 1.父组件在调用子组件的地方,添加自定义的属性 -->
- <!-- 如果自定义的属性的值是变量,boolean类型,number类型,对象,数组,
- null或者undefined,需要使用绑定属性 -->
- <my-child :msg="msg" :flag="true" :num="100" :obj="{a:1}" :arr="[1, 2, 3]"></my-child>
- </div>
- </template>
- <template id="child">
- <div>
- <h3>子组件</h3>
- {{ msg }} - {{ flag }} -- {{ num }} -- {{ obj }} -- {{ arr }}
- </div>
- </template>
- <script src="lib/vue.js"></script>
- <script>
- // 在子组件定义的地方,添加props选项,props可以有两大类的使用方法
- // 数组
- // 数组的元素就是自定义的属性名,这样就可以通过自定义的属性名渲染子组件的数据
- // 对象,对象的写法又分为两大类写法
- const Child = {
- props: ['msg', 'flag', 'num', 'obj', 'arr'],
- template: '#child'
- }
- const Parent = {
- template: '#parent',
- data () {
- return {
- msg: 'hello msg'
- }
- },
- components: {
- MyChild: Child
- }
- }
- // 谁用谁注册
- new Vue({
- el: '#app',
- components: {
- MyParent: Parent
- }
- })
- </script>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>07 父组件给子组件传值2</title>
- </head>
- <body>
- <div id="app">
- <my-parent></my-parent>
- </div>
- </body>
- <template id="parent">
- <div>
- <h1>父组件</h1>
- <!-- 1.父组件在调用子组件的地方,添加自定义的属性 -->
- <!-- 如果自定义的属性的值是变量,boolean类型,number类型,对象,数组,
- null或者undefined,需要使用绑定属性 -->
- <my-child :msg="msg" :flag="true" :num="100" :obj="{a:1}" :arr="[1, 2, 3]"></my-child>
- </div>
- </template>
- <template id="child">
- <div>
- <h3>子组件</h3>
- {{ msg }} - {{ flag }} -- {{ num }} -- {{ obj }} -- {{ arr }}
- </div>
- </template>
- <script src="lib/vue.js"></script>
- <script>
- // 在子组件定义的地方,添加props选项,props可以有两大类的使用方法
- // 数组
- // 数组的元素就是自定义的属性名,这样就可以通过自定义的属性名渲染子组件的数据
- // 对象,对象的写法又分为两大类写法
- // 对象的key值为自定义的属性名,value值为数据类型,这样就可以通过自定义的属性名渲染子组件的数据
- const Child = {
- // props: ['msg', 'flag', 'num', 'obj', 'arr'],
- props: {
- msg: String,
- flag: Boolean,
- num: Number,
- obj: Object,
- arr: Array
- },
- template: '#child'
- }
- const Parent = {
- template: '#parent',
- data () {
- return {
- msg: 'hello msg'
- }
- },
- components: {
- MyChild: Child
- }
- }
- // 谁用谁注册
- new Vue({
- el: '#app',
- components: {
- MyParent: Parent
- }
- })
- </script>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>08 父组件给子组件传值3</title>
- </head>
- <body>
- <div id="app">
- <my-parent></my-parent>
- </div>
- </body>
- <template id="parent">
- <div>
- <h1>父组件</h1>
- <!-- 1.父组件在调用子组件的地方,添加自定义的属性 -->
- <!-- 如果自定义的属性的值是变量,boolean类型,number类型,对象,数组,
- null或者undefined,需要使用绑定属性 -->
- <my-child :msg="msg" :flag="true" :num="100" :obj="{a:1}" :arr="[1, 2, 3]"></my-child>
- <my-child :msg="msg" :flag="true" :arr="[4, 5, 6]"></my-child>
- </div>
- </template>
- <template id="child">
- <div>
- <h3>子组件</h3>
- {{ msg }} - {{ flag }} -- {{ num }} -- {{ obj }} -- {{ arr }}
- </div>
- </template>
- <script src="lib/vue.js"></script>
- <script>
- // 在子组件定义的地方,添加props选项,props可以有两大类的使用方法
- // 数组
- // 数组的元素就是自定义的属性名,这样就可以通过自定义的属性名渲染子组件的数据
- // 对象,对象的写法又分为两大类写法
- // 对象的key值为自定义的属性名,value值为数据类型,这样就可以通过自定义的属性名渲染子组件的数据
- // 对象的key值为自定义的属性名,value值为新的一个对象
- // 对象的key值可以为type,value值则为数据类型
- // 对象的key值可以为default,value值则为属性的默认值(如果默认值是对象和数组,需要使用函数返回他们)
- // 对象的key值可以为 required,value值则表示该属性是必须传递的数据
- // 对象的key值可以为 validator, value值则表示可以对该属性进行验证
- const Child = {
- // props: ['msg', 'flag', 'num', 'obj', 'arr'],
- // props: {
- // msg: String,
- // flag: Boolean,
- // num: Number,
- // obj: Object,
- // arr: Array
- // },
- props: {
- msg: {
- type: String
- },
- flag: Boolean,
- num: {
- type: Number,
- default: 1,
- validator (val) {
- if (val > 10) {
- console.log('ok')
- } else {
- console.log('fail')
- }
-
- return val > 10
- }
- },
- obj: {
- type: Object,
- default () { return { a: 10000 }}
- },
- arr: {
- type: Array,
- required: true
- }
- },
- template: '#child'
- }
- const Parent = {
- template: '#parent',
- data () {
- return {
- msg: 'hello msg'
- }
- },
- components: {
- MyChild: Child
- }
- }
- // 谁用谁注册
- new Vue({
- el: '#app',
- components: {
- MyParent: Parent
- }
- })
- </script>
- </html>
自己整理话术:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>09 子组件给父组件传值</title>
- </head>
- <body>
- <div id="app">
- <my-parent></my-parent>
- </div>
- </body>
- <template id="parent">
- <div>
- <h1>父组件</h1>
- <!-- 父组件在调用子组件的地方,绑定一个自定义的事件
- 这个事件的实现由父组件负责,事件的默认参数就是子组件传递给父组件的值
- -->
- <my-child @my-event="getChildData"></my-child>
- </div>
- </template>
- <template id="child">
- <div>
- <h3>子组件</h3>
- <button @click="sendData">发送数据</button>
- </div>
- </template>
- <script src="lib/vue.js"></script>
- <script>
- const Child = {
- template: '#child',
- // 在子组件的某一个事件内部,通过this.$emit('自定义的事件名', 参数)完成传值
- methods: {
- sendData () {
- this.$emit('my-event', 10000)
- }
- }
- }
- const Parent = {
- template: '#parent',
- components: {
- MyChild: Child
- },
- methods: {
- getChildData (val) {
- console.log(val)
- }
- }
- }
- // 谁用谁注册
- new Vue({
- el: '#app',
- components: {
- MyParent: Parent
- }
- })
- </script>
- </html>
EventBus又被称之为中央事件总线
在Vue中通过单独的 事件中心 来管理非 父子关系 组件(兄弟)间的通信:
公众号千千万,都得先关注公众号,一旦发送消息,就可以收到消息 - 专注交流一百年
核心步骤
- const eventBus = new Vue()
- eventBus.$emit('自定义事件名',传递的数据)
- eventBus.$on('自定义事件名'[,callback])
- eventBus.$off('自定义事件名')
先建立事件中心 const bus = new Vue()
在需要接受数据的地方先监听自定义事件以及接受数据的回调函数bus.$on('my-event', (data) => {})
在需要传递数据的地方提交 自定义事件以及参数 bus.$emit('my-event', params)
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>10 非父子组件传值 - 兄弟组件之间传值</title>
- </head>
- <body>
- <div id="app">
- <my-content></my-content>
- <my-footer></my-footer>
- </div>
- </body>
- <template id="content">
- <div>
- {{ name }}
- </div>
- </template>
- <template id="footer">
- <ul>
- <li @click="changeName('首页')">首页</li>
- <li @click="changeName('分类')">分类</li>
- <li @click="changeName('购物车')">购物车</li>
- <li @click="changeName('我的')">我的</li>
- </ul>
- </template>
- <script src="lib/vue.js"></script>
- <script>
- // 注意:先监听才能接收到数据(得先关注公众号才能接收到消息)
- // 创建vue的实例作为中央事件总线 bus
- const bus = new Vue()
- const Content = {
- template: '#content',
- data () {
- return {
- name: '首页'
- }
- },
- mounted () {
- // 在需要监听数据的地方,通过bus.$on('自定义的事件',回调函数)用来接收数据,
- // 回调函数参数即为兄弟组件传递过来的值
- bus.$on('my-event', (val) => {
- this.name = val
- })
- }
- }
- const Footer = {
- template: '#footer',
- methods: {
- changeName (val) {
- // 在需要传值的地方,通过bus.$emit('自定义的事件',参数)完成传值操作
- bus.$emit('my-event', val)
- }
- }
- }
- // 谁用谁注册
- new Vue({
- el: '#app',
- components: {
- MyContent: Content,
- MyFooter: Footer
- }
- })
- </script>
- </html>
ref 属性被用来给元素或子组件注册引用信息,引用信息将会注册在父组件的 $refs 对象上。
如果在普通的 DOM 元素上使用 ref 属性,则引用指向的就是 DOM 元素;
如果 ref 属性用在子组件上,引用就指向子组件实例。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>11 Ref</title>
- </head>
- <body>
- <div id="app">
- <!-- ref属性既可以使用到 DOM元素,也可以使用到组件
- DOM元素 this.$refs 获取到DOM组成的对象
- 组件上 this.$refs 获取到相关子组件组成的对象
- -->
- <div ref="smile">😂</div>
- <div ref="cry">😭</div>
- <my-content ref="com"></my-content>
- <button @click="getDOMData">获取值</button>
- </div>
- </body>
- <template id="content">
- <div>
- {{ name }}
- </div>
- </template>
-
- <script src="lib/vue.js"></script>
- <script>
- const Content = {
- template: '#content',
- data () {
- return {
- name: '首页'
- }
- }
- }
-
- // 我是***他父亲
- new Vue({
- el: '#app',
- components: {
- MyContent: Content
- },
- methods: {
- getDOMData () {
- console.log(this.$refs) // 获取到所有的ref属性
- console.log(this.$refs.smile.innerHTML)
- console.log(this.$refs.cry.innerHTML)
- console.log(this.$refs.com) // 获取到子组件的实例
- console.log(this.$refs.com.name) // 直接在父组件中调用子组件中属性和方法
- }
- },
- })
- </script>
- </html>
注意:
ref 属性这种获取子元素/组件的方式虽然写法简单,容易上手,但是其由于权限过于开放,不推荐使用,有安全问题。(不仅可以获取值,还可以获取其他所有的元素/组件的数据,甚至可以修改这些数据。)
可以通过$parent直接获取到父组件的实例
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>12 parent</title>
- </head>
- <body>
- <div id="app">
- <!-- $parent可以直接在子组件中获取到父组件的实例的属性和方法
- -->
- <div ref="smile">😂</div>
- <div ref="cry">😭</div>
- <my-content ref="com"></my-content>
- </div>
- </body>
- <template id="content">
- <div>
- {{ name }}
- {{ $parent.msg }}
- <button @click="getData">通过parent获取父组件的数据</button>
- </div>
- </template>
-
- <script src="lib/vue.js"></script>
- <script>
- const Content = {
- template: '#content',
- data () {
- return {
- name: '首页'
- }
- },
- methods: {
- getData () {
- console.log(this.$parent)
- console.log(this.$parent.msg)
- this.$parent.fn()
- }
- },
- }
-
- // 我爸是李刚
- new Vue({
- el: '#app',
- components: {
- MyContent: Content
- },
- data: {
- msg: 'hello msg'
- },
- methods: {
- fn () {
- console.log(110)
- }
- },
- })
- </script>
- </html>
如果需要在组件的结构中访问父组件的属性和方法,不需要添加this,但是也可以添加
通过 provide + inject 完成传值
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>01 祖先组件传值给后代组件</title>
- </head>
- <body>
- <div id="app">
- <com></com>
- </div>
- </body>
- <template id="com">
- <div>
- 后代组件 - {{ theme }}
- </div>
- </template>
- <script src="lib/vue.js"></script>
- <script>
- const Com = {
- inject: ['theme'],
- template: '#com'
- }
- new Vue({
- el: '#app',
- data: {},
- components: {
- Com
- },
- provide: {
- theme: 'dark'
- }
- })
- </script>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>02 祖先组件传值给后代组件</title>
- </head>
- <body>
- <div id="app">
- <com></com>
- </div>
- </body>
- <template id="com">
- <div>
- 后代组件 - {{ theme }} -- {{ message }}
- </div>
- </template>
- <script src="lib/vue.js"></script>
- <script>
- const Com = {
- inject: ['theme', 'message'], // inject 还可以是 对象,类比 props中的对象
- template: '#com'
- }
- new Vue({
- el: '#app',
- data: {
- msg: 'hello msg'
- },
- components: {
- Com
- },
- provide () { // 如果数据中包含组件的变量,使用 返回对象的 provide 函数
- return {
- theme: 'light',
- message: this.msg
- }
- }
- })
- // 状态管理器
- </script>
- </html>
provide 和 inject 主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。
通过分支条件判断实现选项卡切换
- <body>
- <div id="app">
- <home v-if="type==='home'"></home>
- <kind v-else-if="type==='kind'"></kind>
- <cart v-else-if="type==='cart'"></cart>
- <user v-else></user>
- <ul>
- <li @click="goPage('home')">首页</li>
- <li @click="goPage('kind')">分类</li>
- <li @click="goPage('cart')">购物车</li>
- <li @click="goPage('user')">我的</li>
- </ul>
- </div>
- </body>
- <script src="lib/vue.js"></script>
- <script>
- const Home = { template: '<div>首页组件</div>' }
- const Kind = { template: '<div>分类组件</div>' }
- const Cart = { template: '<div>购物车组件</div>' }
- const User = { template: '<div>我的组件</div>' }
- new Vue({
- el: '#app',
- data: {
- type: 'home'
- },
- components: {
- home: Home,
- kind: Kind,
- cart: Cart,
- user: User,
- },
- methods: {
- goPage (type) {
- this.type = type
- }
- }
- })
- </script>
通过使用保留的 <component> 元素,动态地绑定到它的 is 特性,我们让多个组件可以使用同一个挂载点,并动态切换。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>04 动态组件</title>
- </head>
- <body>
- <div id="app">
- <!-- <home v-if="type==='home'"></home>
- <kind v-else-if="type==='kind'"></kind>
- <cart v-else-if="type==='cart'"></cart>
- <user v-else></user> -->
- <!-- type 的值是组件的名称 --- 组件标签 -->
- <component :is="type"></component>
- <ul>
- <li @click="goPage('home')">首页</li>
- <li @click="goPage('kind')">分类</li>
- <li @click="goPage('cart')">购物车</li>
- <li @click="goPage('user')">我的</li>
- </ul>
- </div>
- </body>
- <script src="lib/vue.js"></script>
- <script>
- const Home = { template: '<div>首页组件</div>' }
- const Kind = { template: '<div>分类组件</div>' }
- const Cart = { template: '<div>购物车组件</div>' }
- const User = { template: '<div>我的组件</div>' }
- new Vue({
- el: '#app',
- data: {
- type: 'home'
- },
- components: {
- home: Home,
- kind: Kind,
- cart: Cart,
- user: User,
- },
- methods: {
- goPage (type) {
- this.type = type
- }
- }
- })
- </script>
- </html>
思考:如果每个组件中都有一个输入框,点击切换时输入不同的内容,然后再切换,查看效果
keep-alive的作用:
keep-alive 可以将已经切换出去的非活跃组件保留在内存中。如果把切换出去的组件保留在内存中,可以保留它的状态,避免重新渲染。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>05 动态组件</title>
- </head>
- <body>
- <div id="app">
- <!-- type 的值是组件的名称 --- 组件标签 -->
- <!-- 默认情况下,动态组件在切换时会触发组件的销毁和重建 -->
- <!-- 可以使用 keep-alive 组件解决这个问题
- 可以缓存组件的状态,避免了组件的销毁和重建
- -->
- <keep-alive>
- <component :is="type"></component>
- </keep-alive>
- <ul>
- <li @click="goPage('home')">首页</li>
- <li @click="goPage('kind')">分类</li>
- <li @click="goPage('cart')">购物车</li>
- <li @click="goPage('user')">我的</li>
- </ul>
- </div>
- </body>
- <script src="lib/vue.js"></script>
- <script>
- const Home = {
- template: '<div>首页组件 <input type="text"/></div>',
- mounted () { console.log('home mounted') },
- destroyed() {console.log('home destroyed')},
- }
- const Kind = {
- template: '<div>分类组件 <input type="text"/></div>',
- mounted () { console.log('kind mounted') },
- destroyed() {console.log('kind destroyed')}
- }
- const Cart = {
- template: '<div>购物车组件 <input type="text"/></div>',
- mounted () { console.log('cart mounted') },
- destroyed() {console.log('cart destroyed')}
- }
- const User = {
- template: '<div>我的组件 <input type="text"/></div>',
- mounted () { console.log('user mounted') },
- destroyed() {console.log('user destroyed')}
- }
- new Vue({
- el: '#app',
- data: {
- type: 'home'
- },
- components: {
- home: Home,
- kind: Kind,
- cart: Cart,
- user: User,
- },
- methods: {
- goPage (type) {
- this.type = type
- }
- }
- })
- </script>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>06 动态组件钩子</title>
- </head>
- <body>
- <div id="app">
- <!-- type 的值是组件的名称 --- 组件标签 -->
- <!-- 默认情况下,动态组件在切换时会触发组件的销毁和重建 -->
- <!-- 可以使用 keep-alive 组件解决这个问题
- 可以缓存组件的状态,避免了组件的销毁和重建
- -->
- <!--
- 使用了keep-alive 数据请求在activated,否则可以在 created / mounted
- activated 请求数据
- deactivated
- -->
- <keep-alive>
- <component :is="type"></component>
- </keep-alive>
- <ul>
- <li @click="goPage('home')">首页</li>
- <li @click="goPage('kind')">分类</li>
- <li @click="goPage('cart')">购物车</li>
- <li @click="goPage('user')">我的</li>
- </ul>
- </div>
- </body>
- <script src="lib/vue.js"></script>
- <script>
- const Home = {
- template: '<div>首页组件 <input type="text"/></div>',
- mounted () { console.log('home mounted') },
- destroyed() {console.log('home destroyed')},
- activated() { console.log('home activated')},
- deactivated() {console.log('home deactivated')},
- }
- const Kind = {
- template: '<div>分类组件 <input type="text"/></div>',
- mounted () { console.log('kind mounted') },
- destroyed() {console.log('kind destroyed')},
- activated() { console.log('kind activated')},
- deactivated() {console.log('kind deactivated')},
- }
- const Cart = {
- template: '<div>购物车组件 <input type="text"/></div>',
- mounted () { console.log('cart mounted') },
- destroyed() {console.log('cart destroyed')},
- activated() { console.log('cart activated')},
- deactivated() {console.log('cart deactivated')},
- }
- const User = {
- template: '<div>我的组件 <input type="text"/></div>',
- mounted () { console.log('user mounted') },
- destroyed() {console.log('user destroyed')},
- activated() { console.log('user activated')},
- deactivated() {console.log('user deactivated')},
- }
- new Vue({
- el: '#app',
- data: {
- type: 'home'
- },
- components: {
- home: Home,
- kind: Kind,
- cart: Cart,
- user: User,
- },
- methods: {
- goPage (type) {
- this.type = type
- }
- }
- })
- </script>
- </html>
思考:使用keep-alive 看似保留了所有的状态,但是如果某一个组件不要保留状态呢
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>06 动态组件钩子</title>
- </head>
- <body>
- <div id="app">
- <!-- 可以给 keep-alive 添加属性,表示需要缓存的组件
- include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
- exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
- max - 数字。最多可以缓存多少组件实例。
- -->
- <!-- 逗号分隔字符串、正则表达式或一个数组来 -->
- <!-- 匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 -->
-
- <!-- 逗号分隔字符串,逗号后不要加空格 -->
- <!-- <keep-alive include="ahome,akind">
- <component :is="type"></component>
- </keep-alive> -->
-
- <!-- 正则表达式: 绑定属性 -->
- <!-- <keep-alive :include="/ahome|akind|auser/">
- <component :is="type"></component>
- </keep-alive> -->
-
- <!-- 数组 -->
- <keep-alive :include="['acart', 'user']">
- <component :is="type"></component>
- </keep-alive>
- <!-- 匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 -->
- <ul>
- <li @click="goPage('home')">首页</li>
- <li @click="goPage('kind')">分类</li>
- <li @click="goPage('cart')">购物车</li>
- <li @click="goPage('user')">我的</li>
- </ul>
- </div>
- </body>
- <script src="lib/vue.js"></script>
- <script>
- const Home = {
- name: 'ahome',
- template: '<div>首页组件 <input type="text"/></div>',
- mounted () { console.log('home mounted') },
- destroyed() {console.log('home destroyed')},
- activated() { console.log('home activated')},
- deactivated() {console.log('home deactivated')},
- }
- const Kind = {
- name: 'akind',
- template: '<div>分类组件 <input type="text"/></div>',
- mounted () { console.log('kind mounted') },
- destroyed() {console.log('kind destroyed')},
- activated() { console.log('kind activated')},
- deactivated() {console.log('kind deactivated')},
- }
- const Cart = {
- name: 'acart',
- template: '<div>购物车组件 <input type="text"/></div>',
- mounted () { console.log('cart mounted') },
- destroyed() {console.log('cart destroyed')},
- activated() { console.log('cart activated')},
- deactivated() {console.log('cart deactivated')},
- }
- const User = {
- // name: 'auser',
- template: '<div>我的组件 <input type="text"/></div>',
- mounted () { console.log('user mounted') },
- destroyed() {console.log('user destroyed')},
- activated() { console.log('user activated')},
- deactivated() {console.log('user deactivated')},
- }
- new Vue({
- el: '#app',
- data: {
- type: 'home'
- },
- components: {
- home: Home,
- kind: Kind,
- cart: Cart,
- user: User,
- },
- methods: {
- goPage (type) {
- this.type = type
- }
- }
- })
- </script>
- </html>
案例:使用动态组件实现简易的步骤向导效果
- <body>
- <div id="app">
- <button @click='change("step1")'>第一步</button>
- <button @click='change("step2")'>第二步</button>
- <button @click='change("step3")'>第三步</button>
- <keep-alive>
- <component :is="name"></component>
- </keep-alive>
- </div>
- </body>
- <script src="lib/vue.js"></script>
- <script>
- var step1 = {
- template: '<div>这是第一步的操作</div>'
- }
- var step2 = {
- template: '<div>这是第二步的操作</div>'
- }
- var step3 = {
- template: '<div>这是第三步的操作</div>'
- }
- var vm = new Vue({
- el: "#app",
- data: {
- name: "step2",
- },
- components: {
- step1,
- step2,
- step3
- },
- methods: {
- change:function(name){
- this.name = name
- }
- }
- })
- </script>
组件的最大特性就是 重用 ,而用好插槽能大大提高组件的可重用能力。
插槽的作用:父组件向子组件传递内容。
通俗的来讲,插槽无非就是在 子组件 中挖个坑,坑里面放什么东西由 父组件 决定。
插槽类型有:
匿名插槽一般就是使用单个插槽
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>06 动态组件钩子</title>
- </head>
- <body>
- <div id="app">
- <!-- 默认情况下,调用组件时,直接写到内部的内容是不会显示的 -->
- <!-- 如果想要显示,就在定义组件模版时,添加 slot 标签 -->
- <!-- 插槽:内容分发 -->
- <!-- 组件内部的代码显示还是不显示,在哪里显示,如何显示,这就是内容分发所干的活 -->
- <my-header>
- <ul>
- <li>logo</li>
- <li>搜索</li>
- <li>我的</li>
- </ul>
- </my-header>
- <my-header>
- <ul>
- <li>返回</li>
- <li>标题</li>
- <li></li>
- </ul>
- </my-header>
- </div>
- </body>
- <template id="header">
- <header>
- <slot></slot>
- <hr />
- <slot></slot>
- </header>
- </template>
- <script src="lib/vue.js"></script>
- <script>
- const Header = {
- template: '#header'
- }
- new Vue({
- el: '#app',
- components: {
- MyHeader: Header
- }
- })
- </script>
- </html>
注意:子组件的 slot 标签中允许书写内容,当父组件不往子组件传递内容时, slot 中的内容才会被展示出来。
slot 元素可以用一个特殊的特性 name 来进一步配置如何分发内容。多个插槽可以有不同的名字,具名插槽将匹配内容片段中有对应 slot 特性的元素。
上中下 形式网页布局示例代码
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>06 动态组件钩子</title>
- </head>
- <body>
- <div id="app">
- <!-- 默认情况下,调用组件时,直接写到内部的内容是不会显示的 -->
- <!-- 如果想要显示,就在定义组件模版时,添加 slot 标签 -->
- <!-- 插槽:内容分发 -->
- <!-- 组件内部的代码显示还是不显示,在哪里显示,如何显示,这就是内容分发所干的活 -->
- <!-- 具名插槽:调用组件时,有多个代码需要显示到不同的位置
- 定义模版添加slot 并配有 name 属性,调用组件时添加 slot属性指向之前的name的值
- -->
- <my-header>
- <div slot="left">logo</div>
- <div>搜索</div>
- <div slot="right">我的</div>
- </my-header>
- <my-header>
- <div slot="left">返回</div>
- <div>标题</div>
- <div slot="right"></div>
- </my-header>
- </div>
- </body>
- <template id="header">
- <header>
- <slot name="left"></slot>
- <!-- 默认的name属性为 default -->
- <slot></slot>
- <slot name="right"></slot>
- </header>
- </template>
- <script src="lib/vue.js"></script>
- <script>
- const Header = {
- template: '#header'
- }
- new Vue({
- el: '#app',
- components: {
- MyHeader: Header
- }
- })
- </script>
- </html>
具名插槽存在的意义就是为了解决在单个页面中同时使用多个插槽。
应用场景:父组件对子组件的内容进行加工处理
作用域插槽是一种特殊类型的插槽,作用域插槽会绑定了一套数据,父组件可以拿这些数据来用,于是,情况就变成了这样:样式父组件说了算,但父组件中内容可以显示子组件插槽绑定的数据。
自 2.6.0 起有所更新。已废弃的使用 slot-scope attribute 的语法在这里。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>10 作用域插槽</title>
- </head>
- <body>
- <div id="app">
- <my-header>
- <div slot-scope="props">
- <h1>1111</h1>
- {{ props.test }}
- </div>
- </my-header>
- </div>
- </body>
- <template id="header">
- <header>
- <slot test = "测试"></slot>
- </header>
- </template>
- <script src="lib/vue.js"></script>
- <script>
- const Header = {
- template: '#header'
- }
- new Vue({
- el: '#app',
- components: {
- MyHeader: Header
- }
- })
- </script>
- </html>
https://cn.vuejs.org/v2/api/#v-slot
定义组件
挖个坑 <slot></slot>,如果要使用具名插槽<slot name="test"></slot>
调用组件时<template v-slot></template>或者 <template v-slot="test"></template>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>11 v-slot</title>
- </head>
- <body>
- <div id="app">
- <my-header>
- <!-- <div slot="left">logo</div>
- <div>搜索</div>
- <div slot="right">我的</div> -->
- <template v-slot:left>
- <div>logo</div>
- </template>
- <div>搜索</div>
- <template v-slot:right>
- <div>我的</div>
- </template>
- </my-header>
- </div>
- </body>
- <template id="header">
- <header>
- <slot name="left"></slot>
- <!-- 默认的name属性为 default -->
- <slot></slot>
- <slot name="right"></slot>
- </header>
- </template>
- <script src="lib/vue.js"></script>
- <script>
- const Header = {
- template: '#header'
- }
- new Vue({
- el: '#app',
- components: {
- MyHeader: Header
- }
- })
- </script>
- </html>