vue 笔记

423 阅读4分钟

什么是Vue

Vue 不支持 IE8 及以下版本,因为 Vue 使用了 IE8 无法模拟的 ECMAScript 5 特性。但它支持所有兼容 ECMAScript 5 的浏览器

生命周期钩子

生命周期 描述
beforeCreate 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
created 在实例创建完成后,属性已绑定。挂载阶段还没开始,DOM还未生成,$el 属性目前不可见。
beforeMount 在挂载开始之前被调用
mounted 模板编译挂载之后(不保证组件已在doucument中)
beforeUpdate 组件更新之前
updated 组件更新之后
activated keep-alive 组件激活时调用
deactivated keep-alive 组件停用时调用
beforeDestroy 实例销毁前调用,在这一步,实例仍然完全可用
destroyed 实例销毁后调用
errorCaptured 当捕获一个来自子孙组件的错误时被调用

注意箭头函数的使用

不在选项属性或者回调上使用箭头函数,因为箭头函数是与父级作用域绑定在一起的,this指向的是最近的普通函数的作用域

created: () => console.log(this.a); //Uncaught TypeError: Cannot read property of undefined
vm.$watch('a', newValue => this.myMethod()); //Uncaught TypeError: this.myMethod is not a function

计算属性和侦听器

计算属性

  • 当有些数据需要随着其他数据的变动而变动时,数据依赖;
  • 基于它们的依赖进行缓存的,不希望有依赖可使用方法实现;
//计算属性默认只有 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]
    }
  }
}
//...

侦听器: 当需要的数据在变化时执行异步执行或开销较大的操作

绑定 HTML Class

可同时写固定class和动态class

<div :class="[icon, {isActive ? activeClass : ''}]"></div>

条件渲染

1.在 <template> 元素上使用 v-if 条件渲染分组

<template v-if>
    <div>template元素不会渲染到页面上</div>
</template>

2.用 key 管理可复用的元素

<!--这两个元素是完全独立的,不要复用它们-->
<!--如果不使用 key 两个模板使用了相同的元素,<input> 不会被替换掉——仅仅是替换了它的 placeholder-->
<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>

3.v-if='null、undefined 或 false' 表示为 fasle

列表渲染

1.可用 of 替代 in, 因为它更接近 JavaScript 迭代器的用法

<div v-for="item of items"></div>

2.一个对象的 v-for 在遍历对象时,是按 Object.keys() 的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下是一致的。

object: {
  firstName: 'John',
  lastName: 'Doe',
  age: 30
}
<div v-for="(value, key, index) in object">
  {{ index }}. {{ key }}: {{ value }}
</div>
//结果
0.firstName: John
1.lastName: Doe
2.age: 30

3.数组更新检测

var example1 = new Vue({
  el: '#example-1',
  data: {
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})
  • 变异方法:触发视图更新 包括: pop(), push(), shift(), unshift(), splice(), sort(), reverse()
example1.items.push({ message: 'AOA' })
  • 替换数组: 用一个含有相同元素的数组去替换原来的数组是非常高效的操作 包括: filter(), concat(), slice(), 总是返回一个新数组
example1.items = example1.items.filter(function (item) {
  return item.message.match(/Foo/)
})
  • 注意事项: 由于 JavaScript 的限制,Vue 不能检测以下变动的数组
  1. 当你利用索引直接设置一个项时,响应式: Vue.set(vm.items, indexOfItem, newValue)vm.$set(vm.items, indexOfItem, newValue)
  2. 当你修改数组的长度时: vm.items.splice(newLength)

4.对象更改检测注意事项 还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除

var vm = new Vue({
  data: {
    userProfile: {
      name: 'Anika'
    }
  }
})

//添加单个属性
Vue.set(vm.userProfile, 'age', 27);
vm.$set(vm.userProfile, 'age', 27);

//添加多个属性
vm.userProfile = Object.assign({}, vm.userProfile, {
    age: 27,
    height: 188
})

5.显示过滤/排序结果

  • 计算属性
<li v-for="n in evenNumbers">{{ n }}</li>
data: {
    numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
    evenNumbers: funciton() {
        return this.numbers.filter(function (num) {
            return num % 2 === 0;
        }) 
    }
}
  • methods
<li v-for="n in even(numbers)">{{ n }}</li>
data: {
    numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
    even(numbers) {
        return numbers.filter(function (num) {
            return num % 2 === 0;
        }) 
    }
}

事件处理

1.事件修饰符

  • .stop: 阻止单击事件继续传播
  • .prevent: 提交事件不再重载页面
  • .capture: 添加事件监听器时使用事件捕获模式,即元素自身触发的事件先在此处理,然后才交由内部元素进行处理
  • .self: 只当在 event.target 是当前元素自身时触发处理函数, 即事件不是从内部元素触发的
  • .once: 点击事件将只会触发一次
  • .passive: 尤其能够提升移动端的性能, 不和.prevent一起使用

2.按键修饰符

  • .enter 回车键 <input @keyup.enter="submit">
  • .delete 捕获“删除”和“退格”键

v-model修饰符

.lazy: 使用 change 事件进行同步

<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg">

.number: 自动将用户的输入值转为数值类型

<input v-model.number="age" type="number">

.trim: 自动过滤用户输入的首尾空白字符

<input v-model.trim="msg">

v-model等价于

<input v-model="searchText">
<!--等价于-->
<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>

组件

1.data必须是一个函数,每个实例可以维护一份被返回对象的独立的拷贝

2.通过事件向父级组件发送消息: $emit('方法名字', 参数), 通过方法名触发事件并传值

<!--应用页面-->
<div>
    <div :style="{ fontSize: postFontSize + 'em' }">
    	<blog-post
    		v-for="post of posts"
    		:key="post.id"
    		:post="post"
    		@enlarge-text='enlarge'>
    	</blog-post>
    </div>
    <custom-input v-model="searchText"></custom-input>
</div>
<script>
    //组件模板
    Vue.component('blog-post', {
    	props: ['post'],
    	template: `
    		<div>
    			<p>{{post.title}}</p>
    			<button @click="$emit('enlarge-text', 0.1)">
    	        	Enlarge text
    	      	</button>
    			<div v-html="post.contet"></div>
    		</div>
    	`
    })
    //v-model
    Vue.component('custom-input', {
      props: ['value'],
      template: `
        <input
          v-bind:value="value"
          v-on:input="$emit('input', $event.target.value)"
        >
      `
    })
    
    //vue实例
    var vm = new Vue({
		el: '#app',
		data: {
			posts: [
				{ id: 1, title: 'My journey with Vue', contet: 'sdfasdfad' },
				{ id: 2, title: 'Blogging with Vue', contet: 'sdfasdfad22'  },
				{ id: 3, title: 'Why Vue is so fun', contet: 'sdfasdfads1111'  }
			],
			postFontSize: 1,
			searchText: ''
		},
		methods: {
			enlarge(e) {
				this.postFontSize += e; //e: 0.1
			} 
		}
	})
</script>

3.插槽<slot></slot>

  • 作用域插槽

例如一个<todo-list>组件模板可能包含了以下代码:

<ul>
    <li v-for="todo in todos" :key="todo.id">
        <!-- 我们为每个 todo 准备了一个插槽,将 `todo` 对象作为一个插槽的 prop 传入。-->
        <slot v-bind:todo="todo">
            <!-- 回退的内容 -->
            {{ todo.text }}
        </slot>
    </li>
</ul>

现在当我们使用 <todo-list> 组件的时候,我们可以选择为待办项定义一个不一样的 <template> 作为替代方案,并且可以通过 slot-scope 特性从子组件获取数据

<todo-list :todos="todos">
    <template slot-scope="slotProps">
        <span v-if="slotProps.todo.isComplete"></span>
        {{ slotProps.todo.text }}
    </template>
</todo-list>

<!-- ES2015解构赋值 -->
<todo-list :todos="todos">
    <template slot-scope="{ todo }">
        <span v-if="todo.isComplete"></span>
        {{ todo.text }}
    </template>
</todo-list>

4.动态组件is

<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>

5.基础组件的自动化全局注册

6.自定义事件

  • 始终使用 kebab-case 的事件名
<my-component @my-event="doSomething"></my-component>

Vue.component('my-component', {
    props: [],
    template: `
        <div @click="$emit('my-event')"></div>
    `
})
  • 将原生事件绑定到组件
  1. $attrs属性: 使用 v-bind="$attrs", 将父组件中不被认为 props特性绑定的属性传入子组件中,通常配合 interitAttrs 选项一起使用
  2. $listeners属性
    <base-input label="vue啊" value='ddd' v-model='getValue'></base-input>
    
    Vue.component('base-input', {
    	inheritAttrs: false,
    	props: ['label', 'value'],
    	computed: {
        	inputListeners: function() {
        		var vm = this
        		// `Object.assign` 将所有的对象合并为一个新对象
        		return Object.assign({},
        			// 我们从父级添加所有的监听器
        			this.$listeners,
        			// 然后我们添加自定义监听器,
        			// 或覆写一些监听器的行为
        			{
        				// 这里确保组件配合 `v-model` 的工作
        				input: function(event) {
        					vm.$emit('input', event.target.value)
        				}
            		}
        		)
    	    }
    	},
    	template: `
    	    <label>
    	      {{ label }}
    	      <input
    	        v-bind="$attrs"
    	        v-bind:value="value"
    	        v-on="inputListeners"
    	      >
    	    </label>
    	  `
    })
    
  3. .sync修饰符

待续