
参考资料:
1. 组件
组件,扩展HTML元素,封装可重用的代码(让组件可以和html标签一样去使用)。vue最强大的功能之一

1.1 创建组件
全局组件:Vue.component(tagName, options)
1 2 3 4 5 6 7 8 9 10 11 12
| <div id="app"> <navbar></navbar> </div> <script> Vue.component('navbar', { // 全局组件 navbar template: '<h1>自定义组件!</h1>', methods: { handleXxx(){} ... } }) new Vue({ el: '#app' }) </script>
|
局部组件:new Vue({ el:..., components: {xxx:Child} })
- 如果写在 Vue.component 中,就属于他自己的子组件,只能在自己的内部使用
- 只能在当前初始化实例中使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <div id="app"> <navbar></navbar> </div> <script> var Child = { template: '<h1>自定义组件!</h1>' } new Vue({ el: '#app', components: { 'navbar': Child // <navbar> 将只在当前实例可用 } }) </script>
|
组件创建命名参考,如果使用 - 短杠分隔,那么就可以使用驼峰方式初始化组件。eg:
1 2 3 4 5
| <film-detail></film-detail>
Vue.component("filmDetail", { template: `<div></div>` })
|
1.2 父子组件通信
1.2.1 父传子
子组件标签上通过:自定义属性去绑定父组件的属性值
- 子组件的
props属性通过该自定义属性来接受父组件的属性值
- props 校验类型 type 可以是下面原生构造器:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
- props 是否能修改?父组件传给你的属性,只有父组件可以重新传,但
不允许子组件随意修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <div id="box"> <div style="background: yellow;">根组件标题</div> <navbar myname="电影" :myright="false" :my_parent="parent"></navbar> <navbar myname="影院" :myright="true" :my_parent="parent"></navbar> </div> <script> Vue.component("navbar", { props: { myname: { type: String, default: "" }, myright: { type: Boolean, default: true }, my_parent: { type: String, default: "" } }, template: `<div> <button>left</button> <span>{{myname}}-{{my_parent}}</span> <button v-show="myright">Q</button> </div>` }) new Vue({ el: "#box", data: { parent: "11111111111" } }) </script>
|
效果:

1.2.2 子传父
子组件1标签上通过@自定义事件去绑定父组件的事件方法
- 子组件1的 methods 点击事件中操作子组件自身的
this.$emit("自定义事件", 参数) 触发执行绑定父组件的事件方法
this.$emit(eventName, args...) 该方法为固定写法,且支持传参(也可以不传)
- 子组件2因为绑定了对应的变量,如 v-show,所以会跟随更新渲染
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <div id="box"> <navbar @my_event="handleEvent"></navbar> <sidebar v-show="isShow"></sidebar> </div> <script> Vue.component("navbar", { template: ` <div> <button @click="handleClick()">点击</button>-导航栏 </div>`, methods: { handleClick() { console.log("子传父") this.$emit("my_event", 100) } } }) Vue.component("sidebar", { template: ` <div> <ul> <li>111</li> <li>222</li> <li>333</li> <li>444</li> <li>555</li> </ul> </div>` }) new Vue({ el: "#box", data: { isShow: true }, methods: { handleEvent(arg) { console.log("父组件定义的事件:", arg) this.isShow = !this.isShow } } }) </script>
|
效果:类似抽屉效果

1.2.3 子传子(兄弟通信-中间人模式)
父组件,称之为 中间人。
- 组合子传父、父传子的两个通信方式,即 子1传父、父传子2 来实现兄弟通信
示例:(列表与详情是两个同级的子组件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="./lib/vue.js"></script> <style> .item{ overflow: hidden; padding: 5px; width: 300px; border: 1px solid gray; } .item img{ float: left; width: 100px; } .filmInfo{ max-width: 300px; min-height: 200px; position: fixed; right: 0; top: 50px; background: yellow; } </style> </head> <body> <div id="box"> <button @click="handleClick">ajax</button> <film-item v-for="item in datalist" :key="item.filmId" :item="item" @event="handleEvent"></film-item> <film-detail :detail-data="detailData"></film-detail> </div> <script> Vue.component("filmItem", { props: { item: Object, default: {} }, template: ` <div class="item"> <img :src="item.poster"> {{item.name}} <div> <button @click="handleClick">详情</button> </div> </div> `, methods: { handleClick() { console.log(this.item.synopsis) this.$emit("event", this.item.synopsis) } } })
Vue.component("filmDetail", { props: { detailData: { type: String, default: "没有简介" } }, template: ` <div class="filmInfo"> {{detailData}} </div> ` })
new Vue({ el: "#box", data: { datalist: [], detailData: "" }, methods: { handleClick() { fetch("./json/maizuo.json") .then(res => res.json()) .then(res => { console.log(res.data.films) this.datalist = res.data.films }) }, handleEvent(data) { console.log("父组件自定义事件", data) this.detailData = data } } }) </script> </body> </html>
|
效果:

1.2.4 中央事件总线 bus
组件之间通信,关系变得复杂时,方案有二:
bus 中央事件总线 var bus = new Vue(),原理是 订阅发布模式
bus.$on() 监听事件,接收数据的组件中使用(记得定义接收数据的属性)
bus.$emit() 触发事件,发送数据的组件中使用
vuex 状态管理
作用:实现不同组件之间进行通信(非父子关系)。
原理:$bus就是vue原型上添加的一个公共的vue实例,用于存储、监听以及触发事件。
实现步骤:
- 注册事件总线
1 2 3
| var bus = new Vue()
Vue.prototype.bus = new Vue();
|
- 在需要发送信息的组件中触发事件
1 2
| bus.$emit("eventname") bus.$emit("eventname",params)
|
- 在需要接收信息的组件中监听事件,需要写在
mounted() 函数中
1 2 3 4 5 6 7
| mounted() { bus.$on("eventname",(params)=>{...}); bus.$on('eventname', () => {...}); }
|
- 在接收信息的组件
beforeDestroy事件中销毁接收事件
1 2 3
| beforeDestroy() { this.bus.$off('eventname'); }
|
示例:(改造列表与详情是两个组件的情况,模拟组件之间通信)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="./lib/vue.js"></script> <style> .item{ overflow: hidden; padding: 5px; width: 300px; border: 1px solid gray; } .item img{ float: left; width: 100px; } .filmInfo{ max-width: 300px; min-height: 200px; position: fixed; right: 0; top: 50px; background: yellow; } </style> </head> <body> <div id="box"> <button @click="handleClick">ajax</button> <film-item v-for="item in datalist" :key="item.filmId" :item="item" @event="handleEvent"></film-item> <film-detail :detail-data="detailData"></film-detail> </div> <script> var bus = new Vue()
Vue.component("filmItem", { props: { item: Object, default: {} }, template: ` <div class="item"> <img :src="item.poster"> {{item.name}} <div> <button @click="handleClick">详情</button> </div> </div> `, methods: { handleClick() { bus.$emit("event", this.item.synopsis) } } })
Vue.component("filmDetail", { data() { return { info: "" } }, template: ` <div class="filmInfo"> {{info}} </div> `, mounted() { bus.$on("event", (params) => { console.log("$on:", params) this.info = params }) } })
new Vue({ el: "#box", data: { datalist: [], detailData: "" }, methods: { handleClick() { fetch("./json/maizuo.json") .then(res => res.json()) .then(res => { console.log(res.data.films) this.datalist = res.data.films }) }, handleEvent(data) { console.log("父组件自定义事件", data) this.detailData = data } } }) </script> </body> </html>
|
效果一样。
扩展:v-once
v-once 有什么用?
渲染普通的HTML元素在VUE中是非常快速的,有时组件内包含了大量的静态内容,这种情况下可以在根元素上添加 v-once 属性,以确保这些内容只计算一次然后就被缓存起来了。
如:
1 2 3
| Vue.component("terms-of-service", { template: `<div v-once> ... </div>` })
|
1.3 模版引用 ref
ref 绑定模版引用,它允许我们在一个特定的 DOM 元素或子组件实例被挂载后,获得对它的直接引用。
this.$refs 获取引用对象
- 绑定 dom 节点,拿到的就是 dom对象
- 绑定 组件,拿到的就是 组件对象
注意:该引用如果被重新赋值,就打破了数据通信流程,导致数据流转混乱。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <div id="box"> <input type="text" ref="myText"> <input type="password" ref="myPassword"> <button @click="handleAdd">add</button> <child ref="myChild"></child> </div> <script> Vue.component("child", { data() { return { myName: "jerry11111" } }, template: `<div>child-{{myName}}</div>` })
new Vue({ el: "#box", methods: { handleAdd() { console.log(this.$refs.myChild.myName) this.$refs.myChild.myName = "tom222222" console.log(this.$refs.myChild.myName) } } }) </script>
|
1.4 动态组件
<component is="xxx"></component> vue内置的标签,可以作为一个动态组件使用。
- 如果需要保存该动态组件,如输入框中的已输入的值,则使用
<keep-alive></keep-alive>包裹
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <div id="box"> <button @click="which='first'">组件1</button> <button @click="which='second'">组件2</button> <button @click="which='third'">组件3</button> <component :is="which"></component> </div> <script> Vue.component("first", { template: `<div>111111</div>` }) Vue.component("second", { template: `<div>222222</div>` }) Vue.component("third", { template: `<div>333333</div>` })
new Vue({ el: "#box", data: { which: "first" } }) </script>
|
1.5 插槽 slot
<slot></slot> 或 v-slot: 或 # 插槽,也叫内容分发。 扩展组件的能力,提高组件的复用性。会将对应的内容直接进行替换。
- 单个插槽,就写标签即可
<slot></slot>
- 具名插槽,给属性 name,即
<slot name="xxx"></slot>
- 新版slot写法,将
v-slot:name 简写为 #name,该写法只能写在 <template> 标签上
内容分发:混合父组件的内容与子组件自己的模版。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <div id="box"> <child> <template v-slot:a> <div>aaaaaaaaaaaaa</div> </template> <template #b> <div>bbbbbbbbbbbbb</div> </template> </child> </div> <script> Vue.component("child", { template: ` <div> <slot name="a"></slot> <slot name="b"></slot> </div> ` }) new Vue({ el: "#box" }) </script>
|
旧版插槽 slot:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <div id="box"> <div>11111</div> <div>22222</div> <div slot="a">aaaaa</div> <div slot="b">bbbbb</div> </div> <script> Vue.component("child", { template: ` <div> <slot></slot> <slot name="a"></slot> <slot name="b"></slot> </div> ` }) new Vue({ el: "#box" }) </script>
|
1.6 过渡动画
transition 标签包裹需要加动画的内容。参考资料:https://www.runoob.com/vue2/vue-transitions.html
<transition name="jerry" appear mode="in-out"> 内部默认只生效1个元素(可用div包裹为1个) </transition>
name=”jerry” 其中的 jerry是动画 class 名称的前缀,会被拆解匹配为
- .jerry
-enter-active {动画效果CSS样式}
- .jerry
-leave-active {动画效果CSS样式}
appear 属性是设置初始元素过渡动画,让页面刷新时初始动画也能生效
mode 可以设置为 in-out 或者 out-in,意为 先进后出动画,或者 先出后进动画
当有相同标签名的元素切换时,通过 key 属性设置唯一值来标记它们,配合 v-if 和 v-else 在动画中切换内容
- diff算法带来的效果:
- 如果key一样就复用,如果key不一样就会删除并创建新的对比
- 如果标签不一样,也会删除dom节点重新创建
<transition-group name="jerry"></transition-group> 用于多个列表的过渡,列表元素需要设置唯一的key值(内部还是dom的diff算法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="./lib/vue.js"></script> <style> .jerry-enter-active { animation: aaa 1s; }
.jerry-leave-active { animation: aaa 1s reverse; }
@keyframes aaa { 0% { opacity: 0; transform: translateX(100px); }
100% { opacity: 1; transform: translateX(0px); } } </style> </head>
<body> <div id="box"> <button @click="isShow = !isShow">change</button> <transition enter-active-class="jerry-enter-active" leave-active-class="jerry-leave-active"> <div v-show="isShow">hello, change</div> </transition>
<transition name="jerry" appear mode="out-in"> <div v-if="isShow" key="1">hello, jerry1</div> <div v-else key="2">hello, jerry2</div> </transition> </div> <script> new Vue({ el: "#box", data: { isShow: true } }) </script> </body>
</html>
|
效果:

案例:待办事项-列表过渡动画
1 2 3 4 5 6 7 8
| <ul v-show="dataList.length"> <transition-group name="jerry"> <li v-for="(item, index) in dataList" :key="item"> {{item}}-{{index}} <button @click="handleDel(index)">删除</button> </li> </transition-group> </ul>
|
案例:抽屉效果-过渡动画
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="./lib/vue.js"></script> <style> .jerry{ position: fixed; right: 0px; top: 0px; } .jerry-enter-active { animation: aaa 1s; }
.jerry-leave-active { animation: aaa 1s reverse; }
@keyframes aaa { 0% { opacity: 0; transform: translateX(100px); }
100% { opacity: 1; transform: translateX(0px); } } </style> </head>
<body> <div id="box"> <navbar> <button @click="isShow = !isShow">点击</button> </navbar> <sidebar v-show="isShow" mode="jerry"></sidebar> </div> <script> Vue.component("navbar", { template: ` <div> 导航栏-<slot></slot> </div>`, methods: { handleClick() { console.log("子传父") this.$emit("my_event", 100) } } }) Vue.component("sidebar", { props: { mode: { type: String, default: "" } }, template: ` <transition :name=mode> <div> <ul :class=mode> <li>111</li> <li>222</li> <li>333</li> <li>444</li> <li>555</li> </ul> </div> </transition> ` }) new Vue({ el: "#box", data: { isShow: false }, methods: { handleEvent(arg) { console.log("父组件定义的事件:", arg) this.isShow = !this.isShow } } }) </script> </body>
</html>
|
1.7 生命周期
四个阶段,八个钩子函数。创建 和 挂载,即前4个生命周期一个组件只会走一次。
①创建:
②挂载:
③更新:
④销毁:
生命周期图示vue2:

生命周期图示vue3:

示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| <div id="box"> {{myname}} - {{globalName}} <button @click=" myname = 'tom' ">更新</button> </div> <script> new Vue({ el: "#box", data: { myname: "jerry", user: {} }, beforeCreate() { console.log("beforeCreate ->", this.myname) }, created() { console.log("created ->", this.myname) this.myname = this.myname + "111111111" this.globalName = "this可以直接访问的属性值" this.user = localStorage.getItem("user") }, beforeMount() { console.log("beforeMount ->", this.$el) }, mounted() { console.log("mounted ->", this.$el) setTimeout(() => { this.datalist = ["aaa", "bbb", "ccc"] console.log("长度:", document.getElementsByTagName("li").length) }, 2000) }, beforeUpdate() { console.log("beforeUpdate ->", this.myname) }, updated() { console.log("updated ->", this.myname) }, beforeUnmount() { console.log("beforeUnmount ->") window.onresize = null }, unmounted() { console.log("unmounted ->") } }) </script>
|
案例:封装轮播swiper组件-vue2&3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel="stylesheet" href="../lib/swiper-bundle.min.css"> <script src="../lib/swiper-bundle.min.js"></script> <script src="../lib/vue.js"></script> <style> .swiper { width: 600px; height: 300px; } </style> </head>
<body> <div id="box"> <swiper v-if="datalist.length" :loop="true"> <swiper-item v-for="(item, index) in datalist" :key="item"> <img :src="item" /> </swiper-item> </swiper> </div>
<script> Vue.component("swiperItem", { template: ` <div class="swiper-slide"> <slot></slot> </div> ` })
Vue.component("swiper", { props: { loop: { type: Boolean, default: true } }, template: ` <div class="swiper"> <div class="swiper-wrapper"> <slot></slot> </div> <!-- 如果需要分页器 --> <div class="swiper-pagination"></div> </div> `, mounted() { console.log("mounted") new Swiper(".swiper", { loop: this.loop, pagination: { el: '.swiper-pagination', }, navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', }, autoplay: { delay: 2000, disableOnInteraction: false, }, }) }, destroyed() { console.log("destroyed") } })
new Vue({ el: "#box", data: { datalist: [] }, mounted() { setTimeout(() => { this.datalist = ["https://static.maizuo.com/pc/v5/usr/movie/71207b4b172609411d1b2e429ea08961.jpg?x-oss-process=image/quality,Q_70", "https://static.maizuo.com/pc/v5/usr/movie/53443bf08ac8f08d23e3fe35959a3240.jpg?x-oss-process=image/quality,Q_70", "https://static.maizuo.com/pc/v5/usr/movie/a9ce1fce280bd093b0bfab88dd05317a.jpg?x-oss-process=image/quality,Q_70"] }, 2000) } }) </script> </body>
</html>
|
vue3写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel="stylesheet" href="../lib/swiper-bundle.min.css"> <script src="../lib/swiper-bundle.min.js"></script> <script src="../lib/vue.global.js"></script> <style> .swiper { width: 600px; height: 300px; } </style> </head>
<body> <div id="box"> <swiper v-if="datalist.length" :loop="true"> <swiper-item v-for="(item, index) in datalist" :key="item"> <img :src="item" /> </swiper-item> </swiper> </div>
<script> var obj = { data() { return { datalist: [] } }, mounted() { setTimeout(() => { this.datalist = ["https://static.maizuo.com/pc/v5/usr/movie/71207b4b172609411d1b2e429ea08961.jpg?x-oss-process=image/quality,Q_70", "https://static.maizuo.com/pc/v5/usr/movie/53443bf08ac8f08d23e3fe35959a3240.jpg?x-oss-process=image/quality,Q_70", "https://static.maizuo.com/pc/v5/usr/movie/a9ce1fce280bd093b0bfab88dd05317a.jpg?x-oss-process=image/quality,Q_70"] }, 2000) } }
let app = Vue.createApp(obj) app.component("swiperItem", { template: ` <div class="swiper-slide"> <slot></slot> </div> ` }) app.component("swiper", { props: { loop: { type: Boolean, default: true } }, template: ` <div class="swiper"> <div class="swiper-wrapper"> <slot></slot> </div> <!-- 如果需要分页器 --> <div class="swiper-pagination"></div> </div> `, mounted() { console.log("mounted") new Swiper(".swiper", { loop: this.loop, pagination: { el: '.swiper-pagination', }, navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', }, autoplay: { delay: 2000, disableOnInteraction: false, }, }) }, destroyed() { console.log("destroyed") } }) app.mount("#box") </script> </body>
</html>
|
2. 指令
2.1 自定义指令
参考资料:https://www.runoob.com/vue2/vue-custom-directive.html
Vue.directive()自定义指令,对普通DOM元素进行底层操作。(对底层DOM的一种封装)
实际应用:通过指令知道什么时候dom创建完成,从而进行依赖dom库的初始化工作。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <div id="box"> <div v-hello>1111111111111</div> <div v-hello=" 'yellow' ">2222222222222</div> <div v-hello="whichColor">3333333333333</div> </div> <script> Vue.directive("hello", { inserted(el, binding) { console.log("inserted", el) el.style.background = "red" el.style.background = binding.value }, update(el, binding) { console.log("update") el.style.background = binding.value } }) var vm = new Vue({ el: "#box", data: { whichColor: "blue" } }) </script>
|
指令函数简写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <div id="box"> <div v-hello>1111111111111</div> <div v-hello=" 'yellow' ">2222222222222</div> <div v-hello="whichColor">3333333333333</div> </div> <script> Vue.directive("hello", (el, binding) => { console.log("创建或更新都会去执行", el) el.style.background = "red" el.style.background = binding.value }) var vm = new Vue({ el: "#box", data: { whichColor: "blue" } }) </script>
|
2.2 指令应用
轮播改造:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel="stylesheet" href="../lib/swiper-bundle.min.css"> <script src="../lib/swiper-bundle.min.js"></script> <script src="../lib/vue.js"></script> <style> .swiper { width: 600px; height: 300px; } </style> </head>
<body> <div id="box"> <header>导航</header> <div class="swiper"> <div class="swiper-wrapper"> <div class="swiper-slide" v-for="(item, index) in datalist" :key="item" v-swiper="{index: index, length: datalist.length}"> {{item}} </div> </div> <div class="swiper-pagination"></div>
<div class="swiper-button-prev"></div> <div class="swiper-button-next"></div> </div> <footer>底部内容</footer> </div>
<script> Vue.directive("swiper", { inserted(el, binding) { console.log("inserted", el, binding.value) let { index, length } = binding.value if (binding.value = length) { console.log("new Swiper") new Swiper(".swiper", { loop: true, pagination: { el: '.swiper-pagination', }, navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', }, autoplay: { delay: 2500, disableOnInteraction: false, }, }) } } })
new Vue({ el: "#box", data: { datalist: [] }, mounted() { setTimeout(() => { this.datalist = ["aaa", "bbb", "ccc"] }, 2000) }, }) </script> </body>
</html>
|
2.3 指令函数
bind: 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。但是可能发生在其子Vnode更新之前。通过比较更新前后的绑定值,可以忽略不必要的模板更新(详细的钩子函数参数见下)。
componentUpdated: 指令所在组件的VNode及其子VNode全部更新后调用。
unbind: 只调用一次, 指令与元素解绑时调用。
2.4 $nextTick
指令轮播黑魔法:$nextTick 能解决一些问题,但没有什么复用性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| <div id="box"> <header>导航</header> <div class="swiper"> <div class="swiper-wrapper"> <div class="swiper-slide" v-for="(item, index) in datalist" :key="item"> {{item}} </div> </div> <div class="swiper-pagination"></div>
<div class="swiper-button-prev"></div> <div class="swiper-button-next"></div>
</div> <footer>底部内容</footer> </div> <script> new Vue({ el: "#box", data: { datalist: [] }, mounted() { setTimeout(() => { this.datalist = ["aaa", "bbb", "ccc"]
this.$nextTick(() => { console.log("此处比updated执行的都晚,而且只执行一次")
new Swiper(".swiper", { loop: true, pagination: { el: '.swiper-pagination', }, navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', }, autoplay: { delay: 2500, disableOnInteraction: false, }, }) })
}, 2000) }, }) </script>
|