vue填坑(一)

MVVM模式

MVVM、MVC、MVP都是架构模式,这里是这三者的图示。vue中MVVM架构如下: 
这里写图片描述


Vue基本语法

Vue实例和Vue组件

Vue实例由Vue函数创建。

var vm = new Vue({
//
})
  • 1
  • 2
  • 3

Vue组件也是Vue实例,组件可以扩展html元素,封装可重用代码。

// 注册
Vue.component('my-component', {
  template: '<div>A custom component!</div>'
})

// 创建根实例
new Vue({
  el: '#example'
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

渲染为

<div id="example">
  <div>A custom component!</div>
</div>
  • 1
  • 2
  • 3

父子组件通信

参考笔记

父组件给子组件传递字符串
// html
<div id="m-dialog">
<!-- 
<child value="str"></child>
该种方法“只能传递字符串”,
将child的data中的value = "str";不需要父组件data中有数据
 -->
    <child value="str"></child>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
// js
// 注册子组件
Vue.component("child", {
    // 获取value的依赖
    props:["value"],
    template: '<span>{{ value }}</span>'
});

// 父实例
new Vue({
    // 这里的el可视为父组件
    el:"#m-dialog"
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

渲染结果 
这里写图片描述

父组件给子组件传递数据(动态prop)

使用v-bind 绑定子组件的data,如果父组件的flag发生改变,会传递给子组件。

// html
<div id="m-dialog">
    <child v-bind:state = 'flag'></child>
</div>
  • 1
  • 2
  • 3
  • 4
// js
Vue.component("child", {
    props:["state"],
    template: '<span>{{ state }}</span>'
});
new Vue({
    el:"#m-dialog",
    data: {
        flag: true
    }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

渲染结果

true
  • 1
子组件数据emit给父组件
// html
<div id="m-dialog">
<!-- v-on:cancle="other"表示如果触发组件中的cancle事件则连带触发父组件中的other时间-->
    <child v-on:cancle="other"></child>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
// js
Vue.component("child", {
    template: '<button v-on:click="cancle">我是按钮,点我触发父组件事件</button>',
    methods: {
        cancle: function() {
            // 与父组件通信一定要加上这句话
            this.$emit('cancle');
        }
    }
});
var vue1 = new Vue({
    // 这里的el可视为父组件
    // 所以下面这个片段是放在#m-dialog里面的
    el:"#m-dialog",
    data: {
        flag: true
    },
    methods: {
        other: function() {
            alert();
        }
    }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
ref属性

使用 ref 为子组件 / 元素指定一个引用 ID:ref = 'ID'。通过vm.$refs.ID 获取该组件/元素。

<div id="app">
    <child ref = 'mycom'></child> // 子组件
</div>
  • 1
  • 2
  • 3

则在父组件中,通过vm.$refs.mycom 获取整个子组件对象。(vm是父组件)


指令

Mustache语法、v-text和v-html
  • {{content}} 更新文本(部分文本)的textContent
  • <div v-text = 'content'></div> 插入的content会被解析为字符串
  • <div v-html = 'content'></div> 更新div的innerHtml,内容按普通 HTML 插入 - 不会作为 Vue 模板进行编译
v-cloak
// html
<div v-cloak> {{ content }} </div>

// css
[v-cloak] {
  display: none;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

则div中content会在vue实例准备完毕之后才显示。次方法可以避免网速较慢时候,{{ content }} 引起的闪烁。

v-bind
  • 绑定class
<div class = "static" v-bind: class = "{ active: isActive, 'text-danger': hasError }"></div>
  • 1

若data如下

data() {
  return {
    isActive: true,
    hasError: false
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

结果渲染为<div class = "static active"></div> 
绑定的数据对象不必内联定义在模板中

// 渲染结果同上
<div v-bind:class="classObject"></div>
data: {
  classObject: {
    active: true,
    'text-danger': false
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

也可以返回绑定对象的计算属性

<div v-bind:class="classObject"></div>
data: {
  isActive: true,
  error: null
},
computed: {
  classObject: function () {
    return {
      active: this.isActive && !this.error,
      'text-danger': this.error && this.error.type === 'fatal'
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 绑定style
  • 绑定元素的属性 
    例如,绑定一个input标签的name属性
<input type = 'text' v-bind = "{ value: 'hello ' + name }" />

// data
data() {
  name: 'hello world!'
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

渲染结果 
这里写图片描述

v-model

上面input的value值如果发生改变,是不会“同步”到data中的name值,要实现“双向绑定”,需要使用v-model 指令。一般用在表单输入绑定,input、textarea等。

v-for

vue2.0之后,要访问遍历的数组中对象的下标,要把index写在括号中第二位。

<ul>
    <li v-for = "(item, index) in items">{{ index }} - {{ item }}</li>
</ul>

// data
items: ['item1', 'item2', 'item3']
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

结果 
这里写图片描述 
遍历一个对象的属性及属性值

<ul>
    <li v-for = "(item, key) in user">
        {{ key }}-{{ item }}
    </li>
</ul>

// data
user: {
    name: 'userName',
    age: 20
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

结果 
这里写图片描述

列表渲染中的key属性 
key 的特殊属性主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试修复/再利用相同类型元素的算法。使用 key,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。

有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。 
假如v-for 出来的元素中有相同的,console会华丽丽打出warning

[Vue warn]: Duplicate keys detected: '重复渲染项'. This may cause an update error.
  • 1

它也可以用于强制替换元素/组件而不是重复使用它。当你遇到如下场景时它可能会很有用:

  • 完整地触发组件的生命周期钩子
  • 触发过渡
<transition name = 'fade'>
     <span>{{ text }}</span>
</transition>
  • 1
  • 2
  • 3

如果span 没有绑定key 属性,则在text发生改变的时候,transition 组件会被重复使用,此时text文本被简单替换,不会触发元素过渡效果。 
这里写图片描述 
为span元素绑定key属性<span :key = 'text'>{{ text }}</span> ,则text发生变化的时候,transition 组件会被替换/重新渲染。 
这里写图片描述

v-if vs v-show

v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。

v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS display: none;进行切换。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

v-on

用在普通元素上时,只能监听原生 DOM 事件。用在自定义元素组件上时,也可以监听子组件触发的自定义事件。简写@ 
在监听键盘事件时,我们经常需要检查常见的键值。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符,以下几种写法等效。

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

<!-- 同上 -->
<input v-on:keyup.enter="submit">

<!-- 缩写语法 -->
<input @keyup.enter="submit">
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

可以通过全局 config.keyCodes 对象自定义按键修饰符别名:

// 可以使用 v-on:keyup.f1
Vue.config.keyCodes.f1 = 112
  • 1
  • 2

watch与计算属性computed

computed

模板内的表达式{{ expression }} 的逻辑若太复杂,不好看也不易维护。

<div id="example">
  {{ message.split('').reverse().join('') }}
</div>
  • 1
  • 2
  • 3

此时使用计算属性。

<div id="example">
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
  • 1
  • 2
  • 3
  • 4
var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

结果: 
Original message: “Hello”

Computed reversed message: “olleH”

也可以在methods 中定义一个reversedMessage 方法。效果一样。但是两者也有区别:

[Note] computed 只有在相关依赖发生变化的时候,才会重新求值。例如reversedMessage 属性依赖message 属性,如果message 不改变,reversedMessage 不会更新,此时多次访问reversedMessage 会立即返回缓存的值。 
看这个例子:

// html

<p>date: '{{ date }}'</p>
<p>date + 1: '{{ dateAdd }}'</p>
<!-- past 的值在vue实例生成后,不再改变 -->
<p>computed: past: '{{ past }}'</p>

<p>function called: now: '{{ now() }}'</p>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
// javascript

var vm = new Vue({
    el:"#app",
    data: function(){
        return {
            message: 'Hello',
            date: 1518673470535
        }
    },
    computed: {
        // 计算属性dateAdd的依赖是date
        dateAdd: function() {
            console.log('computed: date + 1')
            return this.date + 1
        },

        // 计算属性past没有依赖date
        past: function() {
            console.log('computed: past')
            return Date.now()
        }
     },
    methods: {
        now: function() {
            console.log('function called: now')
            return Date.now()
        }
    }
    });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

vm 实例第一次生成后,计算属性dateAdd 和past 第一次更新。now() 函数调用,所以在控制台可以看到三个消息: 
这里写图片描述 
改变date的值

  • dateAdd 更新。
  • 因为past 的值不依赖date ,所以它不更新。
  • 触发重新渲染,函数 now() 会执行,页面中{{ now() }} 也会更新。 
    这里写图片描述
watch

监听数据变化,特别是在有些数据会随着其他数据变化时变化,当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

var vm = new Vue({
    el:"#app",
    data: function(){
        return {
            firstName: 'html',
            lastName: 'css',
            fullName: 'html-css'
        }
    },
    watch: {
        // firstName 发生改变的时候,这个函数会立刻被调用
        /**
        params:
        newval:firstName新值
        oldval:firstName旧值
        **/
        firstName: function(newval, oldval) {
        // 更新fullName
        this.fullName = this.firstName + '-' + this.lastName
        }
    }
    });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

watch 属性中firstName 函数,作用就是监听firstName 的值,参数newvaloldval 都是可选的。

监控路由对象:

'$route': function(newRoute, oldRoute){
    // 获取新的路由对象和旧的路由对象
    // 对一些特定页面或者特殊操作(如返回),执行某些操作
}
  • 1
  • 2
  • 3
  • 4

过滤

  • 列表渲染v-for 渲染结果的过滤:在computed属性中使用js内置filter
// html
// ...
<tr v-for = 'item in filterItems'>
    <td>{{ item.id }}</td>
    <td>{{ item.text }}</td>
    <td>{{ item.ctime }}</td>
    <td>
        <button @click = 'remove(item.id)'>delete</button>
    </td>
</tr>

// js
computed: {
    filterItems: function() {
        var self = this
        return self.items.filter(function(item){
            return item.text.indexOf(self.value) !== -1
        })
    }
}

// data
value: ''
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

效果 
这里写图片描述

  • 本地过滤器 
    在vue组件filter 属性中定义私有过滤器。
<!-- 在双花括号中 -->
{{ message | capitalize }}

<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
  • 1
  • 2
  • 3
  • 4
  • 5
filters: {
  capitalize: function(value){
    // ...
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5

value是默认参数,取自过滤器capitalize左边管道符(|)的左边的参数,这里是message。有这几机制,则可以很好理解多级过滤。

{{ message | filterA | filterB }}
  • 1

过滤器是js的函数,有一个取自管道符(|)左边的默认参数,也可以向过滤器传递参数。

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

以上filterA接受三个参数,第一个参数是默认参数message,第二个参数是字符串常量'arg1 ,第三个参数是表达式arg 。

在创建Vue实例之前,定义全局过滤器。

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

new Vue({
  // ...
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

自定义指令

全局注册

Vue.directive('不带 v- 的指令名称', {
  钩子函数: function(para){}
}
  • 1
  • 2
  • 3

局部注册

directives: {
  '不带v-的指令名称': {
    钩子函数: function(para){}
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5

钩子函数

bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。 
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。 
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。 
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。 
unbind:只调用一次,指令与元素解绑时调用。

钩子函数的参数elbindingvnodeoldVnode。见官网-钩子函数的参数

猜你喜欢

转载自blog.csdn.net/qq_37818095/article/details/80860086