Vue的学习笔记
一 Vue基本
1. Vue的设计理念
vue要做一个渐进式框架,它给你提供足够的选项,但并不主张很多必须要求,它承担了较难的做减法的部分,而留给用者较简单的做加法的部分。
Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。
所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。
在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。
关于响应式,我个人理解Vue的响应式开发主要体现在双向绑定方面,所有的数据、样式、属性我们都可以动态的进行变化,
Vue响应式原理
2. Vue的区域性
举个例子
{{ message }}
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
当我们创建这样一个Vue实例后,它就挂在到了一个id为app的dom对象上,我们就不再需要和HTML直接交互了,我们仅仅需要利用Vue实例操作即可
3 Vue提供的指令(绑定到元素的属性)
v-on(简写@)监听事件
v-on 指令可以监听DOM 事件 比如click,enter,mouseover
,我们在vue中可以通过body里传入&event的方式间接来获取事件信息.按键修饰符
```
new Vue({
el:"#app",
methods:{
fun:function(event){
var keyCode=event.keyCode
if(keyCode>=48&&keyCode<=57||keyCode>=97&&keyCode<=105||keyCode==8){
event.getPreventDefault();//键盘有效,允许输入
}else{
event.preventDefault()//键盘无效,不允许输入
}
}
}
})
```
v-bind(简写:):给标签的属性赋值
我们传统方式,如用插值表达式{{}}是无法给我们我属性赋值的颜色1
所以我们要想在js中动态给标签里的属性赋值,我们需要用到v-bind:
我们可以在属性前面加v-bind: 代表后面的属性值是从我们的vue对象中取得
如下
v-model
颜色1
颜色2
颜色3
颜色4
另外V-bind支持对属性支持多次赋值以及三目运算符以及.
- 对象赋值
- 数组赋值
绑定类方式:对象语法
用法一:直接通过{}绑定一个类
Hello World
用法二:也可以通过判断,传入多个值
Hello World
用法三:和普通的类同时存在,并不冲突
注:如果isActive和isLine都为true,那么会有title/active/line三个类
Hello World
用法四:如果过于复杂,可以放在一个methods或者computed中
注:classes是一个计算属性
Hello World
绑定类方式:数组语法
数组语法的含义是:class后面跟的是一个数组。
用法一:直接通过{}绑定一个类
Hello World
用法二:也可以传入多个值
Hello World
用法三:和普通的类同时存在,并不冲突
注:会有title/active/line三个类
Hello World
用法四:如果过于复杂,可以放在一个methods或者computed中
注:classes是一个计算属性
Hello World
一般不用这个,因为意义不大,和我们直接用class=""差不多
而我们用对象语法时候比较灵活比如:class="{active:true,line:false}",我们可以通过一些如动态判断以及绑定进行比较灵活的配置.
v-bind绑定style
- 绑定方式一:对象语法
:style="{color: currentColor, fontSize: fontSize + 'px'}"
style后面跟的是一个对象类型,对象的key是CSS属性名称,对象的value是具体赋的值,值可以来自于data中的属性 - 绑定方式二:数组语法
style后面跟的是一个数组类型,多个值以,分割即可
v-text v-html 向元素加载数据
我们用jquery通常向页面写有两种方式
加载时不被解析的 document.getElementById("div1").innerText=""
加载时被解析的 document.getElementById("div1").innerHTML=""
而我们可以在标签内使v-text和v-html当作属性来使用
html进行渲染
html不进行渲染
mes1=`百度一下`
v-if v-show控制是否元素显示
`v-if v-show都是靠后面的值来决定是否显示的
-
v-if
的特点:每次都会重新删除或创建元素 -
v-show
的特点: 每次不会重新进行DOM的删除和创建操作,只是切换了元素的 display:none 样式
v-if 有较高的切换性能消耗
v-show 有较高的初始渲染消耗
v-else v-else-if
v-else
元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,否则它将不会被识别。
v-else-if
v-else-if
,顾名思义,充当v-if
的“else-if
块”,可以连续使用:
类似于 v-else
,v-else-if
也必须紧跟在带 v-if
或者 v-else-if
的元素之后。
A
B
C
Not A/B/C
注意 : 这里有个问题
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使 Vue 变得非常快之外,还有其它一些好处。例如如下例子中,如果你允许用户在不同的登录方式之间切换:
有时候我们不想这样,那么Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需在不想复用的内部元素上添加一个具有唯一值的 key
attribute即可
如上,我们
v-model用于实现数据双向绑定以及预设值
我们的差值表达式是无法在标签属性栏中使用的,这也就意味着,我们无法给某些文本框之类的直接预设值.
4. 计算属性
使用模板应该注重其中只是简单的声明式逻辑,我们使用计算属性可以避免我们模板语法中出现复杂函数段,反面例子如下{{message.split('').reverse().join('') }}
正面例子
Original message: "{{ message }}"
Computed reversed message: "{{ reversedMessage }}"
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 我们定义的Vue实例vm
return this.message.split('').reverse().join('')
}
}
})
为啥Methods里定义方法也可以实现"同样效果",我们还用计算属性
?
原因是计算属性是基于它们的响应式依赖进行缓存的。
只要我们依赖的数据没发生变化,那么相同的调用计算属性会直接返回数据,这在我们大量计算中会省掉很多性能开销,比如计算一个大数组的数据的和等等.
计算属性默认只有 getter,不过在需要时你也可以提供一个 setter,这是一个当我们向属性赋值后计算属性get前的中间阶段.
5.监听器
Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,监听器是最有用的。对于监听器和计算属性的选择---如果有一些操作是重复的类似的,我们可以定义一个计算属性并在内部做细节判断处理,这样可以避免滥用watch.
6.过滤器
Vue允许自定义过滤器,可被用于常见的文本格式化。比如有时候我们想要对后台传过来的数据做些处理,这就需要过滤器了,过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 。过滤器应该被添加在 JavaScript 表达式的尾部,由
管道符号指示:
data|filtername
具体使用实例方法如下
{{ message | capitalize }}
注意:
- 1.当全局过滤器和局部过滤器重名时,会采用局部过滤器。
- 2.过滤器可以串联,
{{ message | filterA | filterB }}
,处理结果以此往后背过滤处理 - 3.过滤器是 JavaScript 函数,因此可以接收参数:
{{ message | filterA('arg1', arg2) }}
,这里message 的值作为第一个参数,普通字符串 'arg1' 作为第二个参数,表达式 arg2 的值作为第三个参数。 - 4.关于过滤器的声明位置:
- 可以在一个组件的选项中定义本地的过滤器:
- 创建 Vue 实例之前定义全局定义过滤器
- 以上例子可以参考栗子
过滤器和计算属性功能非常类似,关于他俩的区别如下:
计算属性 | 过滤器 |
---|---|
依赖于一个固定的vue实例 ,在某一个实例中使用 | 不依赖于实例。可以定义一个全局过滤器,在多个实例中使用 |
不接受额外参数,依赖于data属性中的变量 | 不要求是data中的变量,可以是临时变量。可接受额外参数。 |
有缓存管理机制,可减少页面调用次数 | 无缓存机制,调用次数,取决于页面中有所多少过滤器 |
计算属性虽默认为只读,但可以定义为对象,开启可读可写模式 | 只能读取操作 |
计算属性被作为一个类属性调用 | 过滤器被作为一个特殊方法处理 |
相同点:
都必须有返回值
二 组件
1. Vue组件基础
1.1 组件名
推荐使用 组件名 用-分割(单词全部使用小写,单词间使用短横线-分割)命名的方式,而不是驼峰命名方式
因为html大小写不敏感,在dom操作中使用驼峰命名会报错,另外在自定义事件中,事件名也应该使用短横线分割方法命名,如果将事件用v-on:myEvent 命名,就会变成v-on:myevent. 导致监听不到事件,所以用v-on:my-event 。
1.2组件模板
以前我们使用 Vue.component
来定义全局组件,紧接着用new Vue({ el: '#container '})
,但这样有许多缺点,比如每个 component 中的命名不得重复,不支持CSS等
Vue基础模板如下
html代码
关注点分离不等于文件类型分离。Vue认为相比于把代码库分离成三个大的层次 html css js
并将其相互交织起来,把它们划分为松散耦合的组件
再将其组合起来更合理一些。在一个组件里,其模板、逻辑和样式是内部耦合的,并且把他们搭配在一起实际上使得组件更加内聚且更可维护。
即便你不喜欢单文件组件,仍然可以把 JavaScript、CSS 分离成独立的文件然后做到热重载和预编译。如下
dddd
1.3 组件注册
- 全局注册
采用Vue.component
来创建组件的组件是全局注册的。可以用在任何新创建的Vue 根实例
和其子组件
中,可以采用new Vue({ el: '#app' })
进行声明,如下
Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })
new Vue({ el: '#app' })
- 局部注册
全局注册后即使我们不用它仍然会被包含在最终的构建结果中,造成用户下载的 JavaScript 的无谓的增加。
我们可以使用如下方式进行生命和注册.
var ComponentA = { /* ... */ }
import ComponentA from './ComponentA.vue'
export default {
components: {
ComponentA(等同于'componentA': ComponentA,这要求其同时是用在模板中的自定义元素的名称也是包含了这个组件选项的变量名)
},
}
2. Vue父子组件之间的通讯
一 父组件向子组件通过props传递数据
在组件中,使用选项
props
来声明需要从父级接收到的数据。(props里的东西其实就是一些等待接受父组件传数据的对象)
props的值有两种方式:
方式一:字符串数组,数组中的字符串就是传递时的名称。
方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。
Prop 是你可以在组件上注册的一些自定义 attribute。
当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property。
1. props值为数组时候
为了给博文组件传递一个标题,我们可以用一个 props 选项将其包含在该组件可接受的 prop列表中:
Vue.component('blog-post', {
props: ['title'],
template: '{{ title }}
'
})
一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。在上述模板中,你会发现我们能够在组件实例中访问这个值,就像访问data
中的值一样。
一个 prop 被注册之后,你就可以像这样把数据作为一个自定义 attribute 传递进来,在这里我们直接用k-v对显示了值,而没有进行v-bind的动态绑定(下面有介绍):
然而在一个典型的应用中,你可能在 data 里有一个博文的数组:
new Vue({
el: '#blog-post-demo',
data: {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
]
}
})
并想要为每篇博文渲染一个组件:
从以上的例子我们可以看出来,如果我们想动态传值
或者在传值之前做一定的计算时候可以使用v-bind绑定要传递的数据。每当父组件的数据变化时,该变化也会传导给子组件。
2. props值为对象时候
通常我们希望每个 prop 都有指定的值类型。这时,我们可以以对象形式列出 prop,这些 property 的名称和值分别是 prop 各自的名称和类型:
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
3. 关于props值为对象时候,我们可以对传入的数据做校验
或者说验证
我们可以为组件的 prop 指定验证要求,例如你知道的这些类型。如果有一个需求没有被满足,则 Vue 会在浏览器控制台中警告你。这在开发一个会被别人用到的组件时尤其有帮助。
3.1.在props中我们可以传一个值做一个对象元素传入,对其做三个限定
.如下如代码中的name
- type 约定该元素类型
- default 约定默认值(如果父组件不传入值的话将直接使用默认值)
- required 约定是否要求必须传入(如果要求了父组件没有传入则会报错)
props: {
title:String,
propB: [String, Number],
name:{
type:String,
default:"zyh",
required:true
}
}
3.2 我们可以对传入的值约定多个可能类型
如propB: [String, Number]
3.如果我们要求传入的数据为对象或者数组,那么默认值需要用工厂函数获取
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
}
注意:HTML 中的 attribute 名是大小写不敏感的,驼峰命名法的 prop 名,我们在父组件传的时候需要使用其等价的 kebab-case (短横线分隔命名) 命名
二 子传父---通过监听子组件事件传递数据和信号给父组件
关于事件名
不同于组件和 prop,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称。
举个例子,如果触发一个 camelCase 名字为的事件:this.$emit('myEvent')
则监听这个名字的 kebab-case 版本是不会有任何效果的:
不同于组件和 prop,事件名不会被用作一个 JavaScript 变量名或 property 名,所以就没有理由使用 camelCase 或 PascalCase 了。
并且v-on
事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的),所以 v-on:myEvent
将会变成 v-on:myevent
——导致 myEvent
不可能被监听到。
因此,Vue官方推荐始终使用 kebab-case 的。
自定义事件的流程:
- 在子组件中,通过$emit来触发事件。
- 在父组件中,通过v-on来监听子组件事件。
自定义组件的 v-model
一个组件上的 v-model
默认会利用名为 value
的 prop 和名为 input
的事件,但是像单选框、复选框等类型的输入控件可能会将 value
attribute 用于不同的目的。model
选项可以用来避免这样的冲突:
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
`
})
现在在这个组件上使用 v-model 的时候:
这里的 lovingVue 的值将会传入这个名为 checked 的 prop。同时当 change
事件并附带一个新的值的时候,这个 lovingVue 的 property 将会被更新。
注意你仍然需要在组件的 props 选项里声明 checked 这个 prop。
关于子组件向父组件传参数量问题
$emit传递一个参数时
子组件:
this.$emit('closeChange',false);
父组件:
closeCom(msg) {
this.msg = msg;
}
$emit传递多个参数时,父组件接受参数类似于一个数组
子组件:
this.$emit('closeChange',false,true);
父组件:
closeCom(msg) {
this.msg1 = msg[0];
this.msg2 = msg[1];
}
2. Vue父子组件的访问方式
如果我们不需要利用父子组件通信去交换什么数据或者信号,我们仅仅需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问根组件,从而可以相互得到对方组件里的数据和方法
,那么不必用之前的prop和自定义事件.
2.1 .Vue提供了一些的方法可以达到父子互相访问的效果.
- 父组件访问子组件:使用
this.$children
或$refs
- this.$children得到是一个
子组件数组
,它包含所有子组件对象。
- this.$children得到是一个
- 子组件访问父组件:使用
this.$parent
- 子组件访问根Vue实例:使用
this.$root
2.2 .父组件访问子组件:使用this.$children
或this.$refs
-
$children (批量获得子组件)
我们在父组件js中使用$children可以获得所有的子组件
,该组件所有的子组件为成为一个数租里的元素,我们可以通过该数组一个个的访问子组件,
缺点:无法特指某一个组件,优点:可以一次性获取所有的子组件
我们使用this.$children
得到的是所有的子组件组成的数组,我们可以用其获得一些组件内的数据比如this.$children[0].name,是获取第一个子组件定义的name属性的值.
我们也可以用如下for函数来遍历每一个子组件元素,调用子组件的getValue(方法);
for (let c of this.$children) {
console. log(c. getValue())
}
-
$refs (常用)
如上我们所看,$children
有一个缺点,无法特指某一个组件,如果我们想获得某一组件必须用$children[索引号]去特别指定,但是我们开发时候可能随时来需求改组件,那么组件位置或者序号就会变化,那么通过原来的索引号就无法正常取得数据了
因此$refs
就显得十分必要,我们可以在子组件引用时在其标签上加上引用名去唯一标识这个子组件,然后即可在父组件中通过this.$refs.引用名.属性名/方法名去获取特定组件的属性值或者调用其方法.如下图通过点击父组件内的button按钮调用子组件的方法.
3.子组件访问父组件 this.$parent
如下图,我们可以通过`$this.parent'获取父组件,可以继续往下调用父组件的属性和方法.
但是我们开发时候一般不建议这样使用,因为我们用组件化开发一般是为了复用,如果我们在组件内定义了调用父组件的方法,但是用在不同位置的组件有不同的父组件,这样就会出现问题了.
4 子附件访问根组件 this.$root
这样获得的是根Vue实例,可以当做使用父组件一样使用,调用方式如下3 插槽
1 .插槽的作用
- 让我们封装的组件更加具有扩展性。
- 让使用者可以决定组件内部的一些内容到底展示什么。
2 插槽的类别:普通插槽,具名插槽,作用域插槽
2.1单个普通插槽
使用作用域插槽其实就是抽取共同点,保留不同点的坑位(插槽),让父组件去扩展定制.
代码举个栗子
全部
插到插槽里,如
我们也可以在组件内部模板定义插槽时候写一些默认值,比如
暂时无法访问>
,但是一旦我们在父组件用的时候写了插槽的值,那么默认值将被替换.
2.2 作用域插槽
作用域插槽的作用核心思想:
有时让插槽内容能够访问子组件中才有的数据是很有用的。例如,设想一个带有如下模板的
{{ user.lastName }}
我们可能想换掉备用内容,用名而非姓来显示。如下:
{{ user.firstName }}
然而上述代码不会正常工作,因为只有
为了让 user 在父级的插槽内容中可用,我们可以将 user 作为
{{ user.lastName }}
绑定在
{{ slotProps.user.firstName }}
在这个例子中,我们选择将包含所有插槽 prop 的对象命名为 slotProps,但你也可以使用任意你喜欢的名字。
2.3 .具名插槽----多个插槽的使用
跟 v-on
和 v-bind
一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:
) 替换为字符 #
。例如 v-slot:header
可以被重写为 #header
:
Here might be a page title
A paragraph for the main content.
And another one.
Here's some contact info
然而,和其它指令一样,该缩写只在其有参数的时候才可用。这意味着以下语法是无效的:
{{ user.firstName }}
如果你希望使用缩写的话,你必须始终以明确插槽名取而代之:
{{ user.firstName }}
其它示例
插槽 prop 允许我们将插槽转换为可复用的模板,这些模板可以基于输入的 prop 渲染出不同的内容。这在设计封装数据逻辑同时允许父级组件自定义部分布局的可复用组件时是最有用的。
例如,我们要实现一个
组件,它是一个列表且包含布局和过滤逻辑:
-
{{ todo.text }}
我们可以将每个 todo 作为父级组件的插槽,以此通过父级组件对其进行控制,然后将 todo
作为一个插槽 prop 进行绑定:
-
{{ todo.text }}
现在当我们使用
组件的时候,我们可以选择为 todo 定义一个不一样的 作为替代方案,并且可以从子组件获取数据:
✓
{{ todo.text }}
这只是作用域插槽用武之地的冰山一角。想了解更多现实生活中的作用域插槽的用法,我们推荐浏览诸如 Vue Virtual Scroller、Vue Promised 和 Portal Vue 等库。