目录
  1. 1. 学习内容
  2. 2. 学习笔记
    1. 2.1. 父子组件间的通信
    2. 2.2. v-text v-html 插值表达式
    3. 2.3. 计算属性,方法,监听
    4. 2.4. Vue中的样式绑定
    5. 2.5. Vue中的条件渲染(v-if,v-show)
    6. 2.6. Vue中的列表渲染;vue中的set方法
    7. 2.7. 组件使用中的细节点
    8. 2.8. 给组件绑定原生事件(.native)
    9. 2.9. 非父子组件间传值(Bus/总线机制/发布订阅模式/观察者模式;Vuex)
    10. 2.10. Vue中的插槽(slot)
    11. 2.11. 动态组件(component)和v-once
    12. 2.12. Vue中CSS动画原理
    13. 2.13. Vue中使用animate.css库;appear初次渲染;:duration显性持续时间
    14. 2.14. Vue中列表过渡
    15. 2.15. Vue中的动画封装
Vue2.5学习笔记

学习内容

1
2
3
4
5
6
7
8
graph TD
A[基础内容] --> B[基础语法[]
B --> C[MVVM模式]
C --> D[组件化]
E[生命周期] --> F[动画特效]
G[实战项目] --> H[环境搭建] --> I[使用Git] --> J[数据模拟] --> K[本地开发]
L[联调] --> M[真机测试] --> N[上线]
O[学习内容流程图]

学习笔记

MVVM模式:面向数据开发;MVP模式:面向dom开发

父子组件间的通信

父子组件通信是单向数据流的,子组件不能改父组件传过来的值会有警告,因为传过来的值可能别的组件也在用会影响

  • props参数校验
1
2
3
4
5
6
7
8
9
10
11
12
props:{content:String}
props:{content:[String,Number]}
props:{
content:{
type:String,
required:false,
default:"default value",
validator:function(value){//校验器
return (value.lenght>5)
}
}
}
  • props特性和非props特性

    • props特性(父组件传,子组件props参数接收):

      1.可以直接在子组件使用
      2.不会把传递的属性在dom中显示

    • 非props特性(父组件传,子组件props参数不接收):

      1.不可以在子组件中使用
      2.会把属性在dom中显示出来

  • 父-子组件通信:

    • 静态Prop(传递的是字符串)

      父:a=”val”
      子:props:[‘a’]

    • 动态Prop(传递的是JS表达式)

      父:v-bind:a=”val”
      子:props:[‘a’]

  • 子-父组件通信:

    子:@click=’事件1’ methods:{事件1(){this.$emit(‘事件2’,this.要传的值)}}
    父:监听事件2=> v-on:事件2=”事件3”(@事件2=”事件3”) methods:{事件3(子传来的值){监听后要执行的逻辑}}
    注:v-bind绑定的属性内容是js表达式:content="123"传过去的就是数字123;content="123"传过去的就是字符串123

1
2
3
4
5
6
7
8
9
10
11
12
<div id="app">
<input type="text" v-model="todoValue">
<button @click="handleBtnClick">提交</button>
<ul>
<todo-item :content="item"
:index="index"
:key="index"
@delete="handleItemDelete"
v-for="(item,index) in list"
></todo-item>
</ul>
</div>
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
// Modle:
var TodoItem = {
props:['content','index'],//接收父组件传值
template:"<li @click='handleItemClick'>{{content}}</li>",
methods:{
handleItemClick(){
this.$emit('delete',this.index);//向父组件传值
}
}
}//局部组件
//注册vue实例
var app = new Vue({
el:"#app",
//注册组件
components:{
TodoItem:TodoItem
},
data:{
list:[],
todoValue:""
},
methods:{
handleBtnClick: function(){
this.list.push(this.todoValue);
this.todoValue = "";
},
handleItemDelete: function(index){
this.list.splice(index,1);
}
}
})

v-text v-html 插值表达式

v-text和插值表达式作用一样;
v-html不同在于对标签不会进行转义,会把标签渲染在dom中

1
2
3
4
5
<div id="app">
<p v-html="msg"></p><!-- 显示dom结构 -->
<p v-text="msg"></p><!-- 显示字符串 -->
<p>{{msg}}</p><!-- 显示字符串 -->
</div>
1
2
3
4
5
6
var app = new Vue({
el:'#app',
data:{
msg:"<h1>一级标题</h1>"
}
})

计算属性,方法,监听

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="app">
<!-- 方法 -->
<!-- {{fullName()}}
{{age}} -->

<!-- 计算属性 -->
{{fullName}}
{{age}}

<!-- 监听 -->
<!-- {{fullName}}
{{age}} -->
</div>
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
data:{
//方法
// firstName:"Ray",
// lastName:"Lee",

//计算属性
firstName:"Ray",
lastName:"Lee",

//监听
// firstName:"Ray",
// lastName:"Lee",
// fullName:"",
},
//方法(没有缓存,改变age会重新计算)
// methods:{
// fullName:function(){
// console.log("计算了一次");
// return this.firstName + " " + this.lastName;
// }
// }
//计算属性(有缓存-性能更高,改变age并不会重新计算)
computed:{
fullName:function(){
console.log("计算了一次");
return this.firstName + " " + this.lastName;
},
//计算属性的get,set
// fullName:function(){
// get:function(){
// console.log("计算了一次");
// return this.firstName + " " + this.lastName;
// },
// set:function(value){
// var arr = value.split(' ');
// this.firstName = arr[0];
// this.lastName = arr[1];
// }
// }
}
//监听(有缓存)
// watch:{
// firstName:function(){
// console.log("计算了一次");
// this.fullName = this.firstName + " " + this.lastName;
// },
// lastName:function(){
// console.log("计算了一次");
// this.fullName = this.firstName + " " + this.lastName;
// }
// }

Vue中的样式绑定

  1. class的对象绑定:class="{activated: isActivated}"通过控制class类名的显隐改变样式
  2. class的数组绑定:class="[activated,activatedOne,......]"class类名取数组内变量的值
  3. style样式绑定::style="styleObj" :style="[styleObj,{fontSize:'20px'}]"通过改变styleObj对象的值控制样式

Vue中的条件渲染(v-if,v-show)

  • v-show和v-if的区别:

false时:v-show中dom存在,只是display:none;
v-if在dom中消失
性能上v-show更好,不会重复渲染dom

1
2
3
4
5
6
7
8
<!-- 正在更新已渲染过的元素列表时,它默认用“就地复用”策略
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。-->
<div v-if="show">
用户名:<input type="text" key="username">
</div>
<div v-else>
邮箱:<input type="text" key="email">
</div>

Vue中的列表渲染;vue中的set方法

  • 数组改变实时渲染的方法

    1.使用数组的七个变异方法pop()--删除数组最后一项返回删除的值;push()--数组结尾处添加一项返回新数组的长度;shift()--删除首个数组元素返回删除的值;unshift()--数组开头添加一项返回新数组的长度;splice()--拼接数组,第一个参数定义了应添加新元素的位置(拼接)。第二个参数定义应删除多少元素,其余参数定义要添加的新元素,返回包含已删除元素(如果有)的数组;sort()--对数组的元素进行排序返回排序后的数组;reverse()--反转数组改变数组才能实时渲染页面,使用数组下标方式无法实时渲染。
    变异方法 (mutation method),顾名思义,会改变被这些方法调用的原始数组
    2.改变数组的引用:vm.list = [1,2,5]
    3.Vue.set(vm.list,2,5)vm.$set(vm.list,2,5)

  • 对象改变实时渲染的方法:

    1.改变对象的引用vm.userInfo = {name:"ray",age:22,adderss:"beijing"}
    2.Vue.set(vm.userInfo,"address","beijing")vm.$set(vm.userInfo,"address","beijing")

使用template占位符进行列表渲染,template标签不会在dom中渲染出来,提高渲染性能

组件使用中的细节点

  • table中规定<tbody>下必须是<tr></tr>;ul,ol标签下规定是li标签;select标签下规定是option标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 使用组件row要借助is属性 -->
<table>
<tbody>
<tr is="row"></tr>
<!-- Vue.component('row',{
template:`<tr><td>this is row</td></tr>`
}) -->
</tbody>
</table>
<ul>
<li is="row"></li>
</ul>
<select>
<option is="row"></option>
</select>
  • 在非根组件中data的定义要是一个函数data(){retrun {content:"this is content"}};目的是让子组件数据独立存储互不影响

  • ref属性写在标签上是获取的是dom元素,组件上ref获取的是组件的引用

1
2
3
4
5
6
7
8
9
10
11
<!-- ref属性写在标签上获取的是dom元素 -->
<div ref="hello" @click="handleClick">
hello world
</div>
methods:{
handleClick(){
console.log(this.$refs.hello.innerHTML)
}
}
<!-- 组件中的ref获取的就是组件的引用 -->
<item ref="hello"></item>
1
2
3
4
5
6
7
8
9
10
11
12
13
Vue.component("item",{
template:"<div @click="handleClick">{{content}}</div>",
data(){
return {
content:"this is content"
}
}
})
methods:{
handleClick(){
console.log(this.$refs.hello.content)//this is content
}
}

给组件绑定原生事件(.native)

在组件中绑定的是自定义事件,<child @click="handleClick"></child>点击并不会触发,要想触发子组件必须用this.$emit("click")
给组件绑定原生事件<child @click.native="handleClick"></child>点击就能触发

非父子组件间传值(Bus/总线机制/发布订阅模式/观察者模式;Vuex)

  • Bus/总线/发布订阅模式/观察者模式
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
//View:实现点击让另一个变成同样的值
<child content="ray"></child>
<child content="lee"></child>
//Model:
Vue.prototype.bus = new Vue();//创建bus总线让它有vue的所有方法
Vue.component("child",{
data:function(){
return {
selfContent: this.content//因为是单向数据流不能改变父组件传递的值,所以要保存自身content
}
},
props:{
content: String
},
template: "<div @click="handleClick">{{selfContent}}</div>",
methods:{
handleClick:function(){
this.bus.$emit("change",this.selfContent)
}
},
mouted: function() {
var this_ = this;//保存this指向
this.bus.$on("change",function(msg){//监听change
this_.selfContent = msg;
})
}
})
  • VueX(有待补充)

Vue中的插槽(slot)

在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了 slot 和 slot-scope 这两个目前已被废弃但未被移除且仍在文档中的特性

  • 2.6.0之后的插槽写法

    • 具名插槽

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      <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>

      <template v-slot:footer>
      <p>Here's some contact info</p>
      </template>
      </base-layout>
      <!-- 现在 <template> 元素中的所有内容都将会被传入相应的插槽。任何没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容 -->
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      Vue.component("base-layout",{
      template: `<div class="container">
      <header>
      <slot name="header"></slot>
      </header>
      <main>
      <slot></slot>
      </main>
      <footer>
      <slot name="footer"></slot>
      </footer>
      </div>`
      })
    • 作用域插槽

      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
      <current-user>
      <!-- 父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字 -->
      <template v-slot:default="slotProps">
      {{ slotProps.user.firstName }}
      </template>
      </current-user>

      <!-- 独占默认插槽的缩写 start-->
      <!-- 当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。这样我们就可以把 v-slot 直接用在组件上。不带参数的 v-slot 被假定对应默认插槽 -->
      <current-user v-slot="slotProps">
      {{ slotProps.user.firstName }}
      </current-user>
      <!-- 默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确。
      只要出现多个插槽,请始终为所有的插槽使用完整的基于 <template> 的语法 -->
      <!-- 独占默认插槽的缩写 end-->

      <!-- 解构插槽Prop start-->
      <!-- es6解构赋值 -->
      <current-user v-slot="{ user }">
      {{ user.firstName }}
      </current-user>
      <!-- 定义默认内容,用于插槽 prop 是 undefined 的情形 -->
      <current-user v-slot="{ user = { firstName: 'Guest' } }">
      {{ user.firstName }}
      </current-user>
      <!-- 解构插槽Prop end-->
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      Vue.component("current-user",{
      data (){
      return (){
      user:{
      firstName:"lee",
      lastName:"ray"
      }
      }
      },
      template: `<span>
      <slot v-bind:user="user">
      {{ user.lastName }}
      </slot>
      </span>`
      })
      //为了让 user 在父级的插槽内容中可用,我们可以将 user 作为 <slot> 元素的一个 attribute 绑定上去被称为插槽 prop
  • 2.6.0之前:插槽;具名插槽(已废弃)

1
2
3
4
5
6
<!-- View:优雅的传递Dom结构 -->
<body-content>
<div class="header" slot="header"></div>
<!-- 要插入的dom,为避免插槽重复接收,使用具名插槽,用slot接收 -->
<div class="footer" slot="footer"></div>
</body-content>
1
2
3
4
5
6
7
8
// Model:插槽可以写默认内容--<slot>默认内容</slot>
Vue.component("body-content",{
template: `<div>
<slot name="header">default header</solt>
<div class="content">content</div>
<slot name="footer"></solt>
</div>`
})
  • 2.6.0之前:作用域插槽(已废弃)
1
2
3
4
5
6
7
<!-- View:当子组件做循环或者某一部分dom结构应该要外部传递进来的时候就需要使用作用域插槽 -->
<child>
<template slot-scope="props">
<!-- 作用域插槽必须由template标签包裹,solt-scope接收子组件插槽传递的值 -->
<h1>{{props.item}}</h1>
</template>
</child>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Model:
Vue.component("child",{
data: function(){
return {
list: [1, 2, 3, 4]
}
},
template: `<div>
<ul>
<slot v-for="item in list" :item="item"></slot>
</ul>
</div>`
})
//你也可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法

动态组件(component)和v-once

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//View:实现一个toggle
// <component is="type"></component>
// <button @click="handleClick"></button>
//Model:
//v-once只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
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: "#root",
data:{
type: "child-one"
},
methods:{
handleClick: function() {
this.type = (this.type === "child-one" ? "child-two" : "child-one");
}
}
})

Vue中CSS动画原理

Vue 提供了transition的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡

  • 条件渲染 (使用 v-if)
  • 条件展示 (使用 v-show)
  • 动态组件
  • 组件根节点
1
2
3
4
5
6
<transition>
<p v-if="show">hello world</p>
</transition>
<button v-on:click="show = !show">
Toggle
</button>
1
2
3
4
5
6
.v-enter,.v-leave-to{
opacity: 0;
}
.v-enter-active, .v-leave-active {
transition: opacity .5s;
}

Vue中使用animate.css库;appear初次渲染;:duration显性持续时间

1
2
3
4
5
6
7
8
9
10
11
12
13
<transition
:duration="{enter:5000,leave:10000}"
name="donghua"
appear
appear-active-class="animated swing"
enter-active-class="animated swing"
leave-active-class="animated shake"
>
<div v-show="show">hello world</div>
</transition>
<button @click="show = !show">
Toggle
</button>

Vue中列表过渡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<style>
.v-enter,.v-leave-to{
opacity:0;
}
.v-enter-active,.v-leave-active{
transition:opacity 1s;
}
</style>
<transition-group>
<div v-for="item of list" :key="item.id">
{{item.title}}
</div>
</transition-group>
<button @click="handleAdd">add</button>
<button @click="handleDel">del</button>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var vm = new Vue({
el:'#app',
data:{
count:0,
list:[]
},
methods:{
handleAdd(){
this.list.push({
id:this.count++,
title:'hello'
})
},
handleDel(){
this.list.pop();
this.count--;
}
}
})

Vue中的动画封装

1
2
3
4
5
6
7
8
9
<div id="app">
<fade :show="show">
<div>hello</div>
</fade>
<fade :show="show">
<h1>hello</h1>
</fade>
<button @click="handleBtnClick">Toggle</button>
</div>
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
Vue.component('fade',{
props:['show'],
template:`
<transition @before-enter="handleBeforeEnter" @enter="handleEnter">
<slot v-if="show"></slot>
</transition>
`,
methods: {
handleBeforeEnter(el){
el.style.color = 'red';
},
handleEnter(el,done){
setTimeout(() => {
el.style.color = 'yellow';
done();
},2000)
}
},
})
var vm = new Vue({
el:"#app",
data:{
show:false
},
methods: {
handleBtnClick(){
this.show = !this.show;
}
}
})
文章作者: Ray
文章链接: https://lirui9825.github.io/2020/01/08/Vue2.5%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ray's Blog
打赏
  • 微信
  • 支付寶