vue.js零基础入门到vue项目入土2.0
VUE2.0——VUE3.0系列第二章
1. 旧版slot
单个插槽, <slot></slot>
具名插槽 <slot name="a"></slot>
slot=‘插槽名’ slot-scope=“子组件传递过来的数据对象
slot的作用
有了slot,那么组件的复用性则会提升,可以在组件中多放置一些插槽,在不同的文档中使用该组件时,插入不同的东西
在父组件中的标签用插槽引入子组件中也可以在父组件中进行编译
可以在组件里面添加DOM结构
插槽的意义:扩展组件能力,提高组件的复用性
1.1 没有给slot给名字时,单个插槽
有几个slot,那么里面的全部DOM结构就会复制几遍
<script src="./vue.js/vue2.js"></script>
<div id="box">
<child>
<div>11111111111111</div>
<div>22222222222222</div>
<div>33333333333333</div>
</child>
</div>
<script type="text/javascript">
Vue.component("child", {
template: `
<div>
child
<slot></slot>
<slot></slot>
<slot></slot>
</div>
`
})
new Vue({
el:"#box"
})
</script>
1.1 给slot给名字时,具名插槽
则会按照名称的顺序来表达出来
<script src="./vue.js/vue2.js"></script>
<div id="box">
<child>
<div slot="a">11111111111111</div>
<div slot="b">22222222222222</div>
<div slot="c">33333333333333</div>
<div>4444444444</div>
</child>
</div>
<script type="text/javascript">
Vue.component("child", {
template: `
<div>
child
<slot name="a"></slot>
<slot name="b"></slot>
<slot name="c"></slot>
<slot></slot>
</div>
`
})
new Vue({
el:"#box"
})
</script>
2. 新版slot
- 具名插槽的缩写 v-slot:替换为字符#
- template配合v-slot:/#使用
<script src="./vue.js/vue2.js"></script>
<div id="box">
<!-- 当前组件或者节点 在哪个模板中,就能访问哪个模板状态 -->
<child>
<template v-slot:a>
<div>1111111111</div>
</template>
<template #b>
2222222222222222222
</template>
<div slot="c">33333333333333</div>
<div>44444444444444</div>
</child>
<navbar>
<template #left>
<button>aaa</button>
</template>
<template #right>
<i class="iconfont icon-all">字体图标</i>
</template>
</navbar>
</div>
<script>
// 插槽的意义 : 扩展组件能力, 提高组件的复用性
Vue.component("navbar", {
template: `
<div>
<slot name="left"></slot>
<span>navbar</span>
<slot name="right"></slot>
</div>
`
})
Vue.component("child", {
template: `
<div>
child
<slot name="a"></slot>
<slot name="b"></slot>
<slot name="c"></slot>
<slot></slot>
</div>
`
})
new Vue({
el: "#box"
})
</script>
3. 插槽版抽屉案例
<script src="./vue.js/vue2.js"></script>
<div id="box">
<navbar>
<button @click="isShow=!isShow">click</button>
</navbar>
<sidebar v-show="isShow"></sidebar>
</div>
<script>
Vue.component("navbar",{
template:`
<div>
nabbar- <slot></slot>
</div>
`
})
Vue.component("sidebar",{
template:`
<ul style="background-color: yellow;width:200px;height:500px;">
<li>首页</li>
<li>钱包</li>
<li>设置</li>
</ul>
`
})
new Vue({
el: "#box",
data:{
isShow:false
}
})
</script>
4. 过渡效果
过渡其实就是一个淡入淡出的效果。Vue在元素显示与隐藏的过渡中,提供了 6 个 class 来切换:
- v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
- v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
- v-enter-to: 2.1.8版及以上 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter
被移除),在过渡/动画完成之后移除。 - v-leave: 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
- v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
- v-leave-to: 2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。
对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 <transition>
,则 v- 是这些类名的默认前缀。如果你使用了 <transition name="my-transition">
,那么 v-enter 会替换为 my-transition-enter。
作用:
为了让vue 页面效果展示时高端点、友好点,我们可以适当的加入一些过渡效果
使用规则:
- 在vue 中,如果要让某个组件(元素)添加过渡效果,就必须让该组件(元素)包裹在
<transition></transition>
元素中。该元素有个name 属性,用于标识作用。此外,我们还要去设置过渡效果css - 这个标签只能过渡一个标签
<script src="./vue.js/vue2.js"></script>
<style>
/* 进场动画 */
.kerwin-enter-active{
animation: aaa 1.5s;
}
/* 出场动画 */
.kerwin-leave-active{
animation: aaa 1.5s reverse;
}
@keyframes aaa{
0%{
opacity: 0;
transform: translateX(100px);
}
100%{
opacity: 1;
transform: translateX(0px);
}
}
</style>
<div id="box">
<button @click="isShow=!isShow">change</button>
<transition enter-active-class="kerwin-leave-acitve" leave-active-class="kerwin-leave-active">
<div v-if="isShow">11111111111111</div>
</transition>
<transition name="kerwin" appear>
<!-- 让第一次出现动画 -->
<div v-if="isShow">222222222222</div>
</transition>
</div>
<script>
new Vue({
el: "#box",
data:{
isShow:true
}
})
</script>
<script src="./vue.js/vue2.js"></script>
<style>
/* 进场动画 */
.kerwin-enter-active{
animation: aaa 1.5s;
}
/* 出场动画 */
.kerwin-leave-active{
animation: aaa 1.5s reverse;
}
@keyframes aaa{
0%{
opacity: 0;
transform: translateX(100px);
}
100%{
opacity: 1;
transform: translateX(0px);
}
}
</style>
<div id="box">
<button @click="isShow=!isShow">change</button>
<transition name="kerwin">
<!-- <div v-if="isShow" >11111111111111</div>
<div v-else="isShow" >2222222222222</div>
两个div点击时不出现想要的动画效果
-->
<div v-if="isShow" key="1">11111111111111</div>
<div v-else="isShow" key="2">2222222222222</div>
</transition>
</div>
<script>
var vm =new Vue({
el: "#box",
data:{
isShow:false
}
})
</script>
5. 过渡中的diff算法
- Vue的diff算法会使改变前与改变后进行父对父,子对子对比
- 在改变过渡效果时如果使相同标签对比,则可能会用diff算法,直接将不同的东西改变,导致效果无法看到
- 而如果标签不相同,那么diff算法就认为发生了翻天覆地的改变,使用过渡效果,两个标签替换时,就会先删除第一个再让第二个加载
<button @click="isShow = !isShow" >change</button>
<transition name="kerwin" mode=”out-in>
<div v-if="isShow">111111111111111</div>
<p v-else >2222222222222222222222222</p>
</transition>
<!--
<button @click="isShow = !isShow" >change</button>
<transition name="kerwin" mode=”out-in>
<div v-if="isShow" key="1">111111111111111</div>
<div v-else key="2">2222222222222222222222222</div>
</transition>
-->
6. 列表过度
属性
mode=“out-in”:先走再来
mode=“in-out”:先来再走
组件过渡:组件过渡可以使用动态组件,在动态组件上加上<transition>
标签即可
列表过渡:<transition-grop>
可以添加多个标签,需要给标签加key值
<transition-grop>
有tag属性,可以指定转化成的一个标签
<script src="./vue.js/vue2.js "></script>
<style>
/* 进场动画 */
.kerwin-enter-active {
animation: aaa 1.5s;
}
/* 出场动画 */
.kerwin-leave-active {
animation: aaa 1.5s reverse;
}
@keyframes aaa {
0% {
opacity: 0;
transform: translateX(100px);
}
100% {
opacity: 1;
transform: translateX(0px);
}
}
</style>
<div id="box">
<!--双向绑定了一个输入框的value -->
<input type="text" v-model="mytext" />
<!-- {{mytext}} -->
<button @click="handleAdd()">add</button>
<!-- 当被删除完之后显示空空如也-->
<div v-show="!datalist.length">待办事项空空如也</div>
<transition-group v-show="datalist.length" name="kerwin" tag="ul">
<li v-for="(item,index) in datalist" :key="item">
{{item}}--{{index}}
<button @click="handeleDel(index)">del</button>
</li>
</transition-group>
</div>
<script>
var vm = new Vue({
el: "#box",
data: {
mytext: "11111111",
datalist: ["1111", "2222", "3333"]
},
methods: {
handleAdd() {
console.log("获取value", this.mytext)
this.datalist.push(this.mytext)
//清空输入框的value
this.mytext = " "
},
handeleDel(index) {
console.log("del", index)
this.datalist.splice(index, 1)//删除数组元素
}
}
})
</script>
7. 可复用过渡
将动画封装在一个组件中,即可以做到复用过渡
<style>
.left {}
/* 在mode填写修改从左边还是从右边 */
.right {
position: fixed;
right: 0px;
top: 0px;
}
/* 进场动画 */
.left-enter-active {
animation: aaa 1.5s;
}
/* 出场动画 */
.left-leave-active {
animation: aaa 1.5s reverse;
}
@keyframes aaa {
0% {
opacity: 0;
transform: translateX(-100%);
}
100% {
opacity: 1;
transform: translateX(0px);
}
}
.right-enter-active {
animation: bbb 1.5s;
}
/* 出场动画 */
.right-leave-active {
animation: bbb 1.5s reverse;
}
@keyframes bbb {
0% {
opacity: 0;
transform: translateX(100px);
}
100% {
opacity: 1;
transform: translateX(0px);
}
}
</style>
</head>
<body>
<div id="box">
<navbar @myevent="handleEvent"></navbar>
<sidebar v-show="isShow" mode="right"></sidebar>
</div>
<script>
Vue.component("navbar", {
template: `
<div>
nabbar-<button @click="handleClick">click</button>
</div>
`,
methods: {
handleClick() {
// 通知父组件 取反 isShow - 子传父 依靠 事件
this.$emit("myevent")
}
}
})
Vue.component("sidebar", {
props: ["mode"],
template: `
<transition :name="mode">
<ul style="background-color: yellow;width: 200px;height: 500px;" :class="mode">
<li>首页</li>
<li>钱包</li>
<li>设置</li>
</ul>
</transition>
`
})
new Vue({
el: "#box",
data: {
isShow: false
},
methods: {
handleEvent() {
console.log("父组件", "1111111")
this.isShow = !this.isShow
}
}
})
</script>
8. 生命周期
什么是生命周期
组件的生命周期指的是:组件从创建 -> 运行(渲染) -> 销毁的整个过程,强调的是一个时间段
什么是Vue 组件生命周期钩子函数
所谓生命周期钩子函数(简称生命周期函数),指的是组件的创建、更新、销毁三个阶段所触发执行的函数。 根据每个阶段触发的钩子函数,我们可以相应的做一些操作,如获取后端接口数据、监听事件、执行事件、执行定时器、移除事件、清理定时器等等。
什么是钩子函数
简单点来说,钩子函数就是你创建的Vue在初始化、更新数据、销毁时会被自动调用的函数。
八大钩子数分别是:
beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestory,destoryed
面试可能的问题:
说一下vue的生命周期函数,包括每一个生命周期函数在项目中什么时候去用的。
钩子名固定,与data和methods属于平级关系,如果执行相应的方法,会输出一句话
<div id="app"></div>
<script>
var vm=new Vue({
el: "#app",
data: {},
beforeCreate() {console.log("beforeCreate")},
created() {console.log("created")},
beforeMount() {console.log("beforeMount")},
mounted() {console.log("mounted")},
beforeUpdate() {console.log("beforeUpdate")},
updated() {console.log("updated")},
beforeDestroy() {console.log("beforeDestroy")},
destroyed() {console.log("destroyed")},
methods: {}
});
</script>
8.1 创建阶段
每个 Vue 组件实例在创建时都会经历一系列初始化步骤——例如,它需要设置数据观察、编译模板、将实例挂载到 DOM 以及在数据更改时更新 DOM。在此过程中,它还运行称为生命周期钩子的函数,让用户有机会在特定阶段添加自己的代码。
调用所有生命周期钩子时,其上下文都指向调用它的当前活动实例。请注意,这意味着在声明生命周期挂钩时应避免使用箭头函数,因为如果这样做,将无法访问组件实例。
(1). beforeCreate创建前
beforeCreate可以简单的理解为在数据初始化的之前被调用,这时候data和methods尚未没有数据。
在实例处理完所有与状态相关的选项后调用。
在要挂载组件之前调用。
(2). created创建完成
created可以理解为在数据初始化之后被调用,这时候data和methods已经被填充了相应的数据。
调用此钩子时,已设置以下内容:反应式数据、计算属性、方法和观察程序。但是,安装阶段尚未开始,该属性尚不可用
<div id="app"></div>
<script>
var vm=new Vue({
el: "#app",
data: {
msg:"李是神魔念" //添加msg数据
},
beforeCreate() {
console.log("this= "+this)
console.log("this.msg= "+this.msg)
console.log("this.md= "+this.md)
console.log("")
},
created() {
console.log("this= "+this)
console.log("this.msg= "+this.msg)
console.log("this.md= "+this.md)
console.log("")
},
methods: {
md: function(){}, //空方法
}
});
</script>
结果显示在beforeCreate方法与create方法之间完成了资源的注入。
(3). beforeMount挂载前
调用此钩子时,组件已完成其反应状态的设置,但尚未创建 DOM 节点。它即将首次执行其 DOM 渲染效果。
beforeMount在页面尚未被渲染时使用,也就是Vue的数据没有传到页面。
(4).mounted挂载完成
mounted在页面渲染完成之后使用,也就是此时页面已完全取出Vue中的数据。
组件被视为在以下之后安装:
它的所有同步子组件都已挂载(不包括异步组件或树中的组件)。
它自己的 DOM 树已被创建并插入到父容器中。请注意,如果应用程序的根容器也在文档中,则仅保证组件的 DOM 树在文档中。
此挂钩通常用于执行需要访问组件呈现的 DOM 的副作用,或用于在服务器呈现的应用程序中将与 DOM 相关的代码限制为客户端。
<div id="app">
<h1 id="ren">{{msg}}</h1>
</div>
<script>
var vm=new Vue({
el: "#app",
data: {
msg:"李在赣神魔" //添加msg数据
},
beforeMount() {
let doc = document.querySelector("#ren");//查询到id名为ren的节点
console.log(doc)
console.log("")
},
mounted() {
let doc = document.querySelector("#ren");
console.log(doc)
},
});
</script>
此时,Vue对象中资源已注入完毕,页面也已经渲染完毕,上述四个方法在页面被加载时自动被执行
案例:
<div id="box">
</div>
<script type="text/javascript">
//根组件
var vm = new Vue({
el:"#box",
data:{
myname:"van",
datalist:[]
},
// template:`<div>root component--{{myname}}</div>`,支持template,不止子组件可以用
beforeCreate(){
console.log("beforeCreate",this.myname)
},
created(){
console.log("created","初始化状态或者挂载到当前实例的一些属性")
this.myname = this.myname+"11111111" //被拦截的状态
this.user =localStorage.getItem("user") // this下面的属性
// 发ajax
},
beforeMount(){
console.log("beforeMount",this.$el) // 模板解析之前最后一次修改模板节点。
},
mounted(){
console.log("mounted","拿到真实的dom节点",document.getElementById("box").innerHTML)
// 依赖于dom创建之后, 才进行初始化工作的插件 (轮播插件)
// 订阅 bus.$on
// 发ajax
}
})
</script>
8.2 更新阶段
(5)beforeUpdate
这个钩子可以用来在 Vue 更新 DOM 之前访问 DOM 状态。在此挂钩中修改组件状态也是安全的。
beforeUpdate在页面更新渲染完成后,DOM树发生改变前被调用
(6)updated
updated在页面DOM树改变后被调用
需要注意的是如果只是改变了dom中的数据(data),未对页面造成任何影响,就不会触发beforeUpdate,updated方法。
父组件的更新钩子在其子组件的钩子之后调用。
此钩子在组件的任何 DOM 更新后调用,这可能是由不同的状态更改引起的。如果您需要在特定状态更改后访问更新的 DOM,请改用 nextTick()。
<div id="app">
<h1 id="ren">
<p v-if="msg"></p>
</h1>
</div>
<script>
var vm=new Vue({
el: "#app",
data: {
msg:true //添加msg数据
},
beforeUpdate() {
let a = document.getElementById("ren");
console.log(a.childElementCount)
console.log("")
},
updated() {
let a = document.getElementById("ren");
console.log(a.childElementCount)
},
});
</script>
案例:
8.3 销毁
(7)beforeDestory
beforeDestory是在Vue组件销毁之前被调用
(8)destoryed
destoryed在Vue组件销毁之后被调用
<div id="app">
<mytest id="child" v-if="flag">
</mytest>
</div>
<script>
let myname = Vue.component('mytest', {
template: '<p>yes</p>',
beforeDestroy() {
console.log("beforeDestroy被执行")
},
destroyed() {
console.log("destroyed被执行")
},
});
var vm=new Vue({
el: "#app",
data: {
flag: true
},
components:{
"mytest" : myname,
},
});
</script>
案例:
<script src="./vue.js/vue2.js "></script>
<div id="box">
<child v-if="isCreated"></child>
</div>
<script>
Vue.component("child", {
data() {
return {
time: 1000
}
},
created() {
this.id = null
},
mounted() {
this.id = setInterval(() => {
console.log("倒计时")
this.time--
}, 1000)
window.onresize = () => {
console.log("resize")
}
},
template: `
<div>
抢购倒计时组件
<div>{{time}}</div>
</div>
`,
beforeDestroy() {
console.log("beforeDestroy", "清除定时器, 事件解绑,,,,")
clearInterval(this.id)
window.onresize = null
},
destroyed() {
console.log("destroyed", "清除定时器, 事件解绑,,,,")
}
})
var vm = new Vue({
el: "#box",
data: {
isCreated: true
}
})
</script>
9. swiper 静态使用
swiper使用需要两个文件,一个是swiper.css,里面规定了一些在这个滑动轮播插件中常用的样式,当然如果愿意的话可以自己定义样式
另外一个是swiper.js这个是插件的主体部分。
这些文件可以在官网查找获取方法
在页面中引入这两个文件之后首先要写基本的html结构
swiper插件:
https://swiper.com.cn/
<script src="./swiper/swiper-bundle7.min.js"></script>
<link rel="stylesheet" href="./swiper/swiper-bundle7.min.css">
<style>
.van {
height: 500px;
}
</style>
<header>导航</header>
<div class="swiper van">
<div class="swiper-wrapper">
</div>
<!-- 如果需要分页器 -->
<div class="swiper-pagination"></div>
<!-- 如果需要导航按钮 -->
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
<!-- 如果需要滚动条 -->
<!-- <div class="swiper-scrollbar"></div> -->
</div>
<footer>底部内容</footer>
<script>
//swiper 初始化过早
setTimeout(()=>{
var list = ["aaaa","bbbbb","cccc"]
var newlist = list.map(item=>`
<div class="swiper-slide">${item}</div>
`)
console.log(newlist.join(""))
var owrapper = document.querySelector(".swiper-wrapper")
owrapper.innerHTML = newlist.join('')
//dom插入完之后,再new swiper,就可以解决数据慢载入的问题
init()
},1000)
//初始化 swiper
function init(){
new Swiper(".van", {
// direction:"vertical",//垂直方向
//如果需要分页器
pagination: {
el: '.swiper-pagination',
},
loop: true, // 循环模式选项
// 自动轮播
autoplay: {
delay: 2500,
disableOnInteraction: false,
},
// 如果需要前进后退按钮
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
})
}
</script>
10. vue-swiper 动态
<script src="./swiper/swiper-bundle7.min.js"></script>
<link rel="stylesheet" href="./swiper/swiper-bundle7.min.css">
<script src="./vue.js/vue2.js"></script>
<div id="box">
<header>导航-{{myname}}</header>
<div class="swiper van">
<div class="swiper-wrapper">
<div class="swiper-slide" v-for="data in datalist" :key="data">
<img :src="data" />
</div>
</div>
<!-- 如果需要分页器 -->
<div class="swiper-pagination"></div>
</div>
<footer>底部内容</footer>
</div>
<script>
//初始化 swiper --初始化过早
var vm = new Vue({
el: "#box",
data: {
datalist: [],
myname: "van"
},
mounted() {
//ajax
setTimeout(() => {
this.datalist = ["https://static.maizuo.com/pc/v5/usr/movie/e856bdc65507b99800f22e47801fa781.jpg",
"https://static.maizuo.com/pc/v5/usr/movie/47aa5a3ad2ebff403d073288e4365694.jpg",
"https://static.maizuo.com/pc/v5/usr/movie/8b0755547313706883acc771bda7709d.jpg"
]
//过早
// 这里输出console.log(document.querySelectorAll(".swiper-slide").length) 为0,是因为dom更新是异步的,虽然状态被父子了
}, 1000)
//初始化过早
},
updated() {
console.log(document.querySelectorAll(".swiper-slide").length)
new Swiper(".van", {
// direction:"vertical", //垂直
// 如果需要分页器
pagination: {
el: '.swiper-pagination',
},
loop: true,
autoplay: {
delay: 2500,
disableOnInteraction: false,
}
})
}
})
/*缺点:
1. 无法复用
2. 如果当前页面 状态不止datalist一个,其他状态更新, update重新运行,new Swiper 执行多次, 出bug
*/
</script>
11. swiper 组件
一、
<script src="./swiper/swiper-bundle7.min.js"></script>
<link rel="stylesheet" href="./swiper/swiper-bundle7.min.css">
<script src="./vue.js/vue2.js"></script>
<div id="box">
<swiper v-if="datalist.length">
<!-- 根据数组的长度改变,由之前的为0不创建,到长度不为0开始创建 -->
<swiper-item v-for="data in datalist" :key="data">
<img :src="data" />
</swiper-item>
</swiper>
<!-- <swiper :key="datalist.length"></swiper>> -->
</div>
<script>
Vue.component("swiperItem", {
template: `
<div class="swiper-slide">
<slot></slot>
</div>
`
})
Vue.component("swiper", {
template: `
<div class="swiper van">
<div class="swiper-wrapper">
<slot></slot>
</div>
<div class="swiper-pagination"></div>
</div>`,
mounted() {
new Swiper(".van", {
// direction:"vertical",//垂直方向
//如果需要分页器
pagination: {
el: '.swiper-pagination',
},
loop: true, // 循环模式选项
// 自动轮播
autoplay: {
delay: 2500,
disableOnInteraction: false,
}
})
},
destroyed(){
}
})
new Vue({
el: '#box',
data:{
datalist:[]
},
mounted() {
setTimeout(() => {
this.datalist = ["https://static.maizuo.com/pc/v5/usr/movie/e856bdc65507b99800f22e47801fa781.jpg",
"https://static.maizuo.com/pc/v5/usr/movie/47aa5a3ad2ebff403d073288e4365694.jpg",
"https://static.maizuo.com/pc/v5/usr/movie/8b0755547313706883acc771bda7709d.jpg"
]
}, 1000)
}
})
</script>
二、
<script src="./swiper/swiper-bundle7.min.js"></script>
<link rel="stylesheet" href="./swiper/swiper-bundle7.min.css">
<script src="./vue.js/vue2.js"></script>
<div id="box">
<swiper v-if="datalist.length" :loop="false">
<!-- :loop="false"写的话,轮播到最后不会再直接跳转轮播到第一张, -->
<swiper-item v-for="data in datalist" :key="data">
<img :src="data" />
</swiper-item>
</swiper>
<!-- <swiper :key="datalist.length"></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 van">
<div class="swiper-wrapper">
<slot></slot>
</div>
<div class="swiper-pagination"></div>
</div>`,
mounted() {
new Swiper(".van", {
// direction:"vertical",//垂直方向
//如果需要分页器
pagination: {
el: '.swiper-pagination',
},
loop: this.loop, // 循环模式选项
// 自动轮播
autoplay: {
delay: 2500,
disableOnInteraction: false,
}
})
},
destroyed(){
}
})
new Vue({
el: '#box',
data:{
datalist:[]
},
mounted() {
setTimeout(() => {
this.datalist = ["https://static.maizuo.com/pc/v5/usr/movie/e856bdc65507b99800f22e47801fa781.jpg",
"https://static.maizuo.com/pc/v5/usr/movie/47aa5a3ad2ebff403d073288e4365694.jpg",
"https://static.maizuo.com/pc/v5/usr/movie/8b0755547313706883acc771bda7709d.jpg"
]
}, 1000)
}
})
</script>
12. vue3组件写法
<script src="./swiper/swiper-bundle7.min.js"></script>
<link rel="stylesheet" href="./swiper/swiper-bundle7.min.css">
<script src="./vue.js/vue3.js"></script>
<div id="box">
{{myname}}
<navbar myname="aaaa">
<div>1111111111111</div>
</navbar>
<sidebar></sidebar>
</div>
<script>
// console.log(Vue.component)
var obj = {
data(){
return{
myname:"van"
}
},
methods:{
},
computed:{
}
}
var app = Vue.createApp(obj)
app.component("navbar",{
props:["myname"],
template:`
navbar-{{myname}}
<div>navbar</div>
<slot></slot>
`
})
app.component("sidebar",{
template:`
<div>sidebar</div>
`
})
app.mount("#box")
</script>
13. vue3 生命周期&轮播
vue3生命周期
<script src="./swiper/swiper-bundle7.min.js"></script>
<link rel="stylesheet" href="./swiper/swiper-bundle7.min.css">
<script src="./vue.js/vue3.js"></script>
<div id="box">
{{myname}}
<navbar myname="aaaa">
<div>1111111111111</div>
</navbar>
<sidebar></sidebar>
</div>
<script>
// console.log(Vue.component)
var obj = {
data(){
return{
myname:"van"
}
},
methods:{
},
computed:{
},
created(){
console.log("created")
},
beforeMount(){
},
mounted(){
console.log("mounted")
},
//生命周期替换
// beforeDestroy(){
// console.log("beforedestroy")
// },
// destroyed(){
// }
beforeUnmount(){
},
unmounted(){
}
}
var app = Vue.createApp(obj)
app.component("navbar",{
props:["myname"],
template:`
navbar-{{myname}}
<div>navbar</div>
<slot></slot>
`
})
app.component("sidebar",{
template:`
<div>sidebar</div>
`
})
app.mount("#box")
//vue2 (类,this)
//vue3 类(vue》90%一样),hooks(函数式)
</script>
vue3轮播
<script src="./swiper/swiper-bundle7.min.js"></script>
<link rel="stylesheet" href="./swiper/swiper-bundle7.min.css">
<script src="./vue.js/vue3.js"></script>
<div id="box">
<swiper v-if="datalist.length" :loop="false">
<swiper-item v-for="data in datalist" :key="data">
<img :src="data" />
</swiper-item>
</swiper>
<!-- <swiper :key="datalist.length"></swiper> -->
</div>
<script>
var obj = {
data() {
return {
datalist: []
}
},
mounted() {
setTimeout(() => {
this.datalist = ["https://static.maizuo.com/pc/v5/usr/movie/e856bdc65507b99800f22e47801fa781.jpg",
"https://static.maizuo.com/pc/v5/usr/movie/47aa5a3ad2ebff403d073288e4365694.jpg",
"https://static.maizuo.com/pc/v5/usr/movie/8b0755547313706883acc771bda7709d.jpg"
]
}, 2000)
}
}
var 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 kerwin">
<div class="swiper-wrapper">
<slot></slot>
</div>
<div class="swiper-pagination"></div>
</div>`,
mounted() {
// console.log("mounted")
new Swiper(".kerwin", {
// direction:"vertical", //垂直
// 如果需要分页器
pagination: {
el: '.swiper-pagination',
},
loop: this.loop,
autoplay: {
delay: 2500,
disableOnInteraction: false,
}
})
},
// destroyed() {
// // console.log("destroy")
// //
// }
})
app.mount("#box")
</script>
14. 指令写法
Vue.js的指令是以v-开头的,它们作用于HTML元素,指令提供了一些特殊的特性,将指令绑定在元素上时,指令会为绑定的目标元素添加一些特殊的行为,我们可以将指令看作特殊的HTML特性(attribute)。
指令的作用是当表达式的值改变时,相应地将某些行为应用到 DOM 上。
vue常用指令有:
v-once指令、v-show指令、v-if指令、v-else指令、v-else-if指令、v-for指令、v-html指令、v-text指令、v-bind指令、v-on指令、v-model指令等等。
14.1 自定义指令介绍directives
对普通DOM元素进行底层操作
<script src="./swiper/swiper-bundle7.min.js"></script>
<link rel="stylesheet" href="./swiper/swiper-bundle7.min.css">
<script src="./vue.js/vue2.js"></script>
<div id="box">
<div v-hello="'yellow'">111111111111</div>
<div v-hello="'red'">22222222222</div>
<div v-hello="whichColor">3333333333</div>
</div>
<script>
//指令 :为了操作底层dom。作者给留方案
//实际应用--可以通过指令知道什么时候dom创建完成,从而进行依赖dom的库的初始化工作
Vue.directive("hello", {
//inserted是指令的生命周期
inserted(el,binding) {
//第一次插入到父节点中触发,拿到的binding是一个对象
console.log("inserted",binding)
el.style.background = binding.value
},
update(el,binding){
//拿到了最新的dom节点和对象,在修改时可以得到期望的效果
console.log("update")
el.style.background = binding.value
}
})
var vm = new Vue({
el:"#box",
data:{
whichColor:"blue"
}
})
</script>
15. 指令应用
vue3指令生命周器约==组件生命周期
<script src="./swiper/swiper-bundle7.min.js"></script>
<link rel="stylesheet" href="./swiper/swiper-bundle7.min.css">
<script src="./vue.js/vue2.js"></script>
<div id="box">
<header>导航-{{myname}}</header>
<div class="swiper van">
<div class="swiper-wrapper">
<div class="swiper-slide" v-for="(data,index) in datalist" :key="data" v-swiper="{
index:index,
length:datalist.length
}">
<img :src="data" />
</div>
</div>
<!-- 如果需要分页器 -->
<div class="swiper-pagination"></div>
</div>
<footer>底部内容</footer>
</div>
<script>
//初始化 swiper --初始化过早
Vue.directive("swiper", {
inserted(el, binding) {
console.log(binding.value)
let { index, length } = binding.value
if (index === length - 1) {
// new Swiper
new Swiper(".van", {
// direction:"vertical", //垂直
// 如果需要分页器
pagination: {
el: '.swiper-pagination',
},
loop: true,
autoplay: {
delay: 2500,
disableOnInteraction: false,
}
})
}
}
})
var vm = new Vue({
el: "#box",
data: {
datalist: [],
myname: "van"
},
mounted() {
//ajax
setTimeout(() => {
this.datalist = ["https://static.maizuo.com/pc/v5/usr/movie/e856bdc65507b99800f22e47801fa781.jpg",
"https://static.maizuo.com/pc/v5/usr/movie/47aa5a3ad2ebff403d073288e4365694.jpg",
"https://static.maizuo.com/pc/v5/usr/movie/8b0755547313706883acc771bda7709d.jpg"
]
//过早
// 这里输出console.log(document.querySelectorAll(".swiper-slide").length) 为0,是因为dom更新是异步的,虽然状态被父子了
}, 1000)
//初始化过早
}
})
</script>
16. 指令补充&nextTick
1.
<script src="./swiper/swiper-bundle7.min.js"></script>
<link rel="stylesheet" href="./swiper/swiper-bundle7.min.css">
<script src="./vue.js/vue3.js"></script>
<div id="box">
<header>导航-{{myname}}</header>
<div class="swiper van">
<div class="swiper-wrapper">
<div class="swiper-slide" v-for="(data,index) in datalist" :key="data" v-swiper="{
index:index,
length:datalist.length
}">
<img :src="data" />
</div>
</div>
<!-- 如果需要分页器 -->
<div class="swiper-pagination"></div>
</div>
<footer>底部内容</footer>
</div>
<script>
var obj = {
data(){
return{
datalist:[],
myname:"van"
}
},
mounted() {
//ajax
setTimeout(() => {
this.datalist = ["https://static.maizuo.com/pc/v5/usr/movie/e856bdc65507b99800f22e47801fa781.jpg",
"https://static.maizuo.com/pc/v5/usr/movie/47aa5a3ad2ebff403d073288e4365694.jpg",
"https://static.maizuo.com/pc/v5/usr/movie/8b0755547313706883acc771bda7709d.jpg"
]
//过早
// 这里输出console.log(document.querySelectorAll(".swiper-slide").length) 为0,是因为dom更新是异步的,虽然状态被父子了
}, 1000)
//初始化过早
}
}
var app= Vue.createApp(obj)
app.directive("swiper", {
mounted(el, binding) {
console.log("111111111111111")
let { index, length } = binding.value
if (index === length - 1) {
// new Swiper
new Swiper(".van", {
// direction:"vertical", //垂直
// 如果需要分页器
pagination: {
el: '.swiper-pagination',
},
loop: true,
autoplay: {
delay: 2500,
disableOnInteraction: false,
}
})
}
}
})
app.mount("#box")
</script>
什么是nextTick()
定义:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
所以就衍生出了这个获取更新后的DOM的Vue方法。所以放在Vue.nextTick()回调函数中的执行的应该是会对DOM进行操作的 js代码;
理解:nextTick(),是将回调函数延迟在下一次dom更新数据后调用,简单的理解是:当数据更新了,在dom中渲染后,自动执行该函数
案例:使用this.$nextTick()
<script src="./swiper/swiper-bundle7.min.js"></script>
<link rel="stylesheet" href="./swiper/swiper-bundle7.min.css">
<script src="./vue.js/vue2.js"></script>
<div id="box">
<header>导航-{{myname}}</header>
<div class="swiper van">
<div class="swiper-wrapper">
<div class="swiper-slide" v-for="data in datalist" :key="data">
<img :src="data" />
</div>
</div>
<!-- 如果需要分页器 -->
<div class="swiper-pagination"></div>
</div>
<footer>底部内容</footer>
</div>
<script>
//初始化 swiper --初始化过早
var vm = new Vue({
el: "#box",
data: {
datalist: [],
myname: "van"
},
mounted() {
//ajax
setTimeout(() => {
this.datalist = ["https://static.maizuo.com/pc/v5/usr/movie/e856bdc65507b99800f22e47801fa781.jpg",
"https://static.maizuo.com/pc/v5/usr/movie/47aa5a3ad2ebff403d073288e4365694.jpg",
"https://static.maizuo.com/pc/v5/usr/movie/8b0755547313706883acc771bda7709d.jpg"
]
//等上面节点状态什么时候更新到dom之后,nextTick就会触发一次,而以后再更新,就再触发
this.$nextTick(()=>{
console.log("我比updated执行的都晚,并且只执行一次")
new Swiper(".van", {
// direction:"vertical", //垂直
// 如果需要分页器
pagination: {
el: '.swiper-pagination',
},
loop: true,
autoplay: {
delay: 2500,
disableOnInteraction: false,
}
})
})
}, 1000)
//初始化过早
},
updated(){
console.log("updated")
}
})
</script>
什么时候需要用的nextTick
1、Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中,原因是在created()钩子函数执行的时候DOM其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。
与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM挂载已完成。
created(){
let that=this;
that.$nextTick(function(){ //不使用this.$nextTick()方法会报错
that.$refs.aa.innerHTML="created中更改了按钮内容"; //写入到DOM元素
});
},
2、当项目中你想在改变DOM元素的数据后基于新的dom做点什么,对新DOM一系列的js操作都需要放进Vue.nextTick()的回调函数中;通俗的理解是:更改数据后当你想立即使用js操作新的视图的时候需要使用它
<template>
<div class="hello">
<h3 id="h">{{testMsg}}</h3>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
testMsg:"原始值",
}
},
methods:{
changeTxt:function(){
let that=this;
that.testMsg="修改后的文本值"; //vue数据改变,改变dom结构
let domTxt=document.getElementById('h').innerText; //后续js对dom的操作
console.log(domTxt); //输出可以看到vue数据修改后DOM并没有立即更新,后续的dom都不是最新的
if(domTxt==="原始值"){
console.log("文本data被修改后dom内容没立即更新");
}else {
console.log("文本data被修改后dom内容被马上更新了");
}
},
}
}
</script>
正确的用法是:vue改变dom元素结构后使用vue.$nextTick()方法来实现dom数据更新后延迟执行后续代码
changeTxt:function(){
let that=this;
that.testMsg="修改后的文本值"; //修改dom结构
that.$nextTick(function(){ //使用vue.$nextTick()方法可以dom数据更新后延迟执行
let domTxt=document.getElementById('h').innerText;
console.log(domTxt); //输出可以看到vue数据修改后并没有DOM没有立即更新,
if(domTxt==="原始值"){
console.log("文本data被修改后dom内容没立即更新");
}else {
console.log("文本data被修改后dom内容被马上更新了");
}
});
},
3、在使用某个第三方插件时 ,希望在vue生成的某些dom动态发生变化时重新应用该插件,也会用到该方法,这时候就需要在 $nextTick 的回调函数中执行重新应用插件的方法。