直接下载,使用script标签导入或MDN引入,或npm
new Vue()
后会返回 一个实例对象,定义一个变量接收const vm=new Vue()
const vm=new Vue({})
①.el(或$mount)
作用:配置控制的元素,表示Vue要控制的区域,值为css选择器
<div id="app">div>
const vm = new Vue({
el: '#app' // 控制id为app的元素
})
或
const vm = new Vue({})
vm.$mount('#app');
②.data
const vm = new Vue({
el: '#app',
data: {
‘xxx’:'yyyy',
}
})
③.插值表达式
数据包括:data中的变量、数字、字符串、布尔值、underfined、null、数组、对象、表达式(运算表达式、逻辑表达式、三元表达式、函数调用)
注意:在插值表达式中直接书写对象类型值时,不要将三个{}连在一起,这样会报错
注意:只要插值表达式中使用了数据,必须在data中声明过,否则会报错;
除非使用了data中引用类型(对象)中的未被声明的变量。
(在作用域上找不到,报错 ;在原型链上找不到,值为undefined;undefined为js基本类型值,所以就不报错啦 )
答:当创建vue实例时,vue会将data中的成员代理给vue实例,目的是为了实现响应式,监控数据变化,执行某个监听函数
为了防止名称冲突。因为会将data中数据代理给vue,假如说我们自己写的data名称和vue中自带的属性冲突了,那么就会覆盖vue内部的属性,所以vue会把自己内部的属性成员名称前加上$或_,如果加上的是$,代表是我们可以使用的,如果加上的是_,是vue自己内部使用的方法或属性,我们不需要调用
– 1、更改的数据必须是存在的数据,否则不能重新渲染页面,因为他监听不到;
–2、更改的数据必须已渲染过的数据,否则从性能角度考虑,不会重新渲染页面;
–3、利用索引直接设置一个数组项时;
–4、修改数组的长度时;
–5、添加或删除对象
更改数组:
1. 利用数组变异方法:push、pop、shift、unshift、splice、sort、reverse
2. 利用vm.$set/Vue.set实例方法
3. 利用vm.$set或Vue.set删除数组中的某一项
vm.$set是Vue.set的别名
使用方法:Vue.set(object, propertyName, value),也就是这个意思:Vue.set(要改谁,改它的什么,改成啥)
vm.$delete是Vue.delete的别名
使用方法:Vue.delete(object, target),也就是这个意思:Vue.delete(要删除谁的值,删除哪个)
vue更新DOM的操作是异步执行的,只要侦听到数据变化,将开启一个异步队列,如果一个数据被多次变更,那么只会被推入到队列中一次,这样可以避免不必要的计算和DOM操作。
答:利用vm.$nextTick或Vue.nextTick,在页面重新渲染,DOM更新后,会立刻执行vm.$nextTick
使用方式:
①、
const vm = new Vue({})
// 1. 使用vm.$nextTick
vm.$nextTick(() => {})
// 2. 使用Vue.nextTick
Vue.nextTick(() => {})
②、vm.nextTick和Vue.nextTick还可以作为Promise使用
const vm = new Vue({})
// 1. 使用vm.$nextTick
vm.$nextTick().then(() => {})
// 2. 使用Vue.nextTick
Vue.nextTick().then(() => {})
Vue.nextTick内部函数的this指向window
Vue.nextTick(function () {
console.log(this); // window
})
vm.$nextTick内部函数的this指向Vue实例对象
vm.$nextTick(function () {
console.log(this); // vm实例
})
// 控制台打印顺序:promise > timeout
setTimeout(() => {
console.log('timeout');
}, 0)
Promise.resolve().then(() => {
console.log('promise');
})
– 在nextTick的实现源码中,会先判断是否支持微任务,不支持后,才会执行宏任务 if(typeof Promise !== 'undefined') {
// 微任务
// 首先看一下浏览器中有没有promise
// 因为IE浏览器中不能执行Promise
const p = Promise.resolve();
} else if(typeof MutationObserver !== 'undefined') {
// 微任务
// 突变观察
// 监听文档中文字的变化,如果文字有变化,就会执行回调
// vue的具体做法是:创建一个假节点,然后让这个假节点稍微改动一下,就会执行对应的函数
} else if(typeof setImmediate !== 'undefined') {
// 宏任务
// 只在IE下有
} else {
// 宏任务
// 如果上面都不能执行,那么则会调用setTimeout
}
const data={
name:'lin',
age:18,
sex:'man',
love:{
food:'fish',
play:'swing'
},
hobby:['asfda','afasd'],
}
//针对数组变异方法的处理
const arrayProto=Array.prototype;
const arrayMethods=Object.create(arrayProto);//创建一个新的数组原型方法的对象
['push','pop','shift','unshift','sort','splice','reverse'].forEach(method=>{
arrayMethods[method]=function(){
arrayProto[method].call(this,...arguments);
render();//变异,原来数组的方法上没有重新渲染
}
})
function defineReactive(data,key,value){
observer(data[key]);
Object.defineProperty(data,key,{
get(){
console.log('du');
return value;
},
set(newValue){
if(newValue!=value){
value=newValue;
console.log('xie');
render();
}
}
})
}
function observer(data){
if(Array.isArray(data)){//为了防止数组情况出现,vue不能对数组进行递归遍历,因为太耗性能,所以使用数组的变异方法
data.__proto=arrayMethods;
return;
}
if(typeof data ==='object'){
for (let key in data) {
defineReactive(data,key,data[key]);
}
}
}
function render(){
console.log('页面渲染');
}
//实现$set
function $set(data,key,value){
if(Array.isArray(data)){
data.splice(key,1,value);
return value;
}
else if(typeof data ==='object'){
defineReactive(data,key,value);//给新添加的值也加上监听
render();
return value;
}
}
//实现$delete
function $delete(data,key){
if(Array.isArray(data)){
data.splice(key,1);
return;
}
delete data[key];
render();
}
observer(data);
<span v-pre>{{ msg }}span>
这个指令保持在元素上直到关联实例结束编译
可以解决闪烁的问题
和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕
[v-cloak] {
display: none;
}
<div v-cloak>
{{ message }}
div>
<span v-once>{{msg}}span>
<div v-once>
<h1>commenth1>
<p>{{msg}}p>
div>
<span v-text="msg">span>
<span>{{msg}}span>
v-text VS Mustache
①v-if
切换多个元素
元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含
元素② v-else
③ v-else-if
④ v-show
<h1 v-show="ok">Hello!h1>
注意 v-if VS v-show
元素作用:动态地绑定一个或多个特性
缩写 ‘:’
:后的为传递的参数(①固定参数,②动态参数)
<img v-bind:src="imageSrc">
<button v-bind:[key]="value">button>
<img :src="imageSrc">
<button :[key]="value">button>
<img :src="'/path/to/images/' + fileName">
没有参数时,可以绑定到一个包含键值对的对象。注意此时 class 和 style 绑定不支持数组和对象。
<div v-bind="{ id: someProp, 'other-attr': otherProp }">div>
由于字符串拼接麻烦且易错,所以在绑定 class 或 style 特性时,Vue做了增强,表达式的类型除了字符串之外,还可以是数组或对象。
绑定class
<div v-bind:class="{ red: isRed }">div>
上面的语法表示 active 这个 class 存在与否将取决于数据属性 isActive 的 真假。注意
:当属性名中存在-
等符号时要使用‘‘
将属性名括起来;若是不存在特殊符号则不需要。<div v-bind:class="[classA, classB]">div>
<div v-bind:class="[isActive ? activeClass : '', errorClass]">div>
<div v-bind:class="[classA, { classB: isB, classC: isC }]">
<div v-bind:class="classA" class="red">
<div v-bind:class="classA" class="red">
绑定style
<div v-bind:style="{ fontSize: size + 'px' }">div>
data: {
size: 30
}
也可以直接绑定一个样式对象,这样模板会更清晰:<div v-bind:style="styleObject">div>
data: {
styleObject: {
fontSize: '13px'
}
}
<div v-bind:style="[styleObjectA, styleObjectB]">div>
<div v-bind:style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }">div>
这样写只会渲染数组中最后一个被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex。缩写: :
修饰符:
修饰符 (modifier) 是以英文句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。
.camel
由于绑定特性时,会将大写字母转换为小写字母,如:
<svg :viewBox="viewBox">svg>
所以,Vue提供了v-bind修饰符 camel,该修饰符允许在使用 DOM 模板时将 v-bind 属性名称驼峰化,例如 SVG 的 viewBox 属性
<svg :view-box.camel="viewBox">svg>
.prop
被用于绑定 DOM 属性 (property)
<div v-bind:text-content.prop="text">div>
.sync
与组件的双向数据绑定有关
<div id="app">
<button v-on:click="counter += 1">点击加 1button>
<p>按钮被点击了 {{ counter }} 次p>
div>
const vm = new Vue({
el: 'app',
data: {
counter: 0
}
})
<div id="app">
<button v-on:click="addCounter">点击加 1button>
<p>按钮被点击了 {{ counter }} 次p>
div>
const vm = new Vue({
el: '#app',
data: {
counter: 0
},
// 在 methods 对象中定义方法
methods: {
addCounter: function (e) {
// this 在方法里指向当前 Vue 实例
this.counter += 1;
// e 是原生 DOM 事件
cosnole.log(e.target);
}
}
})
可以利用特殊变量 $event(当使用方法传入多个参数时,得传入$event才能使用e.target):
<div id="app">
<button v-on:click="addCounter(5, $event)">点击加 5button>
<p>按钮被点击了 {{ counter }} 次p>
div>
new Vue({
el: '#app',
methods: {
addCounter: function (num, e) {
this.counter += 5;
cosnole.log(e.target);
}
}
})
Vue版本需要2.6.0+
<div v-on:[event]="handleClick">点击,弹出1div>
const vm = new Vue({
el: '#app',
data: {
event: 'click'
},
methods: {
handleClick () {
alert(1);
}
}
})
Vue版本需要2.4.0+。
<div v-on="{ mousedown: doThis, mouseup: doThat }">div>
<div id="app">
<div @click="alert('div')">
<button @click.stop="alert('button')">点击button>
div>
div>
const vm = new Vue({
el: '#app',
methods: {
alert(str) { alert(str); }
}
})
<div id="app">
<form v-on:submit.prevent="onSubmit">
<input type="submit">
form>
<form v-on:submit.prevent>
<input type="submit">
form>
div>
const vm = new Vue({
el: '#app',
methods: {
onSubmit() { console.log('submit'); }
}
})
<div id="app">
<div @click.capture="alert('div')">
<button @click="alert('button')">点击button>
div>
div>
const vm = new Vue({
el: '#app',
methods: {
alert(str) { alert(str) }
}
})
只当事件是从侦听器绑定的元素本身触发时才触发回调
<div id="app">
<div id="app">
<div :style="{ backgroundColor: 'red' }"
@click.self="alert('div')">
<button @click="alert('button')">点击button>
div>
div>
div>
const vm = new Vue({
el: '#app',
methods: {
alert(str) { alert(str) }
}
})
只触发一次回调
2.1.4新增
点击两次button按钮,只弹出一次button
<div id="app">
<button @click.once="alert('button')">点击button>
div>
const vm = new Vue({
el: '#app',
methods: {
alert(str) { alert(str) }
}
})
why passive?
在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符
<input v-on:keyup.enter="submit">
你可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符。
<input v-on:keyup.page-down="onPageDown">
在上述示例中,处理函数只会在 $event.key 等于 PageDown 时被调用。
使用 keyCode 特性也是允许的:
<input v-on:keyup.13="submit">
注意:keyCode 的事件用法已经被废弃了,并可能不会被最新的浏览器支持。
为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名:
除了使用Vue提供的按键别名之外,还可以自定义按键别名:
// 全局配置
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
Vue.config.keyCodes = {
v: 86,
f1: 112,
// 小驼峰 不可用
mediaPlayPause: 179,
// 取而代之的是 短横线分隔 且用双引号括起来
"media-play-pause": 179,
up: [38, 87]
}
<input type="text" @keyup.media-play-pause="method">
可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。
修饰键与常规按键不同,在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态,换句话说,只有在按住 ctrl 的情况下释放其它按键,才能触发 keyup.ctrl。而单单释放 ctrl 也不会触发事件。如果你想要这样的行为,请为 ctrl 换用 keyCode:keyup.17。
<input @keyup.alt.67="clear">
<div @click.ctrl="doSomething">Do somethingdiv>
<button @click.ctrl="onClick">Abutton>
<button @click.ctrl.exact="onCtrlClick">Abutton>
<button @click.exact="onClick">Abutton>
利用v-for指令,基于数据多次渲染元素。
用法:(item, index) in items
参数:items: 源数据数组
item:数组元素别名
index:可选,索引
可以访问所有父作用域的属性
<ul id="app">
<li v-for="(person, index) in persons">
{{ index }}---{{ person.name }}---{{ person.age }}
li>
ul>
const vm = new Vue({
el: '#app',
data: {
persons: [
{ name: '杉杉', age: 18 },
{ name: '思彤哥', age: 20 },
{ name: '成哥', age: 22 },
{ name: '邓哥', age: 88 },
]
}
})
可以利用of
替代in
作为分隔符,因为它更接近迭代器的语法:
<div v-for="item of items">div>
用法:(value, key, index) in Object
参数:value: 对象值
key:可选,键名
index:可选,索引
<ul id="app">
<li v-for="(value, key, index) in shan">
{{ value }}
li>
ul>
const vm = new Vue({
el: '#app',
data: {
shan: {
name: '杉',
age: 18,
height: '163cm'
}
}
})
用法:n in num
参数:n: 数字,从1开始
<div>
<span v-for="n in num">{{ n }} span>
div>
const vm = new Vue({
el: '#app',
data: {
num: 10
}
})
用法:str in string
参数:str: 字符串,源数据字符串中的每一个
<div>
<span v-for="str in string">{{ str }} span>
div>
const vm = new Vue({
el: '#app',
data: {
string: 'shanshan'
}
})
可以利用template元素循环渲染一段包含多个元素的内容
<ul id="app">
<template v-for="person in persons">
<li>{{ item.msg }}li>
<li>哈哈li>
template>
ul>
const vm = new Vue({
el: '#app',
data: {
persons: ['shan', 'jc', 'cst', 'deng']
}
})
Vue更新使用v-for渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是简单复用此处每个元素:
<ul id="app">
<li v-for="(person, index) in persons">
{{ person }}
<input type="text" />
<button @click="handleClick(index)">下移button>
li>
ul>
const vm = new Vue({
el: '#app',
data: {
persons: ['shan', 'jc', 'cst', 'deng']
},
methods: {
handleClick (index) {
const deleteItem = this.persons.splice(index, 1);
this.persons.splice(index + 1, 0, ...deleteItem);
}
}
})
在"就地复用"策略中,点击按钮,输入框不随文本一起下移,是因为输入框没有与数据绑定,所以vuejs默认使用已经渲染的dom,然而文本是与数据绑定的,所以文本被重新渲染。这种处理方式在vue中是默认的列表渲染策略,因为高效。
这个默认的模式是高效的,但是在更多的时候,我们并不需要这样去处理,所以,为了给Vue一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,我们需要为每项提供一个唯一key特性,Vue会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
预期值:number | string
有相同父元素的子元素必须有独特的 key,重复的 key 会造成渲染错误,key应唯一。
<ul id="app">
<li v-for="(person, index) in persons" :key="person">
{{ person }}
li>
ul>
const vm = new Vue({
el: '#app',
data: {
persons: ['杉杉', '思彤哥', '成哥', '邓哥']
}
})
不建议将数组的索引作为key值,如:
<li v-for="(person, index) in persons" :key="index">
{{ person }}
li>
当改变数组时,页面会重新渲染,Vue会根据key值来判断要不要移动元素。例如当页面重新渲染时,key值为"杉杉"的元素为
,页面重新渲染前,key值为"杉杉"的元素也为
,那么Vue就会移动这个li
元素,而不是重新生成一个元素。
当使用数组的索引作为key值时,页面重新渲染后,元素的key值会重新被赋值,例如我们将数组进行反转,
反转前:
元素 | key值 |
---|---|
|
0 |
|
1 |
|
2 |
|
3 |
反转后:
元素 | key值 |
---|---|
|
0 |
|
1 |
|
2 |
|
3 |
Vue会比对渲染前后拥有同样key的元素,发现有变动,就会再生成一个元素,如果用索引作key值得话,那么此时,所有的元素都会被重新生成。
那么key如何唯一的?
跟后台协作时,传回来的每一条数据都有一个id值,这个id就是唯一的,用id做key即可。
key不仅为v-for所有,它可以强制替换元素,而不是重复使用它:
<ul id="app">
<button @click="show = !show">{{ show ? '显示' : '隐藏'}}button>
<input type="text" v-if="show" key="a" />
<input type="text" v-else key="b" />
ul>
const vm = new Vue({
el: '#app',
data: {
show: true
}
})
永远不要把 v-if 和 v-for 同时用在同一个元素上。
当 Vue 处理指令时,v-for 比 v-if 具有更高的优先级,所以这个模板:
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
li>
ul>
将会经过如下运算:
this.users.map(function (user) {
if (user.isActive) {
return user.name
}
})
可以在表单元素上(input、textarea、select)创建双向数据绑定。即数据更新元素更新、元素更新数据也会更新。
本质上v-model为语法糖
元素类型 | 属性 | 事件 |
---|---|---|
input[checkbox]、input[radio] | checked | change |
select | value | change |
input[type=text]、textarea | value | input |
<div id="app">
<input v-model="message">
<p>Message 为: {{ message }}p>
div>
const vm = new Vue({
el: '#app',
data:; {
message: ''
}
})
绑定到布尔值,v-model=“Boolean”
<div id="app">
<input
type="checkbox"
id="checkbox"
v-model="checked"
/>
<label for="checkbox">{{ checked }}label>
div>
const vm = new Vue({
el: '#app',
data: {
checked: true
}
})
绑定到同一个数组,v-model=“Array”
数组中的值为被选中的input框value值
<div id="app">
<input type="checkbox" id="cheng" value="成哥" v-model="checkedNames">
<label for="cheng">成哥label>
<input type="checkbox" id="deng" value="邓哥" v-model="checkedNames">
<label for="deng">邓哥label>
<input type="checkbox" id="tong" value="思彤哥" v-model="checkedNames">
<label for="tong">思彤哥label>
<br>
<span>被选中的人有: {{ checkedNames }}span>
div>
const vm = new Vue({
el: '#app',
data: {
checkedNames: []
}
})
被绑定的数据和value同步
<div id="app">
<input type="radio" id="cheng" value="成哥" v-model="picked">
<label for="cheng">成哥label>
<input type="radio" id="deng" value="邓哥" v-model="picked">
<label for="deng">邓哥label>
<input type="radio" id="tong" value="思彤哥" v-model="picked">
<label for="deng">思彤哥label>
<br>
<span>被选中的人: {{ picked }}span>
div>
const vm = new Vue({
el: '#app',
data: {
picked: ''
}
})
<div id="app">
<p >多行文本为:{{ message }}p>
<textarea v-model="message" placeholder="添加文本">textarea>
div>
const vm = new Vue({
el: '#app',
data: {
message: ''
}
})
匹配的值为option中的汉字
<div id="app">
<select v-model="selected">
<option>Aoption>
<option>Boption>
<option>Coption>
select>
<span>选择: {{ selected === '请选择' ? '' : selected }}span>
div>
const vm = new Vue({
el: '#app',
data: {
selected: '请选择'
}
})
注意:如果 v-model 表达式的初始值未能匹配任何选项, 元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,可以提供一个值为空的禁用选项:
<div id="app">
<select v-model="selected">
<option :disabled="selected">请选择option>
<option>Aoption>
<option>Boption>
<option>Coption>
select>
<span>选择: {{ selected === '请选择' ? '' : selected }}span>
div>
绑定到一个数组
<div id="app">
<select v-model="selected" multiple>
<option>Aoption>
<option>Boption>
<option>Coption>
select>
<span>选择: {{ selected }}span>
div>
const vm = new Vue({
el: '#app',
data: {
selected: []
}
})
在默认情况下,v-model在每次input事件触发后将输入框的值与数据进行同步。如果要变为使用change事件同步可以添加lazy修饰符:
<input v-model.lazy="msg" >
自动将用户的输入值转为数值类型:
<input v-model.number="age" type="number">
自动过滤用户输入的首尾空白字符:
<input v-model.trim="msg">
有些时候,我们在模板中放入了过多的逻辑,从而导致模板过重,且难以维护。
基础用法
计算属性是Vue配置对象中的属性,使用方式如下:
<div id="app">
{{ someComputed }}
div>
const vm = new Vue({
el: '#app',
computed: {
// 返回的值,就是计算属性的值
someComputed () {
return 'some values'
}
}
})
虽然在表达式中调用方法也可以实现同样的效果,但是使用计算属性
和使用方法
有着本质的区别。
当使用方法
时,每一次页面重新渲染,对应的方法都会重新执行一次,如:
<div id="app">
<p>{{ name }}p>
<p>{{ reversedMsg() }}p>
div>
const vm = new Vue({
el: '#app',
data: {
msg: 'Hello',
name: 'shanshan'
},
methods: {
reversedMsg: function () {
console.log('方法执行啦');
return this.msg.split('').reverse().join('');
}
}
})
vm.name = 'duyi';
在上面的例子中我们可以看到,一旦更改name的值,页面会重新渲染,此刻控制台中打印出方法执行啦
这串字符串,代表着reversedMsg这个函数执行了,但是我们并不需要该方法执行,因为改动的数据和这个函数没有任何关系,如果这个函数内的逻辑很复杂,那么对于性能来讲,也是一种消耗。
但是利用计算属性
做,就不会有这样的现象出现,如:
const vm = new Vue({
el: '#app',
data: {
msg: 'Hello',
name: 'shanshan'
},
computed: {
reversedMsg: function () {
console.log('计算执行啦');
return this.msg.split('').reverse().join('');
}
}
})
vm.name = 'duyi';
此时可以看到,当给数据name重新赋值时,计算属性并没有执行。
所以,计算属性
和方法
的最本质的区别是:计算属性是基于响应式依赖进行缓存的,计算属性的值一直存于缓存中,只要它依赖的data数据不改变,每次访问计算属性,都会立刻返回缓存的结果,而不是再次执行函数。而方法则是每次触发重新渲染,调用方法将总会再次执行函数。
那么,为什么需要缓存呢?
假如说,我们有一个计算属性A,它需要遍历一个巨大的数组并且做巨大的计算。然后我们需要使用到这个计算属性A,如果没有缓存,我们就会再次执行A的函数,这样性能开销就变得很大了。
计算属性除了写成函数形式,还可以写成对象形式。对象内有两个属性getter和setter
,这两个属性均为函数。
const vm = new Vue({
el: '#app',
computed: {
keyName: {
getter () {
// 一些代码
},
setter () {
// 一些代码
}
}
}
})
在前面,我们直接将计算属性写成了一个函数,这个函数即为getter函数。也就是说,计算属性默认只有getter。
getter的this,被自动绑定为Vue实例。
何时执行?
当我们去获取某一个计算属性时,就会执行get函数。
const vm = new Vue({
el: '#app',
data: {
msg: 'Hello'
},
computed: {
reversedMsg: {
getter () {
return this.msg.split('').reverse().join('');
}
}
}
})
可选,set函数在给计算属性重新赋值时会执行。
参数:为被重新设置的值。
setter的this,被自动绑定为Vue实例。
const vm = new Vue({
el: '#app',
data: {
msg: 'Hello',
firstStr: ''
},
computed: {
reversedMsg: {
getter () {
return this.msg.split('').reverse().join('');
},
setter (newVal) {
this.firstStr = newVal[0];
}
}
}
})
注意:
即使给计算属性赋了值,计算属性也不会改变。
只有当依赖的响应式属性变化了,计算属性才会重新计算。
侦听属性,响应数据(data&computed)的变化,当数据变化时,会立刻执行对应函数
例:
const vm = new Vue({
el: '#app',
data: {
msg: 'hello,你好呀,我是杉杉',
},
watch: {
msg () {
console.log('msg的值改变啦~');
}
}
})
// 更改msg的值
vm.msg = 'hello~~~~'; // 此时会在控制台中打印出` msg的值改变啦 `
侦听器函数,会接收两个参数,第一个参数为newVal(被改变的数据),第二个参数为oldVal(赋值新值之前的值)。如在上述代码中,将侦听器watch更改一下,如:
watch: {
msg (newVal,oldVal) {
conosle.log(newVal, oldVal);
}
}
// 更改msg的值
vm.msg = 'hello~~~~'; // 此时会在控制台中打印出`hello,你好呀,我是杉杉 hello~~~~`
值(即字符串)为方法的名字,被侦听的数据改变时,会执行该方法。
const vm = new Vue({
el: '#app'
data: {
msg: '杉杉'
},
watch: {
msg: 'msgChange'
},
methods: {
msgChange () {
console.log('msg的值改变啦');
}
}
})
vm.msg = 'hello'; // 此时msgChange函数会执行,控制台中打印出 ` msg的值改变啦 `
写成对象类型时,可以提供选项,对象中有三个属性handler、deep、immediate。
必需。handler时被侦听的数据改变时执行的回调函数。
handler的值类型为函数/字符串,写成字符串时为一个方法的名字。
const vm = new Vue({
el: '#app'
data: {
msg: '杉杉'
},
watch: {
msg: {
handler () {
console.log('msg的值改变啦');
}
}
}
})
vm.msg = 'hello'; // 此时回调函数会执行,控制台中打印出 ` msg的值改变啦 `
在默认情况下,侦听器侦听对象只侦听引用的变化,只有在给对象赋值时它才能被监听到。所以需要使用deep选项,让其可以发现对象内部值的变化,将deep的值设置为true,那么无论该对象被嵌套的有多深,都会被侦听到。
const vm = new Vue({
el: '#app'
data: {
personObj: {
name: '邓旭明',
age: 88
}
},
watch: {
personObj: {
handler () {
console.log('对象的值改变啦');
},
deep: true // 开启深度侦听
}
}
})
vm.obj.name = '老邓头'; // 此时回调函数会执行,控制台中打印出 ` 对象的值改变啦 `
注意,当对象的属性较多的时候,性能开销会比较大,此时可以监听对象的某个属性,这个后面再说。
加上immediate选项后,回调将会在侦听开始之后立刻被调用。而不是等待侦听的数据更改后才会调用。
const vm = new Vue({
el: '#app'
data: {
msg: '杉杉'
},
watch: {
msg: {
handler () {
console.log('回调函数执行啦');
},
immediate: true
}
}
})
// 此时未更改msg的值,就会在控制台打印出来` 回调函数执行啦 `
可以将多种不同值类型写在一个数组中。如:
const vm = new Vue({
el: '#app'
data: {
msg: '杉杉'
},
watch: {
msg: [
'msgChange',
function () {},
{
handler () {},
deep: true,
immediate: true
}
]
}
})
以上演示的都是正常的对象key值,这里不再赘述。
当key值类型为字符串时,可以实现监听对象当中的某一个属性,如:
const vm = new Vue({
el: '#app'
data: {
personObj: {
name: '邓旭明',
age: 88
}
},
watch: {
'personObj.name' () {
console.log('对象的值改变啦');
}
}
})
vm.obj.name = '老邓头'; // 此时回调函数会执行,控制台中打印出 ` 对象的值改变啦 `
Vue实例将会在实例化时调用$watch,遍历watch对象的每一个属性。
我们也可以利用vm.$watch来实现侦听,用法与watch选项部分一致,略有不同。以下为使用方法。
// 1. 三个参数,一参为被侦听的数据;二参为数据改变时执行的回调函数;三参可选,为设置的选项对象
vm.$watch(
'msg',
function () {
// 干了点事儿
},
{
deep: Boolean,
immediate: Boolean
}
)
// 2. 二个参数,一参为被侦听的数据;二参为选项对象,其中handler属性为必需,是数据改变时执行的回调函数,其他属性可选。
vm.$watch(
'msg',
{
handler () {
// 干了点事儿
},
deep: Boolean,
immediate: Boolean
}
)
vm.$watch('obj.name', /**参数和上面一样*/)
vm.$watch(function () {
// 表达式`this.a + this.b`每次得出一个不同的结果时该函数都会被调用
// 这就像监听一个未被定义的计算属性
return this.a + this.b;
}, /**参数和上面一致*/)
侦听器函数执行后,会返回一个取消侦听函数,用来停止触发回调:
const unwatch = vm.$watch('msg', function () {});
unwatch(); // 执行后会取消侦听msg数据
使用unwatch时,需要注意
的是,在带有immediate选项时,不能在第一次回调时取消侦听数据。
//错误做法
const unwatch = vm.$watch('msg', function () {
// 干了点儿事
unwatch(); // 此时会报错
},{
immediate: true
}
})
如果仍然希望在回调内部用一个取消侦听的函数,那么可以先检查该函数的可用性:
//真确做法
var unwatch = vm.$watch('msg', function () {
// 干了点儿事
if(unwatch) {
unwatch();
}
},{
immediate: true
}
})
两者都可以观察和响应Vue实例上的数据的变动。
watch擅长处理的场景是:一个数据影响多个数据(因为一次侦听只能对某个属性或数据进行,将属性或数据名作为侦听函数名或对象名);
computed擅长处理的场景是:多个数据影响一个数据(根据数据的变化,得到新的值);
在侦听器中可以执行异步,但是在计算属性中不可以(因为:计算属性函数中必须使用return将处理的结果返回;而侦听属性不需要return。
详细解释:计算属性已经将结果返回,但此时异步处理还未进行,所以此时返回的结果不是最终结果;而watch没有return,可以将异步处理完成的结果返回。)
例:
使用侦听器:
var vm = new Vue({
el: '#app',
data: {
question: '',
},
watch: {
question () {
setTimeout(() => {
alert(this.question);
}, 1000)
}
}
})
Axios是一个基于promise的HTTP库
浏览器支持情况:Chrome、Firefox、Safari、Opera、Edge、IE8+
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
最常用的配置:
axios({
method: 'get', // post、get、put....
baseURL: '', // 请求的域名,基本地址
url: '', // 请求的路径
params: {}, // 会将请求参数拼接在url上
data: {}, // 会将请求参数放在请求体中
headers: {}, // 设置请求头,例如设置token等
timeout: 1000, // 设置请求超时时长,单位:ms
})
为方便起见,为所有支持的请求方法提供了别名。
可以指定将被用在各个请求的配置默认值
axios.defaults.baseURL = 'https://developer.duyiedu.com/vue';
axios.defaults.timeout = 1000;
在实际项目中,很少用全局配置。
可以使用自定义配置新建一个axios实例
const instance = axios.create({
baseURL: 'https://developer.duyiedu.com/vue',
timeout: 1000,
})
instance.get('/getUserInfo').then(res => {
// ...
})
const instance = axios.create();
instance.get('/getUserInfo', {
timeout: 5000
})
全局 < 实例 < 请求
同时进行多个请求,并统一处理返回值
axios.all([
axios.get('/a'),
axios.get('/b')]).then(axios.spread((aRes,bRes)=>{
console.log(aRes,bRes);
}))
interceptors,在发起请求之前做一些处理,或者在响应回来之后做一些处理。
axios.interceptors.request.use(config => {
// 在发送请求之前做些什么
return config;
})
axios.interceptors.response.use(response =>{
//对相应数据做点什么
return response;
})
const myInterceptor=axios.interceptor.request.use(config => {
....
return config;
})
axios.interceptor.request.eject(myInterceptor);
const instance = axios.create({
...
});
instance.interceptor.request.use(config => {
...
return config;
})
应用于多次跳转页面,取消前一次正在进行的http请求数据的请求,提高效率。
const source = axios.CancelToken.source();//首先
axios.get('/getUserInfo',{
cancelToken:source.token,
}).then(res => {
console.log(res);
}).catch(error => {
if(axios.isCancel(errror)){
//取消请求
}else{
//其他错误
}
})
//vue中的:(调用函数取消请求)
methods:{
handle(){
source.cancel('取消请求');
}
}
在请求错误时进行的处理
request / response 是error的上下文,标志着请求发送 / 得到响应
在错误中,如果响应有值,则说明是响应时出现了错误。
如果响应没值,则说明是请求时出现了错误。
在错误中,如果请求无值,则说明是请求未发送出去,如取消请求。
axios.get('/user/12345')
.catch(function (error) {
// 错误可能是请求错误,也可能是响应错误
if (error.response) {
// 响应错误
} else if (error.request) {
// 请求错误
} else {
console.log('Error', error.message);
}
console.log(error.config);
});
在实际开发过程中,一般在拦截器中统一添加错误处理
请求拦截器中的错误,会当请求未成功发出时执行,但是要注意的是:取消请求后,请求拦截器的错误函数也不会执行,因为取消请求不会抛出异常(只有在移除拦截器时出现异常),axios对其进行了单独的处理。
在更多的情况下,我们会在响应拦截器中处理错误。
const instance = axios.create({});
instance.interceptors.request.use(config => {
}, error => {
return Promise.reject(error);
})
instance.interceptors.response.use(response => {
}, error => {
return Promise.reject(error);
})
当axios的请求为非简单请求时,浏览器会进行预检,及发送OPTIONS请求。请求到服务器,询问是否允许跨域。如果响应中允许预检中请求的跨域行为,则浏览器会进行真正的请求。否则会报405错误。
(在vue的配置对象中,如data、el、methods、computed、watch等等,均叫为选项)
关于el
提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以是 CSS 选择器,也可以是一个 HTML 元素 实例。
如果在实例化时存在这个选项,实例将立即进入编译过程,否则,需要显式调用 vm.$mount() 手动开启编译。
template
一个字符串模板作为 Vue 实例的标识使用。模板将会 替换 挂载的元素,挂载元素的内容都将被忽略。
<div id="app">div>
const vm = new Vue({
el: '#app',
template: `
xxx
`,
})
Vue初始化到挂载的流程
每个 Vue 实例在被创建时都要经过一系列的初始化过程,例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
所有的生命周期钩子自动绑定 this 上下文到实例中,因此你可以访问数据,对属性和方法进行运算
在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
<div id="app">
<div @click="handleClick">点击事件div>
div>
const vm = new Vue({
el: '#app',
data: {
msg: 'hellow world',
},
beforeCreate () {
console.log(this.msg); // undefined
console.log(this.handleClick); // undefined
console.log('-----beforeCreate-----');
},
methods: {
handleClick () {
console.log(handleClick);
}
},
watch: {
msg: {
handler () {
console.log('侦听msg的值');
},
immediate: true,
}
}
})
打印顺序:
undefined
undefined
-----beforeCreate-----
侦听msg的值
在实例创建完成后被立即调用。
在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。
如果要在第一时间调用methods中的方法,或者操作data中的数据,可在此钩子中进行操作。
需要注意的是,执行此钩子时,挂载阶段还未开始,$el 属性目前不可见。
此时,可以进行数据请求,将请求回来的值赋值给data中的数据。
<div id="app">
<div @click="handleClick">点击事件div>
div>
const vm = new Vue({
el: '#app',
data: {
msg: 'hellow world',
},
created () {
console.log(this.msg); // hello world
console.log(this.handleClick); // function () {...}
console.log(this.$el); // undefined
console.log('----------created-------');
},
methods: {
handleClick () {
console.log(handleClick);
}
},
watch: {
msg: {
handler () {
console.log('侦听msg的值');
},
immediate: true,
}
}
})
打印顺序:
侦听msg的值
hellow world
ƒ handleClick () { console.log(handleClick); }
undefined
----------created-------
在挂载开始之前被调用,此时模板已经编译完成,只是未将生成的模板替换el对应的元素。
在此钩子函数中,可以获取到模板最初始的状态。
此时,可以拿到vm.$el,只不过为旧模板
const vm = new Vue({
el: '#app',
beforeMount () {
console.log(this.$el);
}
})
el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。
在该钩子函数中的vm.$el为新模板。
执行完该钩子函数后,代表实例已经被完全创建好。
如果要在第一时间,操作页面上的dom节点时,可以在此钩子函数中操作
const vm = new Vue({
el: '#app',
mounted () {
console.log(this.$el);
}
})
数据更新时调用,发生在虚拟 DOM 打补丁之前。此时数据已经更新,但是DOM还未更新
<div id="app">
{{ msg }}
div>
const vm = new Vue({
el: '#app',
data: {
msg: 'hellow world',
},
beforeUpdate () {
console.log(this.msg);
console.log(this.$el);//这里会打印出更新后的,因为this.$el是引用类型,这里要看更新前的,要写成this.$el.outHTML
},
methods: {
handleClick () {
console.log('handleClick');
}
}
})
this.msg = 'xxx';
数据更改导致DOM重新渲染后,会执行该钩子函数。
此时数据和dom同步。
实例销毁之前调用。在这一步,实例仍然完全可用。
可以在该钩子函数中,清除定时器。
<div id="app">
{{ msg }}
div>
const vm = new Vue({
el: '#app',
data: {
msg: 'hellow world',
timer: 0,
},
created () {
this.timer = setInterval(() => {
console.log('xxx');
}, 500)
},
beforeDestroy () {
clearInterval(this.timer);
}
})
Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除。
未完待续。。。