Vue 是一套用于构建用户界面的 渐进式框架
渐进式框架的意思就是,我们可以在项目中一点点的引入和使用 Vue
而不一定要使用 Vue 来开发整个项目
Vue 的本质就是一个 JavaScript 库
我们在使用 Vue 时,需要将其添加到项目中
对于初学者,可以使用 CDN 引入
CDN 是一种通过互联网相互连接的电脑网络系统
利用最靠近每位用户的服务器,更快、更可靠地将音乐、图片、视频、应用程序及其他文件
发送给用户,来提供高性能、可扩展性及低成本的网络内容传递给用户
引入方式如下:
命令式编程关注的是 how to do
声明式编程关注的是 what to do,由框架完成 how 的过程
Vue 就属于声明式编程
Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统
{{message}}
下面来介绍一下模板中的内容
一个字符串模板,用作 component 实例的标记 ?
模板将会 替换 所挂载元素的 innerHTML
挂载元素的原有内容都将被忽略,除非模板中存在通过插槽分发的内容
分离写法和 Vue2 相同
传入的时候加 # 是因为,如果值以 # 开始,那么它将被用作 querySelector
并且使用 匹配元素 的 innerHTML 作为模板字符串
类型:Function
该函数返回组件实例的 data 对象
注:data 中返回的对象会被 Vue 的响应式系统劫持
之后对该对象的修改或者访问都会在劫持中被处理
类型:{ [ key:string ]:Function }
通常我们会在 methods 里面定义很多方法
这些方法可以被绑定到 template 模板中
我们可以直接使用 this 关键字访问 data 中返回的对象的属性
因为方法中的 this 自动绑定为组件实例
注意:methods 中不能使用箭头函数,理由是箭头函数绑定了父级作用域的上下文
所以 this 将不会按照期望指向组件实例
也称双大括号语法,数据绑定最常见的形式
Mustache 中不仅可以是 data 中的属性,也可以是一个 JavaScript 中的表达式
只渲染元素和组件一次
随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过
这可以用于优化更新性能
用法如下:
This will never change: {{msg}}
comment
{{msg}}
- {{i}}
更新元素的文本内容
{{message}}
更新元素的 innerHTML
{{message}}
页面效果如图:
跳过这个元素和它的子元素的编译过程
可以用来显示原始的 Mustache 标签
跳过大量没有指令的节点会加快编译
{{message}}
页面效果如图:
可以隐藏未编译的 Mustache 标签直到组件实例准备完毕
{{message}}
只有在编译结束之后 h2 里面的内容才会显示出来
动态地绑定一个或多个 attribute,或一个组件 prop 到表达式
缩写为 :
用法如下:
对象语法
我们可以传给 v-bind:class 一个对象,以动态地切换 class
所以上面 active 这个 class 是否存在 取决于 isActive 的值是否为 true
此外,v-bind:class 也可以与普通的 class 属性共存
如下:
data() {
return {
isActive: true,
hasError: false
}
}
渲染结果为:
其中 static 就是静态绑定的 class,后面两个 class 则是动态绑定的
注:我们可以看到 text-danger 外面加了引号,短横线拼接的字符串就需要加引号
数组语法
我们可以把一个数组传给 v-bind:class,以应用一个 class 列表
data() {
return {
activeClass: 'active',
errorClass: 'text-danger'
}
}
渲染结果为:
对象语法
直接上代码
也可以像下面这样写:
css 的属性名可以用驼峰式和短横线分割来命名,值得注意的是后者要加引号
data() {
return {
activeColor: 'red',
fontSize: 30
}
}
我们也可以把上面两个样式合并到一个对象中,使模板更加清晰
data() {
return {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
}
数组语法
v-bind:style 的数组语法可以将多个样式对象应用到同一个元素上:
用于监听 DOM 事件
缩写为 @
基本用法:
...
如果希望一个元素绑定多个事件,可以传入一个对象
修饰符:
v-on 的参数传递问题 ?
Vue is awesome!
Oh no
条件为 true 时才会渲染
因为 v-if 是一个指令,所以必须将它添加到一个元素上
但是如果想切换多个元素呢
Title
Paragraph 1
Paragraph 2
用于条件性展示元素的另一种方式
Hello!
首先,在用法上的区别
v-show 是不支持和 template 一起使用
v-show 不可以和 v-else 一起使用
其次,本质的区别是
v-show 元素无论是否需要显示到浏览器上,它的 DOM 实际都是有渲染的
只是通过 CSS 的 display 属性来进行切换
v-if 条件为 false 时,其对应的元素不会被渲染到 DOM 中
在实际开发中,应该如何进行选择呢?
电影列表
- {{index+1}}.{{movie}}
个人信息
- {{index+1}}.{{key}} : {{value}}
数字
- {{item}}
页面效果如图:
- {{key}}
- {{value}}
页面效果如图:
变更方法
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会 触发视图更新
这些被包裹过的方法包括:
替换数组
某些方法不会变更原始数组,而总是返回一个新数组
比如 filter() concat() slice()
?
高效的更新虚拟 DOM
感觉还是没完全搞懂 ?
Hello World
对应的 VNode 为:
const vnode = {
type: "div",
props: {
class: "title",
style: {
"font-size": "30px",
color: "red",
},
},
children: "Hello World",
}
现在有一个列表,里面存放着 A B C D E G
我们现在需要在中间插入一个 F
那么 Vue 内部对列表是如何进行更新的呢
有 key,使用 patchKeyedChildren 方法
没有 key,使用 patchUnkeyedChildren 方法
过程大致是,取新旧 VNode 中列表长度最短的值进行遍历
对于上图来说就是取旧 VNode 列表的长度
然后挨个比较,如果值相同就下一个,如果不同就用新值覆盖旧值,节点还是继续复用
最后新的多就增加节点,旧的多就删除节点
这个感觉还是要看源码会清晰一些
先从头部开始比较,如果两个节点的 key 不一样的话,就跳出循环
对于上图来说就是 a 和 a 比较,b 和 b 比较,c 和 f 比较之后跳出循环
然后从尾部开始比较,如果两个节点的 key 不一样的话,就跳出循环
新节点比较多的情况下,就用空节点与其比较,然后挂载新节点
旧节点比较多的情况下,移除旧节点
如果新旧节点一样多,但中间不一样,且是无序的
它就会尽量去利用旧的节点,去找跟新的节点 key 一样的,达到复用的一个效果
旧的里面有的,新的没有,就做删除操作
旧的里面没有的,新的里面有,就做新增操作
其余的就移动位置
在模板中可以通过插值语法显示 data 中的数据
但是在某些情况下,我们需要对数据进行一些转化后再显示
或者需要将多个数据结合起来进行显示
我们在模板中使用表达式,可以很方便的实现上面的需求
但是 在模板中放入太多的逻辑会让模板过重,难以维护
我们有两种方法可以将逻辑抽离出去
为了对比这两者的区别,我们来看三个案例
{{firstName + lastName}}
{{score >= 60 ? "及格":"不及格"}}
{{message.split("").reverse().join("")}}
data() {
return {
message: 'Hello World !',
firstName: 'coder',
lastName: 'why',
score: 89
}
}
页面效果如图:
可以很明显的看到缺点:
{{getFullName()}}
{{getResult()}}
{{getReverseMessage()}}
methods: {
getFullName() {
return this.firstName + " " + this.lastName;
},
getResult() {
return this.score >= 60 ? "及格":"不及格";
},
getReverseMessage() {
return this.message.split("").reverse().join("");
}
}
这种方法也是有缺点的:
{{fullName}}
{{result}}
{{reverseMessage}}
computed: {
fullName() {
return this.firstName + " " + this.lastName;
},
result() {
return this.score >= 60 ? "及格":"不及格";
},
reverseMessage() {
return this.message.split("").reverse().join("");
}
}
计算属性看起来像是一个函数,但是我们不需要在使用的时候加 ()
直观上看起来也很简洁,并且计算属性是有缓存的
在上面的案例实现方案中,我们会发现计算属性和 methods 的实现看起来差别不是很大
但区别还是很大的,主要表现在 methods 的每一次调用都需要重新去执行相应的函数
就好比计算 1 + 1 = 2
methods 每次都要重新算,而 computed 在执行一次运算之后就可以将结果缓存起来
下次再算的时候可以直接返回结果
计算属性会基于它们的 依赖关系 进行缓存
在数据不发生变化时,计算属性是不需要重新计算的
但是如果 依赖的数据发生变化,在使用时,计算属性依然会 重新进行计算
计算属性在大多数情况下,只需要一个 getter 方法即可
对于上述的 fullName 函数,我们有一个较为完整的写法
如下:
computed: {
// fullName 的 getter 方法
// fullName: function() {
// return this.firstName + " " + this.lastName;
// }
// 计算属性的完整写法
fullName: {
get: function() {
return this.firstName + " " + this.lastName;
},
set: function(newValue) {
// 修改计算属性的值时,就会触发 set 函数,执行一些自定义的操作
}
}
}
总而言之就是,对于计算属性我们有两种写法
但是一般情况,我们只会传入一个 getter,而不是一个包含 setter 和 getter 的对象
对于传入了什么,Vue 内部会有一个逻辑判断
在代码逻辑中监听数据的变化
{{question}}
页面效果如图:
点击之后,我们可以在控制台看到输出
表示已经监听到了变化
默认情况下,我们的侦听器只会侦听到数据本身的改变
就好比我现在的数据是一个对象,对象里面的某个值发生改变是侦听不到的
只有当整个对象被重新赋值时才能侦听到
{{info.name}}
上诉代码中,info 里面的 name 发生改变是侦听不到的
为了侦听到数据内部的改变,我们可以使用深度侦听
即设置 deep 的值为 true
这个时候我们就可以对内部数据进行监听
还有一种监听的方式就是使用 $watch 的 API ?
在表单控件或者组件上创建双向绑定
v-model 的内部其实是包含两个操作的
{{message}}
intro: {{intro}}
isAgree: {{isAgree}}
你的爱好:
hobbies: {{hobbies}}
你的性别:
gender: {{gender}}
喜欢的水果:
fruit: {{fruit}}
data() {
return {
intro: "Hello World",
isAgree: false,
hobbies: ["basketball"],
gender: "",
fruit: "orange"
}
}
?
比如说,我们在文本框中输入信息,不想实时同步
想在回车之后再同步数据,可以给 v-model 添加 lazy 修饰符
如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符
如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符
{{message}}
我是组件
我是组件内容
{{message}}
我是组件
我是组件内容