vue2.x阅读笔记

v-once

作用:
	只赋值一次
注意:
	包括子项都是只赋值一次

模板

	1.v-text  v-html
	2.{}语法
	3.如果是html的属性,则用v-bind绑定,简写:
注意:
	1.可以使用javascript,但只能是包含单个表达式
	2.模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date 。你不应该在模板表达式中试图访问用户定义的全局变量。
比如:
	{{ number + 1 }}
	{{ ok ? 'YES' : 'NO' }}
	{{ message.split('').reverse().join('') }}

指令

作用:
	当表达式的值改变时,将其产生连带影响,响应式的作用于dom中
例子:	
	1.v-if  
	2.v-bind
	3.v-on
修饰符:
	以.为特殊后缀,指出指令用何种特殊方式绑定

计算属性

作用:
	模板中写逻辑不便于维护,代码冗杂
	逻辑业务相同的话难以处理
	建议写在computed里面
例子:
	<div id="example">
	  <p>Original message: "{{ message }}"</p>
	  <p>Computed reversed message: "{{ reversedMessage }}"</p>
	</div>

	var vm = new Vue({
	  el: '#example',
	  data: {
	    message: 'Hello'
	  },
	  computed: {
	    // 计算属性的 getter
	    reversedMessage: function () {
	      // `this` 指向 vm 实例
	      return this.message.split('').reverse().join('')
	    }
	  }
	})

注意:
	1.模板中调用不需要写()
	2.当message变化时,所有依赖reversedMessage的绑定都会更新

computed与methods的区别:
	1.它是存在于缓存中的,计算了一次就直接调缓存的值。除非相关的依赖发生改变时了,才会重新计算。

坑:
	computed: {
	  now: function () {
	    return Date.now()
	  }
	}
此处的Date.now()不是响应式依赖,所以不会多次执行,但在methods里面会多次执行

computed与侦听属性:
例子:
	1.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
		    }
		  }
		})
	
	2.computed的做法

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

html中直接用computed里面的值
只能改firstName lastName,有setter才能改fullName

关于computed的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]
	    }
	  }
	}
可以改fullName

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

侦听器watch

例子:
data: {
  	question: '',
  	answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
   // 如果 `question` 发生改变,这个函数就会运行
   question: function (newQuestion) {
     this.answer = 'Waiting for you to stop typing...'
     this.getAnswer()
   }
},
methods: {
    getAnswer: function(){
		//..逻辑处理
	}
}
在这个示例中,使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

class和style绑定

<div v-bind:class="{ active: isActive }"></div>

上面的语法表示 active 这个 class 存在与否将取决于数据属性 isActive 的 值。

多个class的情况:

	<div class="static"
	     v-bind:class="{ active: isActive, 'text-danger': hasError }">
	</div>
		
	data: {
	  isActive: true,
	  hasError: false
	}

结果为:
	<div class="static active"></div>

绑定的数据对象不必内联定义在模板里

	<div v-bind:class="classObject"></div>

	data: {
	  classObject: {
	    active: true,
	    'text-danger': false
	  }
	}

还可以绑定在computed里面:

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

数组语法:
	<div v-bind:class="[activeClass, errorClass]"></div>

	data: {
	  activeClass: 'active',
	  errorClass: 'text-danger'
	}

渲染为:
	<div class="active text-danger"></div>

数组中用对象写法:
	div v-bind:class="[{ active: isActive }, errorClass]"></div>

绑定内联样式

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

data: {
  activeColor: 'red',
  fontSize: 30
}

直接绑定比较好看:
	<div v-bind:style="styleObject"></div>

	data: {
	  styleObject: {
	    color: 'red',
	    fontSize: '13px'
	  }
	}

数组语法:
	作用:可以将多个样式应用到同个元素上
	<div v-bind:style="[baseStyles, overridingStyles]"></div>

条件渲染

v-if  v-else  v-else-if v-show
1.
	<h1 v-if="ok">Yes</h1>
2.
	<h1 v-if="ok">Yes</h1>
	<h1 v-else>No</h1>
3.
	<template v-if="ok">
	  <h1>Title</h1>
	  <p>Paragraph 1</p>
	  <p>Paragraph 2</p>
	</template>
里面的东西全没了如果ok为false的话

模板的复用会高效,但存在问题。
模板复用问题,用Key解决
	<template v-if="loginType === 'username'">
	  <label>Username</label>
	  <input placeholder="Enter your username">
	</template>
	<template v-else>
	  <label>Email</label>
	  <input placeholder="Enter your email address">
	</template>
这里的input会复用,不会清除已输入的值
	<template v-if="loginType === 'username'">
	  <label>Username</label>
	  <input placeholder="Enter your username" key="username-input">
	</template>
	<template v-else>
	  <label>Email</label>
	  <input placeholder="Enter your email address" key="email-input">
	</template>
加了key之后,切换loginType,input每次都会被重新渲染

v-show
	v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display。
注意:
	v-show 不支持 <template> 元素,也不支持 v-else。

关于v-if与v-show对比:

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

2.	v-if 惰性的,只有条件成立才渲染

3.	v-show 元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

4.	v-if开销大,如果频繁操作的话建议用v-show

另外提一点:
	当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级。

列表渲染

v-for基本用法:
	<ul id="example-2">
	  <li v-for="(item, index) in items">
	    {{ parentMessage }} - {{ index }} - {{ item.message }}
	  </li>
	</ul>

	var example2 = new Vue({
	  el: '#example-2',
	  data: {
	    parentMessage: 'Parent',
	    items: [
	      { message: 'Foo' },
	      { message: 'Bar' }
	    ]
	  }
	})
额外知识:
	如果v-for循环的是一个对象,可以有三个参数
		<div v-for="(value, key, index) in object">
		  {{ index }}. {{ key }}: {{ value }}
		</div>
	渲染的结果是:
	 0.key.value
	 1.key.value
	 2.key.value

2.2.0+ 的版本里,当在组件中使用 v-for 时,key 现在是必须的。

关于v-for中的key
	1.	如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素
	2.	建议尽可能在使用 v-for 时提供 key,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
	3.	它是 Vue 识别节点的一个通用机制

数组更新检测

变异方法:
	改变原数组
非变异方法:
	不改变原数组
注意:
	由于javascript的限制,Vue不能检测以下变动
		1.	直接利用索引设置一个项
			解决:
			//Vue.set
			Vue.set(example1.items, indexOfItem, newValue)
			// Array.prototype.splice
			example1.items.splice(indexOfItem, 1, newValue)
		2.	修改数组长度
			解决:
			example1.items.splice(newLength)

对象更新检测

Vue 不能检测对象属性的添加或删除:
	var vm = new Vue({
	  data: {
	    a: 1
	  }
	})
	// `vm.a` 现在是响应式的
	
	vm.b = 2
	// `vm.b` 不是响应式的	
对于已经创建的实例,可以这么做
	var vm = new Vue({
	  data: {
	    userProfile: {
	      name: 'Anika'
	    }
	  }
	})

	Vue.set(vm.userProfile, 'age', 27)
	或者
	vm.$set(this.userProfile, 'age', 27)
	//它只是全局 Vue.set 的别名

显示过滤/排序结果

有时,我们想要显示一个数组的过滤或排序副本,而不实际改变或重置原始数据。在这种情况下,可以创建返回过滤或排序数组的计算属性或者用methods

这里是方法:
<li v-for="n in even(numbers)">{{ n }}</li>

data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
  even: function (numbers) {
    return numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}

v-if和v-for搭配使用

<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo }}
</li>
上面的代码只传递了未 complete 的 todos。
v-for的优先级比v-if高

事件处理

事件监听

有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法:

<button v-on:click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>

// ...
methods: {
  warn: function (message, event) {
    // 现在我们可以访问原生事件对象
    if (event) event.preventDefault()
    alert(message)
  }
}

事件修饰符

.stop
.prevent
.capture
.self
.once 只触发一次

<!-- 阻止单击事件继续传播 -->
<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>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

注意:
	使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 @click.prevent.self 会阻止所有的点击,而 @click.self.prevent 只会阻止对元素自身的点击。

按键修饰符

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

记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名:

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

<!-- 缩写语法 -->
<input @keyup.enter="submit">

.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right

可以通过全局 config.keyCodes 对象自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112

自动匹配按键修饰符

你也可直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符:
<input @keyup.page-down="onPageDown">
在上面的例子中,处理函数仅在 $event.key === 'PageDown' 时被调用。

鼠标按钮修饰符

.left
.right
.middle

当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何自己清理它们。

表单输入绑定

基础用法

v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。

对于需要使用输入法 (如中文、日文、韩文等) 的语言,你会发现 v-model 不会在输入法组合文字过程中得到更新。如果你也想处理这个过程,请使用 input 事件。

复选框

绑定布尔值
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>

多选按钮

<div id='example-3'>
  <input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
  <label for="jack">Jack</label>
  <input type="checkbox" id="john" value="John" v-model="checkedNames">
  <label for="john">John</label>
  <input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
  <label for="mike">Mike</label>
  <br>
  <span>Checked names: {{ checkedNames }}</span>
</div>

new Vue({
  el: '#example-3',
  data: {
    checkedNames: []
  }
})
用一个数组去装复选框

单选按钮

<div id="example-4">
  <input type="radio" id="one" value="One" v-model="picked">
  <label for="one">One</label>
  <br>
  <input type="radio" id="two" value="Two" v-model="picked">
  <label for="two">Two</label>
  <br>
  <span>Picked: {{ picked }}</span>
</div>

new Vue({
  el: '#example-4',
  data: {
    picked: ''
  }
})

用字符串去存name的值,单选按钮的name属性要一致,v-model相当于name

选择框

<div id="example-5">
  <select v-model="selected">
    <option disabled value="">请选择</option>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <span>Selected: {{ selected }}</span>
</div>

new Vue({
  el: 'example-5',
  data: {
    selected: ''
  }
})

绑定到一个字符串中

注意:
	如果 v-model 表达式的初始值未能匹配任何选项,<select> 元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,更推荐像上面这样提供一个值为空的禁用选项。

用 v-for 渲染的动态选项:
<select v-model="selected">
  <option v-for="option in options" v-bind:value="option.value">
    {{ option.text }}
  </option>
</select>
<span>Selected: {{ selected }}</span>	

new Vue({
  el: '...',
  data: {
    selected: 'A',
    options: [
      { text: 'One', value: 'A' },
      { text: 'Two', value: 'B' },
      { text: 'Three', value: 'C' }
    ]
  }
})

input修饰符

.trim
.number
.lazy

组件

注册

全局注册

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

// 创建根实例
new Vue({
  el: '#example'
})

局部注册

var Child = {
  template: '<div>A custom component!</div>'
}

new Vue({
  components: {
    // <my-component> 将只在父组件模板中可用
    'my-component': Child
  }
})	
注册组件注意事项:
像 ul,ol,table,select 这样的元素里允许包含的元素有限制,而另一些像 option 这样的元素只能出现在某些特定元素的内部。会当做无效。

变通的方案是使用特殊的 is 特性:

<table>
  <tr is="my-row"></tr>
</table>

如果是来源于以下字符串模板,则没有影响:

<script type="text/x-template">
JavaScript 内联模板字符串
.vue 组件

Data必须是函数

在组件的data必须是函数:

var data = { counter: 0 }

Vue.component('simple-counter', {
  template: '<button v-on:click="counter += 1">{{ counter }}</button>',
  // 技术上 data 的确是一个函数了,因此 Vue 不会警告,
  // 但是我们却给每个组件实例返回了同一个对象的引用
  data: function () {
    return data
  }
})

new Vue({
  el: '#example-2'
})

由于这三个组件实例共享了同一个 data 对象,因此递增一个 counter 会影响所有组件!这就错了。我们可以通过为每个组件返回全新的数据对象来修复这个问题:

data: function () {
  return {
    counter: 0
  }
}

现在每个 counter 都有它自己内部的状态了

父子间的传值

父传子

Vue.component('child', {
  // 声明 props
  props: ['message'],
  template: '<span>{{ message }}</span>'
})	

<child message="hello!"></child>

关于camelCase vs. kebab-case
HTML 特性是不区分大小写的。所以,当使用的不是字符串模板时,camelCase (驼峰式命名) 的 prop 需要转换为相对应的 kebab-case (短横线分隔式命名):

Vue.component('child', {
  // 在 JavaScript 中使用 camelCase
  props: ['myMessage'],
  template: '<span>{{ myMessage }}</span>'
})

<!-- 在 HTML 中使用 kebab-case -->
<child my-message="hello!"></child>
prop
动态绑定prop

与绑定到任何普通的 HTML 特性相类似,我们可以用 v-bind 来动态地将 prop 绑定到父组件的数据。每当父组件的数据变化时,该变化也会传导给子组件:

<div>
  <input v-model="parentMsg">
  <br>
  <child :my-message="parentMsg"></child>
</div>

当子组件拿到父组件的参数后
1.	Prop 作为初始值传入后,子组件想把它当作局部数据来用;
2.	Prop 作为原始数据传入,由子组件处理成其它数据输出。
对这两种情况,正确的应对方式是:
1.	定义一个局部变量,并用 prop 的值初始化它:
	props: ['initialCounter'],
	data: function () {
	  return { counter: this.initialCounter }
	}
2.	定义一个计算属性,处理 prop 的值并返回:
	props: ['size'],
	computed: {
	  normalizedSize: function () {
	    return this.size.trim().toLowerCase()
	  }
注意:
	在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。

prop验证
要指定验证规则,需要用对象的形式来定义 prop,而不能用字符串数组:

Vue.component('example', {
  props: {
    // 基础类型检测 (`null` 指允许任何类型)
    propA: Number,
    // 可能是多种类型
    propB: [String, Number],
    // 必传且是字符串
    propC: {
      type: String,
      required: true
    },
    // 数值且有默认值
    propD: {
      type: Number,
      default: 100
    },
    // 数组/对象的默认值应当由一个工厂函数返回
    propE: {
      type: Object,
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        return value > 10
      }
    }
  }
})
注意:
prop 会在组件实例创建之前进行校验,所以在 default 或 validator 函数里,诸如 data、computed 或 methods 等实例属性还无法使用。

非prop特性
???

替换/合并现有的特性
如果父子模板均设了class,将会自动合并

自定义事件(子传父、非父子组件传值也可以)

使用 $on(eventName) 监听事件
使用 $emit(eventName) 触发事件

父组件中用v-on监听:
<div id="counter-event-example">
  <p>{{ total }}</p>
  <button-counter @increment="incrementTotal"></button-counter>
  <button-counter @increment="incrementTotal"></button-counter>
</div>

new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})
子组件中用$emit触发事件:
Vue.component('button-counter', {
  template: '<button @click="incrementCounter">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    incrementCounter: function () {
      this.counter += 1
      this.$emit('increment')
    }
  },
})

父组件中用@监听子组件$emit的自定义事件,然后触发对应的父组件中的methods

在本例中,子组件已经和它外部完全解耦了。它所做的只是报告自己的内部事件,因为父组件可能会关心这些事件。请注意这一点很重要。

如果需要同步父子间的prop
<comp :foo.sync="bar"></comp>

非父子传值
有时候,非父子关系的两个组件之间也需要通信。在简单的场景下,可以使用一个空的 Vue 实例作为事件总线:
var bus = new Vue()
// 触发组件 A 中的事件
bus.$emit('id-selected', 1)
// 在组件 B 创建的钩子中监听事件
bus.$on('id-selected', function (id) {
  // ...
})
//bus.$on放在父组件的钩子里面等触发,复杂情况下可以用状态管理模式Vuex

插槽

假设子组件:
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

父组件:
<app-layout>
  <h1 slot="header">这里可能是一个页面标题</h1>

  <p>主要内容的一个段落。</p>
  <p>另一个主要段落。</p>

  <p slot="footer">这里有一些联系信息</p>
</app-layout>

渲染结果:
<div class="container">
  <header>
    <h1>这里可能是一个页面标题</h1>
  </header>
  <main>
    <p>主要内容的一个段落。</p>
    <p>另一个主要段落。</p>
  </main>
  <footer>
    <p>这里有一些联系信息</p>
  </footer>
</div>

posted on 2017-12-18 13:54  ouruixi  阅读(211)  评论(0)    收藏  举报

导航