vue

一、What?

Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,完全能够为复杂的单页应用提供驱动。

二、Why?(优点)

  • 方便阅读的中文文档
  • 容易上手 (学习曲线比较缓和)
  • 体积小
  • 基于组件化的开发方式
  • 代码的可读性、可维护性得到了提高

三、vue的基础知识

1. MVVM

MVVM是Model-View-ViewModel的简写。

(1)组成

  • m(模型) 是vue实例中的data,自定义的数据或后端返回的数组,不是后端mvc里的model概念不同。
  • vm(视图模型) 是vue的实例 m和v之间的调度者 是mvvm的核心思想。在视图和数据绑定器之间进行通信。
  • v(视图)是 html 要渲染的。

(2)MVVM模式图

采用双向数据绑定

MVVM模式图

(3)MVVM优点

  • 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。

  • 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。

  • 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xaml代码。

  • 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。

2. MVC

(1)组成

  • Model(模型)表示应用程序核心(如数据库)。是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
  • View(视图)显示效果(HTML页面)。是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。
  • Controller(控制器)处理输入(业务逻辑)。是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。

(2)MVC模式图

MVC模式图

(3)MVC优点

  • 耦合性低

视图层和业务层分离,这样就允许更改视图层代码而不用重新编译模型和控制器代码,同样,一个应用的业务流程或者业务规则的改变只需要改动MVC的模型层即可。因为模型与控制器和视图相分离,所以很容易改变应用程序的数据层和业务规则。

模型是自包含的,并且与控制器和视图相分离,所以很容易改变应用程序的数据层和业务规则。如果把数据库从MySQL移植到Oracle,或者改变基于RDBMS数据源到LDAP,只需改变模型即可。一旦正确的实现了模型,不管数据来自数据库或是LDAP服务器,视图将会正确的显示它们。由于运用MVC的应用程序的三个部件是相互独立,改变其中一个不会影响其它两个,所以依据这种设计思想能构造良好的松耦合

  • 重用性高

随着技术的不断进步,需要用越来越多的方式来访问应用程序。MVC模式允许使用各种不同样式的视图来访问同一个服务器端的代码,因为多个视图能共享一个模型,它包括任何WEB(HTTP)浏览器或者无线浏览器(wap),比如,用户可以通过电脑也可通过手机来订购某样产品,虽然订购的方式不一样,但处理订购产品的方式是一样的。由于模型返回的数据没有进行格式化,所以同样的构件能被不同的界面使用。例如,很多数据可能用HTML来表示,但是也有可能用WAP来表示,而这些表示所需要的命令是改变视图层的实现方式,而控制层和模型层无需做任何改变。由于已经将数据和业务规则从表示层分开,所以可以最大化的重用代码了。模型也有状态管理和数据持久性处理的功能,例如,基于会话的购物车和电子商务过程也能被Flash网站或者无线联网的应用程序所重用。 [11]

  • 生命周期成本低

MVC使开发和维护用户接口的技术含量降低。

  • 部署快

使用MVC模式使开发时间得到相当大的缩减,它使程序员(Java开发人员)集中精力于业务逻辑,界面程序员(HTML和JSP开发人员)集中精力于表现形式上。

  • 可维护性高

分离视图层和业务逻辑层也使得WEB应用更易于维护和修改。

  • 有利软件工程化管理

由于不同的层各司其职,每一层不同的应用具有某些相同的特征,有利于通过工程化、工具化管理程序代码。控制器也提供了一个好处,就是可以使用控制器来联接不同的模型和视图去完成用户的需求,这样控制器可以为构造应用程序提供强有力的手段。给定一些可重用的模型和视图,控制器可以根据用户的需求选择模型进行处理,然后选择视图将处理结果显示给用户。

(4)MVC缺点

  • 没有明确的定义

完全理解MVC并不是很容易。使用MVC需要精心的计划,由于它的内部原理比较复杂,所以需要花费一些时间去思考。同时由于模型和视图要严格的分离,这样也给调试应用程序带来了一定的困难。每个构件在使用之前都需要经过彻底的测试。

  • 不适合小型,中等规模的应用程序

花费大量时间将MVC应用到规模并不是很大的应用程序通常会得不偿失。

  • 增加系统结构和实现的复杂性

对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。

  • 视图与控制器间的过于紧密的连接

视图与控制器是相互分离,但却是联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。

  • 视图对模型数据的低效率访问

依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。

  • 一般高级的界面工具或构造器不支持模式

改造这些工具以适应MVC需要和建立分离的部件的代价是很高的,会造成MVC使用的困难。

3. 常用指令

{{ }}:”双大括号里的内容将会被替代为数据对象上面的msg属性的值,如果绑定的这个属性的 值发生改变,那么大括号内的内容会跟着更新。
v-text: 和{{}}一样,会先执行覆盖元素中原本的内容,但是插值表达式只会覆盖自己的占位符,默认不会闪烁。
v-cloak: 解决{{}}插值闪烁问题

<div id="app" v-cloak>
    <span>{{ msg }}<span>
</div>
<div id="app">
    <span v-text="msg"><span>
</div>

v-once:也能执行一次性地插值,当数据改变时,插值处的内容不会更新。

<div id="app">
    <span v-once>这个将不会改变: {{ msg }}</span>
</div>

v-html: 渲染 html标签 覆盖元素中原有元素内容,双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,需要使用 v-html

<div id="app">
    <p>Using mustaches: {{ message }}</p>
    <p>Using v-html: <span v-html="message"></span></p>
</div>
new Vue({
    el: '#app',
    data: {
        message: '<h1 style="color:red">梅须逊雪三分白,雪却输梅一段香</h1>'
    }
})

Using {{ }}:

<h3 style="color:red">梅须逊雪三分白,雪却输梅一段香</h3>

Using v-html:

梅须逊雪三分白,雪却输梅一段香

### 【注意】 ### 
在站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。

v-bind: 简写为: 用来绑定数据 可以写合法的js表达式
v-model: 双向数据绑定

### css ###
.class1{
    background: #444;
    color: #eee;
}
### html ###
<div id="app">
    <input type="checkbox" v-model="use" id="r1"><label for="r1">修改背景颜色</label>
    <h3 v-bind:class="{'class1': use}">
        验证v-bind指令以及数据绑定
    </h3>
    <!-- <h3 :class="{'class1': use}">
        验证v-bind指令以及数据绑定
    </h3> -->
</div>
### js ###
new Vue({
    el: '#app',
    data:{
        use: false
    }
});

代码效果如下:

  • [ ] 修改背景颜色
    验证v-bind指令以及数据绑定

勾选之后效果如下:

  • [X] 修改背景颜色

验证v-bind指令以及数据绑定

v-on: 简写为 @ 用来点击事件

### html ###
<div id="app">
    <p>{{ message }}</p>
    <button v-on:click="reverseMessage">反转字符串</button>
    <!-- <button @:click="reverseMessage">反转字符串</button> -->
</div>
### js ###
new Vue({
    el: '#app',
    data: {
        message: 'Runoob!'
    },
    methods: {
        reverseMessage: function () {
            this.message = this.message.split('').reverse().join('')
        }
    }
})

v-if: 条件渲染可以根据条件来决定是否渲染dom,有较高的切换性能 , 适合元素可能永远不会被用户看到。
v-else: v-else元素必须跟在v-if 的后面

### html ###
<div id="app">
    <p v-if="ok">ok为true展示</p>
    <template v-else="ok">
        <p>ok为false展示</p>
    </template>
</div>
### js ###
new Vue({
    el: '#app',
    data: {
        ok: true
    }
})

v-show:这个命令可以根据条件选择是否展示元素,有较高的初始渲染消耗,适合元素频繁切换。
v-for:可以遍历, 普通数组,对象数组,对象,还可以是数字

### html ###
<div id="app">
    <div v-for='item in object' :key='item.id'>
        {{item.name}}---{{item.id}}
    </div>
</div>
### js ###
new Vue({
    el: '#app',
    data: {
    	object:[
            { id: 1, name: "aaa" },
            { id: 2, name: "bbb" },
            { id: 3, name: "ccc" },
            { id: 4, name: "ddd" }
        ]
    }
})

表达式

### html ###
<div id="app">
    {{5+5}}<br>
    {{ ok ? 'YES' : 'NO' }}<br>
    {{ message.split('').reverse().join('') }}
    <div v-text="'list-' + message"></div>
</div>
### js ###
new Vue({
    el: '#app',
    data: {
        ok: true,
        message: 'RUNOOB'
    }
})

10
YES
BOONUR

4. 常用事件修饰符

  • .stop 阻止冒泡 :外层和里层都有方法 点击里层会产生冒泡,也会触发外层的事件。
    顺序 从里到外产生事件
  • .prevent 阻止浏览器默认行为,
    a标签有浏览器默认行为。
  • .capture 捕获事件 :点击里层先触发外层再触发里层 顺序从外到里产生事件
  • .self 只触发自己本身的事件 不会产生冒泡和捕获事件 类似于阻止冒泡 但只针对自己那一层 最外层还是会被最里层冒泡冒到 stop 是阻止所有层次
  • .once 事件只执行一次
  • .passive 滚动事件的默认行为将会立即触发
  • .left 左键事件
  • .right 右键事件
  • .middle 中间滚轮事件
  • .enter .esc .space ……(按键修饰符)
<!-- 阻止单击事件冒泡 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联  -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件侦听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div v-on:click.self="doThat">...</div>

<!-- click 事件只能点击一次,2.1.4版本新增 -->
<a v-on:click.once="doThis"></a>

<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">

四、组件化

组件:组件就是可被反复使用的,带有特定功能的视图

所谓的组件化,就像玩积木一样,把封装的组件进行复用,把积木(组件)拼接在一起,构成一个复杂的页面应用程序。

组件树就是由各个组件构成的一种数据结构,它存在的意义是为了帮梳理应用程序

1. 组件的创建

全局组件:

Vue.component('my-component-name',{
    // ... 选项 ...
})

全局注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中

Vue.component('my-component-a',{// ... 选项 ...})
Vue.component('my-component-b',{// ... 选项 ...})
Vue.component('my-component-c',{// ... 选项 ...})
new vue({
    el:'#app'
})
<div id="app">
    <my-component-a></my-component-a>
    <my-component-b></my-component-b>
    <my-component-c></my-component-c>
</div>

局部组件:

new Vue({
    components: {
        'component-a': {},
        'component-b': {}
    }
})

2.注意事项

  • 组件的id和使用方式 遵循烤串式命名方式:a-b-c
  • 如果一个组件 要渲染多个元素,将多个元素放在一个顶层标签中,比如div、form
  • 全局组件可以用在id为example的范围内的任何一个组件内部,直接调用可以;但是局部组件只能在父模板中直接调用

五、自定义指令

1. 创建指令

全局指令:

// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
    // 当被绑定的元素插入到 DOM 中时……
    inserted: function (el) {
        // 聚焦元素
        el.focus()  
    }
})

局部指令

directives: {
    focus: {
        // 指令的定义
        inserted: function (el) {
            el.focus()
        }
    }
}

定义完之后可以使用新的 v-focus 属性

<input v-focus>

2. 钩子函数

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是可以通过比较更新前后的值来忽略不必要的模板更新。

  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。

  • unbind:只调用一次,指令与元素解绑时调用。

3. 钩子函数参数

(1)el: 指令所绑定的元素,可以用来直接操作 DOM。
(2)binding: 一个对象,包含以下 property:

  • name:指令名,不包括 v- 前缀。
  • value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
  • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
  • expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
  • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
  • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。

(3)vnode: Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
(4)oldVnode: 上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

###【注意】### 
除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。

六、过滤器

全局指令:

Vue.filter('capitalize', function (value) {
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
})

new Vue({
    // ...
})

局部指令

filters: {
    capitalize: function (value) {
        if (!value) return ''
        value = value.toString()
        return value.charAt(0).toUpperCase() + value.slice(1)
    }
}
<!-- 在双花括号中 -->
{{ message | capitalize }}

<!-- 在 v-bind 中 -->
<div v-bind:id="rawId | capitalize"></div>

过滤器可以串联:

{{ message | filterA | filterB }}

也可以接收参数:

{{ message | filterA('arg1', arg2) }}

七、生命周期

  • beforeCreate():这是我们遇到的第一个生命周期函数,表示实例完全被创建出来之前,会执行它…

  • created(): 这是遇到的第二个生命周期函数…

  • beforeMount():这是遇到的第3个生命周期函数,表示 模板已经在内存中编辑完成,但是尚未把模板渲染(挂载)到页面中。在 beforeMount 执行的时候,页面中的元素,还没有被真正替换过来,只是之前写的一些模板字符串。就像{{text}}这样

  • mounted():这是遇到的第四个生命周期函数,表示内存中的模板,已经真实的挂载到了页面中,用户已经可以看到渲染好的页面了。只要执行完这个生命周期,就表示整个vue实例已经初始化完毕了,此时,组件已经脱离了创建阶段,进入到了运行阶段。

  • beforeUpdate():这时候表示,我们的界面还没有被更新[但数据已经被更新了页面中显示的数据,还是旧的,此时data数据是最新的。页面尚未和最新的数据保持同步

  • update() : 这一步执行的是 先根据data中最新的数据,在内存中重新渲染出一份最新的内存dom树,当最新的内存dom树被更新后,会把最新的内存DOM树重新渲染到真实的页面中去,这时候,就完成了数据data(model层)->view(视图层)的更新,
    页面和data数据已经保持同步了,都是最新的。

  • beforeDestory :当执行 beforeDestory 钩子函数的时候,Vue实例就已经从运行阶段,进入到销毁阶段, 当执行beforeDestroy的时候,实例身上所有的data和所有的methods以及过滤器、指令...都处于可用状态,此时,还没有真正执行销毁的过程。

  • destroyed :当执行这个函数的时候,组件已经被完全销毁了,此时,组件中所有的数据,方法,指令,过滤器...都已经不可用了

八、常用属性

1. computed 计算属性

<div id="app">
    <p>Original message: "{{ message }}"</p>
    <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
    el: '#app',
    data: {
        message: 'Hello'
    },
    computed: {
        // 计算属性的 getter
        reversedMessage: function () {
            // 'this' 指向 vm 实例
            return this.message.split('').reverse().join('')
        }
    }
})

结果:

Original message: "Hello"
Computed reversed message: "olleH"

console.log(vm.reversedMessage) // => 'olleH'
vm.message = 'Goodbye'
console.log(vm.reversedMessage) // => 'eybdooG'

vm.reversedMessage 依赖于 vm.message,因此当 vm.message 发生改变时,所有依赖 vm.reversedMessage 的绑定也会更新。

2. computed vs methods (方法)

可以通过在表达式中调用方法来达到同样的效果:

<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在组件中
new Vue({
    el: '#app',
    data: {
        message: 'Hello'
    },
    methods: {
        reversedMessage: function () {
            return this.message.split('').reverse().join('')
        }
    }
})

可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。

假设我们有一个性能开销比较大的计算属性A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。

3. computed vs watch (侦听属性)

<div id="demo">{{ fullName }}</div>
var vm = new Vue({
    el: '#demo',
    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
        }
    }
})

上面代码是命令式且重复的。将它与计算属性的版本进行比较:

var vm = new Vue({
    el: '#demo',
    data: {
        firstName: 'Foo',
        lastName: 'Bar'
    },
    computed: {
        fullName: function () {
            return this.firstName + ' ' + this.lastName
        }
    }
})

4. 计算属性中的setter

计算属性默认只有 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]
        }
    }
}

现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstName 和 vm.lastName 也会相应地被更新。

九、路由模块

1. SPA的基本概念和工作原理

SPA: single page application 单一页面应用程序,只有一个完整的页面;
它在加载页面时,不会加载整个页面,而是只更新某个指定的容器中内容。
比如Gmail、移动的webApp

工作原理:
(1)解析地址栏:完整的页面地址、路由地址
(2)根据路由地址: 从路由词典中找到真正的要加载的页面
(3)发起ajax请求:请求要加载的页面
(4)像指定的容器中插入加载来的页面

2. 路由配置

new VueRouter({
//路由匹配规则
    routes:[
       {
            path:'/',
            redirect:'/login'
        },
        {
            path:'login',
            component:login
        },
        {
            path:'/register',
            component:register
        }
    ]  
})

后续陆续补充知识点……

【MARK】 文档以整理记录为主,如有错误,欢迎指出哟,大家一起来学习...

posted @ 2021-03-23 14:13  Red-Plum  阅读(21)  评论(0编辑  收藏  举报