vue-重塑筋脉-组件
1.虚拟DOM与diff算法 key的作用
虚拟DOM是vue针对真实dom进行增加或删除的操作的时候,需要将上一个dom树删除再重新生成一个新的dom树,而这样是非常耗费资源并且繁琐的。所以vue就采用了这样一个方法解决了这个问题,首先,开发者需要将dom树的每个节点赋予一个key值,这个key必须是唯一的。而dom发生变化时,首先会通过diff算法,比较更新后的虚拟dom与真实的dom有什么不同,再通过相同key值不变的保留,变化的进行改变来达成增加或删除的操作。这样,没改变值的节点就不需要通过删除然后重新加上这样的操作来达成优化的目的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8n2IEJ6Y-1619966767359)(C:\Users\DSH\AppData\Roaming\Typora\typora-user-images\image-20210201145801104.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-loa2eHmj-1619966767362)(C:\Users\DSH\AppData\Roaming\Typora\typora-user-images\image-20210201145821224.png)]
Diff算法:首先会按照层级进行对比,查看层级是否相同,相同的话在进行key值对比,接着进行同组件对比。如果没有key值则直接进行同组件对比。值得注意的是,但不存在key值时,对于节点相同但内容不同的组件,vue为了使性能更佳,会把节点标签保留只修改内容。这样的影响是,当你有一个组件拥有入场或出场动画时,会因为之歌机制而使动画无法展示。但是有key值的话,vue就不会这么做,只会把vue值不同的删掉或新增。所以当会影响动画效果的时候要给组件加上key值。
2.为什么组件化
扩展HTML元素,封装可重用的代码
3.组件注册方式
-
全局组件
到目前为止,我们只用过Vue.component来创建组件:
组件可以有data、methods、computed…,但是data必须是一个函数,数据通过return返回。
Vue.component('my-component-name',{ template:` <div></div> `, data(){ return { myname:"child-name" } } })
-
局部组件
var ComponentA = { /* ... */} var ComponentB = { /* ... */} var ComponentC = { /* ... */}
然后在components选项中定义你想要使用的组件
new Vue({ el:"#app", components:{ 'component-a':ComponentA, 'component-b':ComponentB } })
注意事项:
起名字 :js驼峰, html 链接符-
dom片段 没有代码提示 没有高亮显示 - vue单文件组件解决
css 只能写成 行内。- vue单文件组件解决
template 包含一个根节点
组件是孤岛,无法【直接】访问外面的组件的状态或者方法。- 间接的组件通信来交流。
自定义的组件 data 必须是一个函数,
所有的组件都在一起, 太乱了 — vue单文件组件解决
4.组件编写方式与Vue实例的区别
- 自定义组件需要有一个root element
- 父子组件的data是无法共享的
- 组件可以有data、methods、computed…,但是data必须是一个函数,数据通过return返回
5.组件通信
-
父子组件传值(props down,events up)
组件父传子,传的是属性,是为了让组件最大化复用
组件子传父,需要再子组件身上监听事件,等子组件通过this. e m i t 触 发 事 件 。 子 传 父 需 要 在 子 组 件 标 签 自 定 义 一 个 事 件 名 然 后 绑 定 父 组 件 里 的 一 个 函 数 , 然 后 通 过 子 组 件 里 的 触 发 事 件 函 数 主 动 的 给 父 组 件 分 发 数 据 = = t h i s . emit 触发事件。 子传父需要在子组件标签自定义一个事件名然后绑定父组件里的一个函数,然后通过子组件里的触发事件函数主动的给父组件分发数据==this. emit触发事件。子传父需要在子组件标签自定义一个事件名然后绑定父组件里的一个函数,然后通过子组件里的触发事件函数主动的给父组件分发数据==this.emit(“aaaaa”,this.money)==,父组件用形参接受数据并使用。
<div id="box"> app <child @aaaaa="handleEvent"></child> </div> <script> Vue.component("child",{ data(){ return { money:10000000 } }, template:` <div style="background:yellow"> child-<button @click="handleClick">click-child</button> </div> `, methods:{ handleClick(){ //分发 this.$emit("aaaaa",this.money) } } }) //root component new Vue({ el:"#box", methods: { handleEvent(data){ console.log("父组件中定义的",data) } }, }) </script>
-
属性验证
组件父传子,子通过props来接收属性,可设置传递的类型与默认值。
props:{ mytitle:{ type:String, default:"默认名字" }
-
事件机制
- 使用 $on(eventName) 监听事件
- 使用 $emit(eventName) 触发事件
-
ref
<input ref="mytext"/> this.$refs.mytext
- ref放在标签上, 拿到的是原生节点
-
ref放在组件上, 拿到的是组件对象,通信功能
-
事件总线
-
new 一个空的Vue对象,命名为bus
-
在需要点击触发的事件函数里bus.$emit(“自定义名称”,“传送的数据”)
-
在接受的组件里
mounted(data){ bus.$on("huang",(data) =>{ this.lover = data }) }
-
<div id="box">
<child1></child1>
<child2 ></child2>
</div>
<script>
var bus = new Vue()
Vue.component("child1",{
template:`
<div>child1
<button @click="handleEvent">黄黄</button>
</div>
`,
methods:{
handleEvent(){
bus.$emit("huang","郭哥")
}
}
})
Vue.component("child2",{
data(){
return {
lover:""
}
},
template:`
<div>child2
<div>{{lover}}</div>
</div>
`,
mounted(data){
bus.$on("huang",(data) =>{
this.lover = data
})
}
})
var vm = new Vue({
el:"#box",
})
-
问题汇总
-
属性可不可以修改?
严格来说,Vue子组件不能随便更改父组件传递过来的属性,但是可以通过props配合$emit改变父组件传入的值
//父组件
my-input:"warning.sunc=“warning”/
在父组件传入值时,需要有xxx.sync修饰符;
//子组件$emit(‘update:warning’,val)
子组件可以在$emit(‘update:xxxx’,val),派发事件修改传入的值;
-
v-once用在组件上有什么用?
渲染普通的HTML元素在Vue中是非常快速的,但有时候你可能有一个组件,这个组件包含了大量静态内容。在这种情况下,你可以在在根元素上添加v-once attribute以确保这些内容只计算一次然后缓存起来,就像这样:
Vue.component(`terms-of-service',{ template:` <div v-once> <h1>Terms of Service</h1> ...a lot of static content... </div> ` })
-
v-model可以用在组件通信吗?
Vue.component('custom-input',{ props:['value'], template:` <input v-bind:value="value" v-on:input="$emit('input',$event.target.value)" > ` })
现在v-model就应该可以在这个组件上完美地工作起来了:
<cusstom-input v-model="searchText"></custom-input>
-
6.动态组件
- 元素,动态地绑定多个组件到它的is属性
- 保留状态,避免被重新渲染
7.slot插槽(内容分发)
- 混合父组件的内容与子组件自己的模板–>内容分发
- 父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译
- 单个slot
- 具名slot
对于一个带有如下模板的组件:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
<div>
在向具名插槽提供内容的时候,我们可以在一个元素上使用v-slot指令,并以v-slot的参数的形式提供其名称:
<base-layout>
<template v-slot:header>
<h1>Here might be a page title<h1>
</template>
<p>A paragraph for the main content.<p>
<p>And another one.<p>
<temolate v-slot:foot>
<p>Here's some contact info</p>
</temolate>
</base-layout>
注意v-slot只能添加在上,文本节点也可以当具名插槽内容插入。v-slot:可以缩写成#
8.transition过渡
Vue在插入‘更新或者移除DOM时,提供多种不同方式的应用过渡效果。
-
单元素/组件过渡
-
css过渡
-
css动画
-
结合animate.css动画库
name属性绑定css样式,会去找到符合name-的css样式
<transition name="custom-classes-transition" enter-active-class="animated tada" leave-active-class="animated bounceOutRight" > <p v-if="show">hello</p> </transition>
-
-
多个元素过渡(设置key)
-
当有相同标签名的元素切换是,需要通过key特性设置唯一的值来标记,以让Vue区分它们,否则Vue为了效率只会替换相同标签内部的内容。
mode:in-out;out-in (显示再删除;先删除在显示)
-
-
多个组件过渡
-
列表过渡(设置key)
- 不同于transition,它会以一个真实元素呈现:默认为一个。你也可以通过tag特性更换为其他元素。
- 提供唯一的key属性值
9.生命周期
-
生命周期各个阶段
https://cn.vuejs.org/v2/guide/instance.html#%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%9B%BE%E7%A4%BA
-
生命周期钩子函数的触发条件与作用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AvoYB9kn-1619966767364)(C:\Users\DSH\AppData\Roaming\Typora\typora-user-images\image-20210201233911869.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x2ipQH25-1619966767365)(C:\Users\DSH\AppData\Roaming\Typora\typora-user-images\image-20210201234028741.png)]
10. swiper学习
https://www.swiper.com.cn/
<!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 type="text/javascript" src="lib/vue.js"></script>
<link rel="stylesheet" href="lib/swiper/css/swiper.css">
<script src="lib/swiper/js/swiper.js"></script>
<style>
.swiper-container {
width: 600px;
height: 300px;
}
.swiper-container img{
width: 100%;
}
.huang{
background-color: cyan;
}
</style>
</head>
<body>
<div id="box">
<swiper :key="list.length" :myoptions="{
loop:true,
}">
<div class="swiper-slide" v-for="data in list">
{{data}}
</div>
<template #pagination>
<div class="swiper-pagination"></div>
</template>
</swiper>
</div>
<!-- Initialize Swiper -->
<script>
Vue.component("swiper",{
props:["myoptions"],
template:`
<div class="swiper-container huang">
<div class="swiper-wrapper">
<slot></slot>
</div>
<div class="swiper-pagination"></div>
</div>
`,
mounted(){
var baseoptions = {
loop:false,
pagination:{
el:'.swiper-pagination',
},
}
new Swiper(".huang",Object.assign(baseoptions,this.myoptions))
}
})
var vm = new Vue({
el:"#box",
data:{
list:[]
},
mounted(){
setTimeout(()=>{
this.list = ['aaa','bbb','ccc']
},2000)
},
})
</script>
</body>
</html>
11.自定义组件的封装
自定义封装swiper(基于swiper)
n:{
el:’.swiper-pagination’,
},
}
new Swiper(".huang",Object.assign(baseoptions,this.myoptions))
}
})
var vm = new Vue({
el:"#box",
data:{
list:[]
},
mounted(){
setTimeout(()=>{
this.list = ['aaa','bbb','ccc']
},2000)
},
})
```
11.自定义组件的封装
自定义封装swiper(基于swiper)
注意:防止swiper初始化过早