Vue 初级
大纲知识点:
- Vue指令
- Vue 基本属性
- Vue组件通信
##SPA和MPA区别及优缺点
SPA(单页面应用)
SPA( Single-page Application )只有一个html页面,所有跳转方式都是通过组件切换完成的。
优点:
1.用户体验好,流畅。
2.因为是单页面,所以对服务器的压力较小。
3.可以在页面切换的时候增加一些炫酷的动画效果。
4.代码的可复用,且由于是组件化开发,有利于后期的维护。
缺点:
1.页面复杂度变大,开发难度较大。
2.不利于SEO
3.首次加载的时候用时较长。
MPA(多页面应用)
MPA(Multi-page Application)就是指一个应用中有多个页面,页面跳转时是整页刷新,拥右多个html页面,常见于PC端网站。
优点:
1.首屏加载较快,只需要加载本页面的HTML、CSS、JS
2.有利于优化SEO。
3.页面复杂度不高,开发成本较低。
缺点:
1.网站的后期维护难度较大。
2.页面之间的跳转受网络以及设备等影响,耗时较长,出现空白等待的页面,用户体验不高
3.代码重复度大。
4.对服务器的压力较大
MVC、MVVM、MVP的理解
MVC
理想的MVC
MVC的理想模型如下图所示:
各层的职责如下所示:
Models: 数据层,负责数据的请求和存储以及处理
View: 展示层,负责View和动画效果的展示,以及用户的交互
Controller: 控制器层,负则连接View和Model,对view的交互事件有controller层处理传递给model,model处理再交互传递给Controller,Controller传递给view
M和View应该是完全隔离的,由C作为中间人来负责二者的交互,同时三者是完全独立分开的,这样可以保证M和V的可测试性和复用性,但是一般由于C都是为特别的应用场景下的M和V做中介者,所以很难复用。
MVC职责
Controller(VC):
- 生成View,组装View
- 响应View的事件,或者作为View的代理
- 调用Model的数据获取接口,拿回返回的数据处理加工渲染到View的展示
- 处理View的生命周期
- 处理View的跳转
VC职责简化
- 生成的View添加到self.view上面,逻辑不在ViewController里
- 管理View的声明周期
- 每个子view设置代理,代理又Controller实现。
Model :
- 业务逻辑封装
- 提供数据接口给Controller使用
- 数据持久化存储与读取
- 作为数据模型存储数据
Model层不仅仅是实体类,他还包含了业务逻辑处理以及数据存储等其他操作。
View层:
- 界面元素搭建,动画展示
- 接受用户操作并提供反馈
MVC优缺点
优点
- 增强代码复用性耦合性低: V对外只暴露Set方法,对M甚至C都是个例状态,复用没问题
- 解决代码臃肿: 布局转移到了View层,业务逻辑转移到了Model层
- 易拓展:无论产品想增加或者模块,只需增加响应的MVC模块
- 可维护:各个模块相互隔离,哪里出错改哪里,不影响其他模块。
缺点
- 简单的小型项目,使用MVC设计反而会降低开发效率层和层虽然相互分离,但是之间关联性太强,没有做到独立的重用。
- 对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率
- 视图与控制器是相互分离,但却是联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。
- 依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。
MVCP
MVC的缺点在于并没有隔离View和Model层, MVP针对以上缺点做了优化, 它将业务逻辑和业务展示也做了一层隔离, 对应的就变成了MVCP.
M和V功能不变, 原来的C现在只负责布局, 而所有的业务逻辑全都转移到了P层。P层处理完了业务逻辑,如果要更改view的显示,那么可以通过回调来实现,这样可以减轻耦合,同时可以单独测试P层的业务逻辑,
MVP从视图层中分离了行为(事件响应)和状态(属性,用于数据展示),它创建了一个视图的抽象,也就是presenter层,而视图就是P层的『渲染』结果。P层中包含所有的视图渲染需要的动态信息,包括视图的内容(text、color)、组件是否启用(enable),除此之外还会将一些方法暴露给视图用于某些事件的响应。
MVP结构
Presenter处理View的业务逻辑,与View耦合,Persenter也给视图暴露接口与其通信,他将原来需要在Controller层里面处理的逻辑移到了Persenter层
MVP的职责
VC层:
- 生成的View添加到self.view上面
- 管理View的声明周期
- 通过P层去获取数据然后渲染到View上面展示。
Controller层:
- 生成view,实现view的代理和数据源
- 绑定view和presenter
- 调用presenter执行业务逻辑
- 负责界面跳转
View层:
- 监听P层数据更新通知,刷新页面展示
- 点击事件触发时调用P层响应的方法
- 界面元素搭建,动画展示
- 接受用户操作并提供反馈
Model层:
- 与MVC的model层一样
Persneter层:
- 实现view的事件处理逻辑,暴露相应的接口给view的事件调用
- 调用model的接口获取数据,然后加工数据,封装成view可以直接用来显示的数据和状态
这里又有两种不同的实现方式:
让P持有V,P通过V的暴露接口改变V的显示数据和状态,P通过V的事件回调来执行自身的业务逻辑
让V持有P,V通过P的代理回调来改变自身的显示数据和状态,V直接调用P的接口来执行事件响应对应的业务逻辑
第一种方式保持了view的纯粹,只是作为被动view来展示数据和更改状态,但是却导致了P耦合了V,这样业务逻辑和业务展示有糅合到了一起,和上面的MVC一样了。
第二种方式保证了P的纯粹,让P只做业务逻辑,至于业务逻辑引发的数据显示的变化,让view实现对应的代理事件来实现即可。这增加了view的复杂和view对于P的耦合。
MVP相对于MVC, 它其实只做了一件事情, 即分割业务展示和业务逻辑. 展示和逻辑分开后, 只要我们能保证V在收到P的数据更新通知后能正常刷新页面, 那么整个业务就没有问题. 因为V收到的通知其实都是来自于P层的数据获取/更新操作, 所以我们只要保证P层的这些操作都是正常的就可以了. 即我们只用测试P层的逻辑, 不必关心V层的情况
MVVM
MVVM其实是在MVP的基础上发展起来的。那么MVVM在MVP的基础上改良了啥呢?答案就是数据绑定。从 Model-View-ViewModel 这个名字来看,它由三个部分组成,也就是 Model、View 和 ViewModel;其中视图模型(ViewModel)其实就是 MVP 模式中的P,在 MVVM 中叫做VM。
MVVM架构图
除了我们非常熟悉的 Model、View 和 ViewModel 这三个部分,在 MVVM 的实现中,还引入了隐式的一个 Binder层,这也是MVVM相对MVP的进步,而声明式的数据和命令的绑定在 MVVM 模式中就是通过binder层来完成的,RAC是iOS下binder的优雅实现,当然MVVM没有RAC也完全可以运行。
下图展示了iOS下的MVC是如何拆分成MVVM的:
image.png
MVVM和MVP相对于MVC最大的改进在于:P或者VM创建了一个视图的抽象,将视图中的状态和行为抽离出来形成一个新的抽象。这可以把业务逻辑(P/VM)和业务展示(V)分离开单独测试,并且达到复用的目的,逻辑结构更加清晰
MVVM层职责
MVVM隔层职责和MVP类似,VM对应P层
MVVM相对于MVP的改进
MVVM相对于MVC的改进是对VM/P和view做了双向的数据和命令绑定
MVP的view触发P的业务逻辑,然后P再回调改变View的显示的操作,使用MVVM的数据绑定来实现让逻辑更加清晰,代码也更少。这就是MVVM相对于MVP的改进之处
缺点
View和Viewmodel因为双向绑定所以没有复用性。
Vue的核心
数据驱动、组件系统
**数据驱动:**ViewModel,保证数据和视图的一致性。
**组件系统:**应用类UI可以看作全部是由组件树构成的。
data 属性
在.vue扩展名文件下,data属性必须定义为函数形式,在普通的Vue对象中,data属性就是一个对象。
相当于react中state,代表了当前组件的状态;组件呈现一个什么样的状态,data属性就必须有什么样的描述。
el属性
Vue 对象/组件将要挂载页面元素,可以使用选择器
-
类型:
string | Element
选择器|HTML元素 -
限制:只在用
new
创建实例时生效。 -
详细:
提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以是 CSS 选择器,也可以是一个 HTMLElement 实例。
new Vue({
el:"#app"
})
components属性
- 类型:
Object
- 详细:
包含 Vue 实例可用组件的哈希表。
methods属性
事件处理函数、组件业务逻辑函数等等都必须定义在methods属性中,注意定义在method属性中的函数,不能使用箭头函数
-
类型:
{ [key: string]: Function }
对象 -
详细:
methods 将被混入到 Vue 实例中。可以直接通过 VM 实例访问这些方法,或者在指令表达式中使用。方法中的
this
自动绑定为 Vue 实例。注意,不应该使用箭头函数来定义 method 函数 (例如
plus: () => this.a++
)。理由是箭头函数绑定了父级作用域的上下文,所以this
将不会按照期望指向 Vue 实例,this.a
将是 undefined。
computed计算属性
- 类型:
{ [key: string]: Function | { get: Function, set: Function } }
{
key:function
key:{
}
}
计算属性对应的是定义在data属性中数据,当参与计算的data中的数据发生变化,相应的计算属性对应函数会被调用,重新计算并且返回结果。计算属性会监听data中参与计算的数据。
计算属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。注意,如果某个依赖 (比如非响应式属性) 在该实例范畴之外,则计算属性是不会被更新的。
watch 监听属性
监听定义在data属性中的数据,监听的数据发生变化,就会触发相应的处理函数
类型:{ [key: string]: string | Function | Object | Array }
var vm = new Vue({
data: {
a: 1,
b: 2,
c: 3,
d: 4,
e: {
f: {
g: 5
}
}
},
watch: {
a: function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
},
// 方法名
b: 'someMethod',
// 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
c: {
handler: function (val, oldVal) { /* ... */ },
deep: true
},
// 该回调将会在侦听开始之后被立即调用
d: {
handler: 'someMethod',
immediate: true
},
// 你可以传入回调数组,它们会被逐一调用
e: [
'handle1',
function handle2 (val, oldVal) { /* ... */ },
{
handler: function handle3 (val, oldVal) { /* ... */ },
/* ... */
}
],
// watch vm.e.f's value: {g: 5}
'e.f': function (val, oldVal) { /* ... */ }
}
})
vm.a = 2 // => new: 2, old: 1
监听属性和计算属性的区别
https://www.cnblogs.com/jiajialove/p/11327945.html
Vue 指令
v-show
根据表达式之真假值,切换元素的 display
CSS 属性。
当条件变化时该指令触发过渡效果。
v-show绑定的boolean值,如果绑定值是true,则当前元素显示,如果绑定的值是false,则当前元素隐藏
v-bind
动态地绑定一个或多个属性,或一个组件 prop 到表达式。
v-if v-else v-else-if
有条件的渲染元素或者组件,只有v-if可以单独使用,v-else或者v-elsep-if必须与v-if联用
v-if:如果指令绑定的表达式的取值为true,则指令所在的元素被渲染,否则则不渲染
v-else:如果上一兄弟元素的v-if取值为false,则v-else所在元素被渲染,反之,上一兄弟元素的v-if绑定的值为true,则不渲染
v-else-if:如果上一兄弟元素绑定的表达式的值没有匹配到,则判断v-else-if的取值,如果是true,则指令所在元素被渲染,否则不渲染
v-text
更新元素的 textContent
。如果要更新部分的 textContent
,需要使用 {{ Mustache }}
插值。
<span v-text="msg"></span>
<!-- 和下面的一样 -->
<span>{{msg}}</span>
v-html
等同于innerHTML,更新元素的 innerHTML
v-for
基于源数据多次渲染元素或模板块。此指令之值,必须使用特定语法 alias in expression
,为当前遍历的元素提供别名:
<div class="list_item" v-for="(number,index) in items" :key="index">{{number}}</div>
v-on
绑定事件监听器。事件类型由参数指定。表达式可以是一个方法的名字或一个内联语句,如果没有修饰符也可以省略。
用在普通元素上时,只能监听原生 DOM 事件用在自定义元素组件上时,也可以监听子组件触发的自定义事件。
事件修饰符
.stop
- 调用event.stopPropagation()
阻止事件冒泡。.prevent
- 调用event.preventDefault()
。阻止元素的默认行为。.capture
- 添加事件侦听器时使用 capture 模式。.self
- 只当事件是从侦听器绑定的元素本身触发时才触发回调。.{keyCode | keyAlias}
- 只当事件是从特定键触发时才触发回调。.native
- 监听组件根元素的原生事件。.once
- 只触发一次回调。.left
- (2.2.0) 只当点击鼠标左键时触发。.right
- (2.2.0) 只当点击鼠标右键时触发。.middle
- (2.2.0) 只当点击鼠标中键时触发。.passive
- (2.3.0) 以{ passive: true }
模式添加侦听器
<!-- 方法处理器 -->
<button v-on:click="doThis"></button>
<!-- 动态事件 (2.6.0+) -->
<button v-on:[event]="doThis"></button>
<!-- 内联语句 -->
<button v-on:click="doThat('hello', $event)"></button>
<!-- 缩写 -->
<button @click="doThis"></button>
<!-- 动态事件缩写 (2.6.0+) -->
<button @[event]="doThis"></button>
<!-- 停止冒泡 -->
<button @click.stop="doThis"></button>
<!-- 阻止默认行为 -->
<button @click.prevent="doThis"></button>
<!-- 阻止默认行为,没有表达式 -->
<form @submit.prevent></form>
<!-- 串联修饰符 -->
<button @click.stop.prevent="doThis"></button>
<!-- 键修饰符,键别名 -->
<input @keyup.enter="onEnter">
<!-- 键修饰符,键代码 -->
<input @keyup.13="onEnter">
<!-- 点击回调只会触发一次 -->
<button v-on:click.once="doThis"></button>
<!-- 对象语法 (2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
v-model 双向绑定
只能输入元素上使用
修饰符:
Vue 组件通信
props属性
-
类型:
Array | Object
-
详细:
props 可以是数组或对象,用于接收来自父组件的数据。props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。
父传子
1 、在父组件中的子组件标签中定义传递数据属性,需要使用动态绑定来传递数据
2 、在子组件中,增加props属性,在props属性中定义一个与传递数据时定义名称相同属性用来接受数据,然后表明数据数据类型
3 、使用数据
//父组件
<NavigationBar title="我的"/>
//子组件
<template>
<div class="navigation_bar_container">
<div class="navigation_bar_wrapper">
<div class="navigation_bar_back_icon">
<img src="../assets/fanhui.png"
v-on:click="onClicBack"/>
</div>
<div>{{title}}</div>
<div></div>
</div>
</div>
</template>
<script>
export default {
//props属性
props:{
title:String//如果传递的数据不符合定义时的数据类型,vue将抛出警告
}
子传父
利用Vue提供的触发自定义事件$emit函数实现
1 、子组件需要向父组件传递数据时,使用$emit函数出发自定义事件,并传递数据
2 、在父组件中,增加对自定义事件的监听,子组件触发自定义事件后,父组件监听并执行事件对应的处理函数,然后获取子组件传递给父组件的数据
//子组件
methods:{
onClicBack(){
// 函数
//参数1:自定义事件名称
//参数2:事件触发需要传递的数据
this.$emit("goback",111111)
}
}
//父组件
//监听自定义事件goback,触发后执行函数back
<NavigationBar title="我的"
v-on:goback="back"/>
跨组件传值(兄弟组件传值)
在Vue中,兄弟组件传值我们使用的是eventBus方式
首先,创建一个名称为EventBus的Vue对象(组件):
import Vue from "vue";
//中转站
//创建了一个Vue对象,使用这个vue对象作为中转站
const EventBus = new Vue({
})
export default EventBus;
然后,在将要传递数据的组件中引入,当我们想要传递数据的时候,使用EventBus对象触发一个自定义事件,将需要传递的数据以事件触发方式传递:
<template>
<div class="input_container">
<div class="input">
<input type="text" placeholder="请输入将要提交的内容"
v-model="inputValue"/>
</div>
<div class="button">
<button v-on:click="onClcikSumit">提交</button>
</div>
</div>
</template>
<script>
import EventBus from "../EventBus/EventBus";
export default {
data(){
return{
inputValue:""
}
},
methods:{
onClcikSumit(){
// var inputValue = this.$refs.input.value;
// window.console.log(this.inputValue);
//$emit触发一个自定义事件
EventBus.$emit("submitMessage",this.inputValue);
}
}
}
</script>
最后,在接受数据的组件中,同样引入EventBus组件,在接受数据组件挂载结束的生命周期函数中,监听触发的自定义事件:
<template>
<div class="content_container">
{{content}}
</div>
</template>
<script>
import EventBus from "../EventBus/EventBus";
export default {
data(){
return{
content:""
}
},
mounted(){
EventBus.$on("submitMessage",(message)=>{
this.content = message
window.console.log(message)
});
}
}
</script>
当事件触发后,就会监听到事件,然后执行事件对应的函数,获取到传递的数据。
Vue文本过滤
Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind
表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示
<!-- 在双花括号中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
局部过滤器
在一个组件的选项中定义本地的过滤器
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
全局过滤器
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
new Vue({
// ...
})
当全局过滤器和局部过滤器重名时,会采用局部过滤器。
串联过滤器
{{ message | filterA | filterB }}
{{ message | filterA('arg1', arg2) }}
Vue 自定义指令
Vue.directive('my-directive', {
bind: function () {},
inserted: function () {},
update: function () {},
componentUpdated: function () {},
unbind: function () {}
})
// 注册 (指令函数)
Vue.directive('my-directive', function () {
// 这里将会被 `bind` 和 `update` 调用
})
// getter,返回已注册的指令
var myDirective = Vue.directive('my-directive')
全局指令
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
局部指令
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
hash模式和history模式的区别
https://blog.csdn.net/lyn1772671980/article/details/80804419
methods、computed、watch区别及各自使用场景
computer
当页面中有某些数据依赖其他数据进行变动的时候,可以使用计算属性。
<p id="app"> {{fullName}} </p>
<script>
var vm = new Vue({
el: '#app',
data: {
firstName: 'Foo',
lastName: 'Bar',
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
</script>
需要注意的是,就算在data中没有直接声明出要计算的变量,也可以直接在computed中写入。
计算属性默认只有getter,可以在需要的时候自己设定setter:
// ...
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
适用场景:
watch
watch和computed很相似,watch用于观察和监听页面上的vue实例,当然在大部分情况下我们都会使用computed,但如果要在数据变化的同时进行异步操作或者是比较大的开销,那么watch为最佳选择。watch为一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。直接引用文档例子
var vm = new Vue({
el: '#app',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
如果在data中没有相应的属性的话,是不能watch的,这点和computed不一样。
适用场景:
methods
方法,跟前面的都不一样,我们通常在这里面写入方法,只要调用就会重新执行一次,相应的有一些触发条件,在某些时候methods和computed看不出来具体的差别,但是一旦在运算量比较复杂的页面中,就会体现出不一样。
需要注意的是,computed是具有缓存的,这就意味着只要计算属性的依赖没有进行相应的数据更新,那么computed会直接从缓存中获取值,多次访问都会返回之前的计算结果。
computed
- 会把输出结果缓存起来,只有当其内部的依赖数据(data 中的数据)改变时才会改变。适用于比较费时的数据计算
methods
- 只要实例被更新,都会被触发执行,适用于需要实时性的数据
watch
- 侦听 data 中数据的变化,在方法中会传入 newVal 和 oldVal。可用于 输入值无效、提供中间值 等场景。不过这个属性还是有一定深度的,可以参看官方的文档。
diff算法
https://www.cnblogs.com/wind-lanyan/p/9061684.html
重用机制key
https://www.jianshu.com/p/0044532e4a93