Vue初级

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的理想模型如下图所示:

img

各层的职责如下所示:

Models: 数据层,负责数据的请求和存储以及处理
View: 展示层,负责View和动画效果的展示,以及用户的交互
Controller: 控制器层,负则连接View和Model,对view的交互事件有controller层处理传递给model,model处理再交互传递给Controller,Controller传递给view

M和View应该是完全隔离的,由C作为中间人来负责二者的交互,同时三者是完全独立分开的,这样可以保证M和V的可测试性和复用性,但是一般由于C都是为特别的应用场景下的M和V做中介者,所以很难复用。

MVC职责

Controller(VC):

  1. 生成View,组装View
  2. 响应View的事件,或者作为View的代理
  3. 调用Model的数据获取接口,拿回返回的数据处理加工渲染到View的展示
  4. 处理View的生命周期
  5. 处理View的跳转

VC职责简化

  1. 生成的View添加到self.view上面,逻辑不在ViewController里
  2. 管理View的声明周期
  3. 每个子view设置代理,代理又Controller实现。

Model :

  1. 业务逻辑封装
  2. 提供数据接口给Controller使用
  3. 数据持久化存储与读取
  4. 作为数据模型存储数据

Model层不仅仅是实体类,他还包含了业务逻辑处理以及数据存储等其他操作。

View层:

  1. 界面元素搭建,动画展示
  2. 接受用户操作并提供反馈
MVC优缺点

优点

  1. 增强代码复用性耦合性低: V对外只暴露Set方法,对M甚至C都是个例状态,复用没问题
  2. 解决代码臃肿: 布局转移到了View层,业务逻辑转移到了Model层
  3. 易拓展:无论产品想增加或者模块,只需增加响应的MVC模块
  4. 可维护:各个模块相互隔离,哪里出错改哪里,不影响其他模块。

缺点

  1. 简单的小型项目,使用MVC设计反而会降低开发效率层和层虽然相互分离,但是之间关联性太强,没有做到独立的重用。
  2. 对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率
  3. 视图与控制器是相互分离,但却是联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。
  4. 依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。
MVCP

MVC的缺点在于并没有隔离View和Model层, MVP针对以上缺点做了优化, 它将业务逻辑和业务展示也做了一层隔离, 对应的就变成了MVCP.
M和V功能不变, 原来的C现在只负责布局, 而所有的业务逻辑全都转移到了P层。P层处理完了业务逻辑,如果要更改view的显示,那么可以通过回调来实现,这样可以减轻耦合,同时可以单独测试P层的业务逻辑,
MVP从视图层中分离了行为(事件响应)和状态(属性,用于数据展示),它创建了一个视图的抽象,也就是presenter层,而视图就是P层的『渲染』结果。P层中包含所有的视图渲染需要的动态信息,包括视图的内容(text、color)、组件是否启用(enable),除此之外还会将一些方法暴露给视图用于某些事件的响应。

MVP结构

img

Presenter处理View的业务逻辑,与View耦合,Persenter也给视图暴露接口与其通信,他将原来需要在Controller层里面处理的逻辑移到了Persenter层

MVP的职责

VC层:

  1. 生成的View添加到self.view上面
  2. 管理View的声明周期
  3. 通过P层去获取数据然后渲染到View上面展示。

Controller层:

  1. 生成view,实现view的代理和数据源
  2. 绑定view和presenter
  3. 调用presenter执行业务逻辑
  4. 负责界面跳转

View层:

  1. 监听P层数据更新通知,刷新页面展示
  2. 点击事件触发时调用P层响应的方法
  3. 界面元素搭建,动画展示
  4. 接受用户操作并提供反馈

Model层:

  1. 与MVC的model层一样

Persneter层:

  1. 实现view的事件处理逻辑,暴露相应的接口给view的事件调用
  2. 调用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。

img

MVVM架构图

除了我们非常熟悉的 Model、View 和 ViewModel 这三个部分,在 MVVM 的实现中,还引入了隐式的一个 Binder层,这也是MVVM相对MVP的进步,而声明式的数据和命令的绑定在 MVVM 模式中就是通过binder层来完成的,RAC是iOS下binder的优雅实现,当然MVVM没有RAC也完全可以运行。
下图展示了iOS下的MVC是如何拆分成MVVM的:

img

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 双向绑定

只能输入元素上使用

修饰符:
  • .lazy - 取代 input 监听 change 事件
  • .number - 输入字符串转为有效的数字
  • .trim - 输入首尾空格过滤

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]
            }
        }
    }

适用场景:

img

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不一样。

适用场景:

img

methods

方法,跟前面的都不一样,我们通常在这里面写入方法,只要调用就会重新执行一次,相应的有一些触发条件,在某些时候methods和computed看不出来具体的差别,但是一旦在运算量比较复杂的页面中,就会体现出不一样。

需要注意的是,computed是具有缓存的,这就意味着只要计算属性的依赖没有进行相应的数据更新,那么computed会直接从缓存中获取值,多次访问都会返回之前的计算结果。

computed - 会把输出结果缓存起来,只有当其内部的依赖数据(data 中的数据)改变时才会改变。适用于比较费时的数据计算

methods - 只要实例被更新,都会被触发执行,适用于需要实时性的数据

watch - 侦听 data 中数据的变化,在方法中会传入 newValoldVal。可用于 输入值无效提供中间值 等场景。不过这个属性还是有一定深度的,可以参看官方的文档

diff算法

https://www.cnblogs.com/wind-lanyan/p/9061684.html

重用机制key

https://www.jianshu.com/p/0044532e4a93

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值