Vue 基础学习
目录
MVC、MVP、MVVM
MVC
- View 传送指令到 Controller
- Controller 完成业务逻辑后,要求 Model 改变状态
- Model 将新的数据发送到 View,用户得到反馈
不足之处:依赖太多
- View 依赖Controller和Model
- Controller依赖View和Model
- Model 和View的关系虽然很弱, 但是也需要某种方式来通知View进行数据更新。
m层和v层直接打交道,导致这两层还存在耦合的
因为所有逻辑都写在c层,导致c层特别臃肿
MVP
- 各部分之间的通信,都是双向的。
- View 与 Model 不发生联系,都通过 Presenter 传递。即View只知道Presenter, 不知道Model 。
- View 非常薄,不部署任何业务逻辑,称为"被动视图"(Passive View),即没有任何主动性,而 Presenter非常厚,所有逻辑都部署在那里。
不足之处:
- Presenter还是需要调用View的方法,也就是说Presenter对View有依赖,这样Presenter就没办法单独做单元测试,非得等到界面做好以后才行。
- 可以让View层提取出接口,Presenter只依赖这个接口
p层代替了了c层,v层和m层的交互被p层隔断,从理论上去除了v和m层的耦合
但是造成p层比原来的c层更加臃肿,为了缓解这种臃肿,MVVM出现了
MVVM
- 唯一的区别是,它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。Angular 和 Ember 都采用这种模式。
- 在MVP模式中:让Presenter调用View的方法去设置界面,仍然需要大量的、烦人的代码。所以ViewModel就诞生了,他是一个数据结构,而view可以根据这个数据结构的变化自动随之变化
基础指令
v-cloak
或{{value}}
能够解决 插值表达式的闪烁问题 不过需要在style里设置样式
[v-cloak]{ display: none !important; }
v-text
没有闪烁问题v-text
会覆盖标签里的内容v-html
会将元素当做HTML解析,上面两个只会当做文本v-bind
绑定属性 缩写:
v-on
绑定事件 缩写@
绑定的方法可以加括号方便传参数v-model
实现数据的双向绑定.stop
阻止事件冒泡.capture
使用捕获机制.self
只有点击当前元素时才触发事件.prevent
去阻止默认行为.once
只触发一次事件处理函数v-for
遍历对象 注意:除了有item key 之外还有索引i 如v-for="(item,key,i) in user
使用:key="item"
指定绑定的key key需要具有唯一性(最好为后台数据库传过来json数据的id)(只能使用number
或者String
)v-if
的特点是每次都会重新删除或者创建元素 有较高的切换性能消耗 (频繁的切换最好不用)v-show
不会每次都操作DOM的创建和删除,只是切换了元素的display:none;
的样式 有较高的初始渲染消耗
Vue实例中的属性
el
表示要控制页面的区域data
表示data中存放的是el中用到的数据methods
存放所需方法,主要处理业务逻辑computed
计算属性 主要当做属性来使用,可在其中进行算数逻辑运算,存在缓存机制性能更高 以减轻模板重量 使程序便于维护watch
监视 监视对象在data中 接收一个回调函数 函数参数ewValue
和oldValue
,可以看做computed
和methods
的结合体
组件
1、组件化的特性:
高内聚性,组建功能必须是完整的,如我要实现下拉菜单功能,那在下拉菜单这个组件中,就把下拉菜单所需要的所有功能全部实现。
低耦合度,通俗点说,代码独立不会和项目中的其他代码发生冲突。在实际工程中,我们经常会涉及到团队协作,传统按照业务线去编写代码的方式,就很容易相互冲突,所以运用组件化方式就可大大避免这种冲突的存在、每一个组件都有子集清晰的职责,完整的功能,较低的耦合便于单元测试和重复利用。
2、组件化的优点:
提高开发效率
方便重复使用
简化调试步骤
提升整个项目的可维护性
便于协同开
Vue生命周期
-
beforeCreate
(创建前) 在数据观测和初始化事件还未开始 -
created
(创建后) 完成数据观测,属性和方法的运算,初始化事件,$el属性还没有显示出来 -
beforeMount
(载入前) 在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。 -
mounted
(载入后) 在el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程中进行ajax交互。 -
beforeUpdate
(更新前) 在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。 -
updated
(更新后) 在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。 -
beforeDestroy
(销毁前) 在实例销毁之前调用。实例仍然完全可用。 -
destroyed
(销毁后) 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
Vue中的样式绑定
1、class的对象绑定
<style>
.activated{
color: red;
}
</style>
<div id='app'>
<div @click="handleDivClick" :class="{activated:isActivated}">
Hello World
</div>
</div>
<script>
var vm=new Vue({
el:'#app',
data:{
isActivated:false
},
methods:{
handleDivClick:function(){
this.isActivated=!this.isActivated;
}
}
});
</script>
2、class的数组绑定
<style>
.activated {
color: red;
}
.activated-one{
font-size: 25px;
}
</style>
<div id='app'>
<div @click="handleDivClick" :class="[activated,activatedOne]">
Hello World
</div>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
activated: "",
activatedOne:"activated-one"
},
methods: {
handleDivClick: function () {
this.activated = this.activated === "activated" ? "" : "activated";
}
}
});
</script>
3、style对象绑定
<div id='app'>
<div :style="styleObj" @click="handleDivClick">
Hello World
</div>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
styleObj:{
color:"black"
}
},
methods: {
handleDivClick:function(){
this.styleObj.color=this.styleObj.color==="black"?"red":"black";
}
}
});
</script>
4、style数组绑定
<div id='app'>
<!-- <div @click="handleDivClick" :class="[activated,activatedOne]">
Hello World
</div> -->
<div :style="[styleObj,{fontSize:'25px'}]" @click="handleDivClick">
Hello World
</div>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
styleObj:{
color:"black"
}
},
methods: {
handleDivClick:function(){
this.styleObj.color=this.styleObj.color==="black"?"red":"black";
}
}
});
</script>
深入理解Vue组件
组件细节
1、tbody、ul、select下只能存放特定的标签
<body>
<div id='app'>
<table>
<tbody>
<row></row>
<row></row>
<row></row>
</tbody>
</table>
</div>
<script>
Vue.component('row',{
template:'<tr><td>this is row</td></tr>'
})
var vm=new Vue({
el:'#app',
data:{
},
methods:{
}
});
</script>
</body>
上述代码出现问题,tr出现在了在<table><tbody></tbody></table>
外,因为<tbody>
里只能放tr
改:
<div id='app'>
<table>
<tbody>
<tr is="row"></tr>
<tr is="row"></tr>
<tr is="row"></tr>
</tbody>
</table>
</div>
2、子组件data为function
因为根组件一样只会被调用一次,子组件会被多次调用,而子组件每个需要自己的数据,所以不能是一个对象,必须是一个方法返回一个对象
Vue.component('row',{
data:function(){
return {
content:'this is content'
}
},
template:'<tr><td>this is row</td></tr>'
})
3、vue 操作DOM方式
vue不推荐操作DOM但有时候不得不去操作(复杂动画)
标签中
使用this.$refs.hello获得DOM
<body>
<div id='app'>
<div ref='hello' @click="handleClick">
Hello World
</div>
</div>
<script>
var vm=new Vue({
el:'#app',
data:{},
methods:{
handleClick:function(){
console.log(this.$refs.hello.innerHTML);
}
}
});
</script>
</body>
组件中
使用this.$refs.hello获得组件的引用
<body>
<div id='app'>
<!-- <div ref='hello' @click="handleClick">
Hello World
</div> -->
<counter @change="handleChange" ref="one"></counter>
<counter @change="handleChange" ref="two"></counter>
<div>{{total}}</div>
</div>
<script>
Vue.component('counter',{
data:function(){
return{
number:0
}
},
methods:{
handleClick:function(){
this.number ++;
this.$emit("change")
}
},
template:'<div @click="handleClick">{{number}}</div>'
})
var vm=new Vue({
el:'#app',
data:{
total:0
},
methods:{
// handleClick:function(){
// console.log(this.$refs.hello.innerHTML);
// }
handleChange:function(){
this.total=this.$refs.one.number+this.$refs.two.number
}
}
});
</script>
</body>
父子组件间的传值
注意单向数据流,不推荐改变父组件传递过来的数据
1、父组件向子组件传值
在父组件中使用属性定义需要传的值,子组件用props接收
:count="0"
传递的是一个数字(js表达式),若为count="0"
传递的是字符
2、子组件向父组件传值
子组件通过事件触发的形式向父组件传值
示例:
<div id='app'>
<counter :count="2" @inc="handleIncrease"></counter>
<counter :count="3" @inc="handleIncrease"></counter>
<div>{{total}}</div>
</div>
<script>
var counter={
props:[
'count'
],
data:function () {
return{
number:this.count
}
},
template:"<div @click='handleClick'>{{number}}</div>",
methods:{
handleClick:function(){
this.number=this.number+2;
this.$emit("inc",2)
}
}
}
var vm=new Vue({
el:'#app',
data:{
total:5
},
methods:{
handleIncrease:function(step){
this.total+=step;
}
},
components:{
counter:counter
}
});
</script>
组件参数校验
props: {
// content:[Number,String]
content: {
type: [Number, String],
required: false, //是否必传
default: "default value",
validator:function(value){
return(value.length>5)
}
}
}
组件绑定原生事件
1、子组件直接绑定
Vue.component('child', {
template: "<div @click='handleChildClick'>Child</div>",
props: {},
methods:{
handleChildClick:function(){
alert('child click')
}
}
})
2、给click
加上.native
<body>
<div id='app'>
<child @click.native="handleClick"></child>
</div>
<script>
Vue.component('child', {
template: "<div>Child</div>",
})
var vm = new Vue({
el: '#app',
data: {},
methods: {
handleClick:function(){
alert('click')
}
}
});
</script>
</body>
非父子组件间的传值
使用 Bus/总线/发布订阅模式 实现
<script>
Vue.prototype.bus=new Vue()//让以后创建的每个Vue示例都有一个共享的属性bus,且bus是你一个Vue实例
Vue.component('child', {
template: "<div @click='handleClick'>{{selfContent}}</div>",
data:function(){
return {
selfContent:this.content
}
},
props:{
content:String
},
methods:{
handleClick:function(){
this.bus.$emit('change',this.selfContent)//bus是个Vue实例所以有$emit()方法
}
},
mounted:function () {
var this_=this
this.bus.$on('change',function (msg) {//$on监听bus触发的事件
this_.selfContent=msg
})
}
})
var vm = new Vue({
el: '#app',
});
</script>
Vue中的插槽
原来:太low
<div id='app'>
<child content="<p>Gu</p>"></child>
</div>
<script>
Vue.component('child',{
props:['content'],
template:'<div><p>hello</p><div v-html="this.content"></div></div>'
})
var vm=new Vue({
el:'#app',
data:{},
});
</script>
1、普通插槽、具名插槽
<div id='app'>
<child>
<!-- <p>Gu</p> -->
<div class="header" slot="header">header</div>
<div class="footer" slot="footer">footer</div>
</child>
</div>
<script>
Vue.component('child',{
props:['content'],
template:'<div><slot name="header">默认内容</slot><p>hello</p><slot name=footer>默认内容</slot></div>'
})
var vm=new Vue({
el:'#app',
data:{},
});
</script>
2、作用域插槽
原来:
<div id='app'>
<child></child>
</div>
<script>
Vue.component('child',{
data:function(){
return{
list:[1,2,3,4]
}
},
template:'<div><ul><li v-for="item of list">{{item}}</li></ul></div>'
})
var vm=new Vue({
el:'#app',
data:{
},
});
</script>
但我们渲染什么需要外部告诉我们而不是child
子组件控制
作用域插槽应该由template
包裹 slot-scope
传过来的接收数据
<div id='app'>
<child>
<template slot-scope="props">
<li>{{props.item}}---hello</li>
</template>
</child>
</div>
<script>
Vue.component('child',{
data:function(){
return{
list:[1,2,3,4]
}
},
template:'<div><ul><slot v-for="item of list" :item=item></slot></ul></div>'//传入item数据
})
var vm=new Vue({
el:'#app',
data:{
},
});
</script>
动态组件与v-once
在组件里加上v-once
那么组件第一次加载时会被放到内存里,下次切换时会这从内存中取出,提高性能
<div id='app'>
<component :is="type"></component>
<!-- <child-one v-if="type==='child-one'"></child-one>
<child-two v-if="type==='child-two'"></child-two> -->
<button @click='handleBtnClick'>change</button>
</div>
<script>
Vue.component('child-one', {
template: '<div v-once>child-one</div>'
})
Vue.component('child-two', {
template: '<div v-once>child-two</div>'
})
var vm = new Vue({
el: '#app',
data: {
type:"child-one"
},
methods: {
handleBtnClick:function () {
this.type=this.type==='child-one' ? "child-two" : "child-one";
}
}
});
</script>
Vue中的动画
Vue中css动画的原理(过度动画)
<transition name="fade">
<div v-if="show">hello world</div>
</transition>
当一个元素被transition
包裹以后,vue会自动分析元素的css样式然后构建一个动画流程
在进入/离开的过渡中,会有 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
被删除),在过渡/动画完成之后移除。
<style>
.fade-enter,
.fade-leave-to{
opacity: 0;
}
.fade-leave-active,
.fade-enter-active{
transition: opacity 4s;
}
</style>
<div id='app'>
<transition name="fade">
<div v-if="show">hello world</div>
</transition>
<button @click="handleBtnClick">Button</button>
</div>
<script>
var vm=new Vue({
el:'#app',
data:{
show:true
},
methods:{
handleBtnClick:function(){
this.show=!this.show
}
}
});
</script>
使用animate
1、自己写:
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
.enter {
transform-origin: left center;
animation: bounce-in 1s;
}
.leave {
transform-origin: left center;
animation: bounce-in 1s reverse;
}
<div id='app'>
<transition enter-active-class="enter" leave-active-class="leave">
<div v-if="show">hello world</div>
</transition>
<button @click="handleBtnClick">Button</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleBtnClick: function () {
this.show = !this.show
}
}
});
</script>
2、使用animate.css库
<link rel="stylesheet" type="text/css" href="../../lib/animate/animate.css">
<div id='app'>
<!-- <transition enter-active-class="enter" leave-active-class="leave"> -->
<transition enter-active-class="animated swing" leave-active-class="animated shake">
<div v-if="show">hello world</div>
</transition>
<button @click="handleBtnClick">Button</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleBtnClick: function () {
this.show = !this.show
}
}
});
</script>
同时使用过度和动画
.fade-enter,
.fade-leave-to{
opacity: 0;
}
.fade-enter-active,
.fade-leave-active{
transition: opacity 1s;
}
<div id='app'>
<!-- type="transition"选择时长为过度动画时长 :duration="{enter:5000,leave:10000}"自定义动画时长-->
<transition
:duration="{enter:5000,leave:10000}"
name="fade"
appear
enter-active-class="animated swing fade-enter-active"
leave-active-class="animated shake fade-leave-active"
appear-active-class="animated swing">
<div v-if="show">hello world</div>
</transition>
<button @click="handleBtnClick">Button</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleBtnClick: function () {
this.show = !this.show
}
}
});
</script>
Vue中js动画与Velocity结合
1、自己写:
<div id='app'>
<transition name="fade"
@before-enter="handleBeforeEnter"
@enter="handleEnter"
@after-enter="handleAfterEnter">
<div v-if="show">hello world</div>
</transition>
<button @click="handleBtnClick">Button</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleBtnClick: function () {
this.show = !this.show
},
handleBeforeEnter:function (el) {
el.style.color="red";
},
handleEnter:function(el,done){
setTimeout(() => {
el.style.color="green";
}, 2000);
setTimeout(() => {
done();
}, 4000);
},
handleAfterEnter:function(el){
el.style.color="#000"
}
}
});
</script>
2、使用velocity.js库
<script src="../../lib/velocity/velocity.min.js"></script>
<div id='app'>
<transition name="fade" @before-enter="handleBeforeEnter" @enter="handleEnter" @after-enter="handleAfterEnter">
<div v-if="show">hello world</div>
</transition>
<button @click="handleBtnClick">Button</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleBtnClick: function () {
this.show = !this.show
},
handleBeforeEnter: function (el) {
el.style.opacity=0;
},
handleEnter: function (el, done) {
Velocity(el,{opacity:1},{duration:1000,complete:done})
},
handleAfterEnter: function (el) {
console.log("动画结束")
}
}
});
</script>
多个元素的过渡动画
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-leave-active,
.fade-enter-active {
transition: opacity 1s;
}
<div id='app'>
<!-- mode="in-out"元素先进入在隐藏 -->
<transition name="fade" mode="out-in">
<!-- 动态组件 -->
<component :is="type"></component>
<!-- <child-one v-if="show" key="one"></child-one>
<child-two v-else key="two"></child-two> -->
<!-- <div v-if="show" key="hello">hello world</div>
<div v-else key="bye">bey world</div> -->
</transition>
<button @click="handleBtnClick">Button</button>
</div>
<script>
Vue.component('child-one', {
template: '<div v-once>child-one</div>'
})
Vue.component('child-two', {
template: '<div v-once>child-two</div>'
})
var vm = new Vue({
el: '#app',
data: {
// show: true
type:"child-one"
},
methods: {
handleBtnClick: function () {
// this.show = !this.show
this.type=this.type==="child-one" ?"child-two":"child-one";
}
}
});
</script>
列表过渡动画
<style>
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-leave-active,
.fade-enter-active {
transition: opacity 1s;
}
</style>
<div id='app'>
<transition-group name="fade">
<div v-for="item of list" :key="item.id">{{item.title}}</div>
</transition-group>
<button @click="handleBtnClick">Add</button>
</div>
<script>
var count = 0;
var vm = new Vue({
el: '#app',
data: {
list: []
},
methods: {
handleBtnClick: function () {
this.list.push({
id: count++,
title: "hello world " + (count - 1)
})
}
}
});
</script>
动画封装
1、css封装
.v-enter,
.v-leave-to {
opacity: 0;
}
.v-leave-active,
.v-enter-active {
transition: opacity 2s;
}
<div id='app'>
<fade :show="show">
<div>Hello World</div>
</fade>
<button @click="handleBtnClick">Button</button>
</div>
<script>
Vue.component("fade",{
props:['show'],
template:'<transition><slot v-if="show"></slot></transition>'
})
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleBtnClick: function () {
this.show = !this.show
}
}
});
</script>
2、js封装
<div id='app'>
<fade :show="show">
<div>Hello World</div>
</fade>
<fade :show="show">
<h1>Hello World</h1>
</fade>
<button @click="handleBtnClick">Button</button>
</div>
<script>
Vue.component("fade",{
props:['show'],
template:'<transition @before-enter="handleBeforeEnter" @enter="handleEnter"><slot v-if="show"></slot></transition>',
methods:{
handleBeforeEnter:function(el){
el.style.color="red";
},
handleEnter:function(el,done){
setTimeout(() => {
el.style.color="green";
done();
}, 2000);
}
}
})
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleBtnClick: function () {
this.show = !this.show
}
}
});
</script>
学习到这路你的基础基本已经掌握,可以看下一张实战
https://blog.csdn.net/Gueyue/article/details/102453943
转载:https://blog.csdn.net/Gueyue/article/details/102453047