Vue.js笔记(一) 基础部分
数据方法
数据加载
<div id="app">
<span>{{a}}--{{b}}</span>
</div>
<script type="text/javascript">
var data = {a:1};
var app = new Vue({
el:'#app',
data:data
});
data.a = "hello!";
data.b = 2;
</script>
发现报错a改为hello成功了,但是b报错了
如果换个顺序
<div id="app">
<span>{{a}}--{{b}}</span>
</div>
<script type="text/javascript">
var data = {a:1};
data.a = "hello!";
data.b = 2;
var app = new Vue({
el:'#app',
data:data
});
</script>
发现没有问题
于是可以发现页面数据的加载是在执行new Vue的时候,如果在new Vue的时候没有相应的属性就会报错。
Object.freezze()
如果在new Vue之前对数据使用了Object.freeze()
方法,这会阻止修改现有的属性,也意味着响应系统无法再追踪变化。
<div id="app">
<span>{{a}}</span>
</div>
<script type="text/javascript">
var data = {a:1};
Object.freeze(data);
var app = new Vue({
el:'#app',
data:data
});
data.a = "hello!";
</script>
无论怎么修改a的值,页面都不会变
有趣的是,如果把Object.freeze方法在new Vue之后执行,就失效了
<div id="app">
<span>{{a}}</span>
</div>
<script type="text/javascript">
var data = {a:1};
var app = new Vue({
el:'#app',
data:data
});
Object.freeze(data);
data.a = "hello!";
</script>
$watch方法
app.$watch('a',function(newVal,oldVal){
...
})
这个方法会在app(一个Vue实例)中的a变量被改变时被调用
举个例子
<div id="app">
<span>{{a}}</span>
</div>
<script type="text/javascript">
var data = {a:'hello!'};
var app = new Vue({
el:'#app',
data:data
});
app.$watch('a',function(newVal,oldVal){
console.log(newVal+"<--"+oldVal);
})
</script>
生命周期
模板语法
Mustache
数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值
<span>Message: {{ msg }}</span>
Mustache 标签将会被替代为对应数据对象上 msg
属性的值。无论何时,绑定的数据对象上 msg
属性发生了改变,插值处的内容都会更新。
通过使用 v-once 指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定:
<span v-once>这个将不会改变: {{ msg }}</span>
v-html
双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html
指令:
<div id="app">
<p>Using mustaches: {{ rawHtml }}</p>
<span>Using v-html directive: <span v-html="rawHtml"></span></span>
</div>
<script type="text/javascript">
var app = new Vue({
el: '#app',
data: {
rawHtml: '<span style="color:red">This should be red</span>'
}
});
</script>
你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。
v-bind
关于更多绑定:https://cn.vuejs.org/v2/guide/class-and-style.html
v-bind:属性=""
例子
<!-- 完整语法 -->
<a v-bind:href="url">...</a>
<!-- 缩写 -->
<a :href="url">...</a>
可以用于绑定class、href等
<div id="app">
<div v-bind:class="color">颜色</div>
</div>
<script type="text/javascript">
var app = new Vue({
el: '#app',
data: {
color:'red'
}
});
</script>
发现class会自动绑定到VUE对象中data的'color'属性
如果我们修改
就会发现发生了变化
这应该是VUE中DOM操作的一个方式
JavaScript表达式
迄今为止,在我们的模板中,我们一直都只绑定简单的属性键值。但实际上,对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
v-if
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。
这里,v-if
指令将根据表达式 seen
的值的真假来插入/移除 <p>
元素
<p v-if="seen">现在你看到我了</p>
此外,v-else-if
,顾名思义,充当 v-if
的“else-if 块”,可以连续使用:
比如以下代码会根据type来进行显示
<body>
<div id="vm">
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#vm',
data:{
type:'A'
}
})
</script>
</body>
v-show
另一个用于根据条件展示元素的选项是 v-show
指令。用法大致一样:
<h1 v-show="ok">Hello!</h1>
不同的是带有 v-show
的元素始终会被渲染并保留在 DOM 中。v-show
只是简单地切换元素的 CSS 属性 display
。
比如说上面的例子,当ok=false的情况下:
v-show vs v-if
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。相比之下,
v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。一般来说,
v-if
有更高的切换开销,而v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用v-show
较好;如果在运行时条件很少改变,则使用v-if
较好。
v-for
主要用于列表渲染
我们可以用 v-for
指令基于一个数组来渲染一个列表。v-for
指令需要使用 item in items
形式的特殊语法,其中 items
是源数据数组,而 item
则是被迭代的数组元素的别名。
简单的例子
-
例一:对数组进行使用
出来的值为数组项
<body> <div id="app"> <ul> <li v-for="(item, index) in items"> {{item.message}} - {{index}} </li> </ul> </div> <script type="text/javascript"> var vm = new Vue({ el: '#app', data: { items:[ {message:'foo'}, {message:'bar'} ] }, }); </script> </body>
其中index参数为可选,表示索引
-
例二:对对象进行使用
出来的值为对象的value
有三种形式
<!--只有一个参数为值--> <div v-for="valuein object"> {{ value }} </div> <!-- 第二个参数为键名 --> <div v-for="(value, name) in object"> {{ name }}: {{ value }} </div> <!-- 第三个参数为索引 --> <div v-for="(value, name,index) in object"> {{index}} - {{ name }}: {{ value }} </div>
下面举个例子
<body> <div id="app"> <ul> <li v-for="(value, key , index) in object"> {{index}} : {{key}} - {{value}} </li> </ul> </div> <script type="text/javascript"> var vm = new Vue({ el: '#app', data: { object: { title: 'How to do lists in Vue', author: 'Jane Doe', publishedAt: '2016-04-10' } }, }); </script> </body>
关于更新
建议尽可能在使用
v-for
时提供key
attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
变异方法
VUE 将被侦听的数组的变异方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
你可以打开控制台,然后对前面例子的 items
数组尝试调用变异方法。比如 example1.items.push({ message: 'Baz' })
。
数组更新注意事项
由于 JavaScript 的限制,Vue 不能检测以下数组的变动:
- 当你利用索引直接设置一个数组项时,例如:
vm.items[indexOfItem] = newValue
- 当你修改数组的长度时,例如:
vm.items.length = newLength
举个例子:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue
相同的效果,同时也将在响应式系统内触发状态更新:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
你也可以使用 vm.$set
实例方法,该方法是全局方法 Vue.set
的一个别名:
vm.$set(vm.items, indexOfItem, newValue)
为了解决第二类问题,你可以使用 splice
:
vm.items.splice(newLength)
对象变更注意事项
还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:
var vm = new Vue({
data: {
a: 1
}
})
// `vm.a` 现在是响应式的
vm.b = 2
// `vm.b` 不是响应式的
对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, value)
方法向嵌套对象添加响应式属性。例如,对于:
var vm = new Vue({
data: {
userProfile: {
name: 'Anika'
}
}
})
你可以添加一个新的 age
属性到嵌套的 userProfile
对象:
Vue.set(vm.userProfile, 'age', 27)
你还可以使用 vm.$set
实例方法,它只是全局 Vue.set
的别名:
vm.$set(vm.userProfile, 'age', 27)
有时你可能需要为已有对象赋值多个新属性,比如使用 Object.assign()
或 _.extend()
。在这种情况下,你应该用两个对象的属性创建一个新的对象。所以,如果你想添加新的响应式属性,不要像这样:
Object.assign(vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
你应该这样做:
vm.userProfile = Object.assign({}, vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
关于
Object.assign()
方法:https://www.jianshu.com/p/d5f572dd3776
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
显示过滤/重排结结果
-
第一个方法是用computed属性
<li v-for="n in evenNumbers">{{ n }}</li> data: { numbers: [ 1, 2, 3, 4, 5 ] }, computed: { evenNumbers: function () { return this.numbers.filter(function (number) { return number % 2 === 0 }) } }
-
在计算属性不适用的情况下 (例如,在嵌套
v-for
循环中) 你可以使用一个方法<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-on
https://cn.vuejs.org/v2/guide/events.html
可以用 v-on
指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。
-
监听事件
<div id="example-1"> <button v-on:click="counter += 1">Add 1</button> <p>The button above has been clicked {{ counter }} times.</p> </div> var example1 = new Vue({ el: '#example-1', data: { counter: 0 } })
-
用函数形式
<div id="example-2"> <!-- `greet` 是在下面定义的方法名 --> <button v-on:click="greet">Greet</button> </div> var example2 = new Vue({ el: '#example-2', data: { name: 'Vue.js' }, // 在 `methods` 对象中定义方法 methods: { greet: function (event) { // `this` 在方法里指向当前 Vue 实例 alert('Hello ' + this.name + '!') // `event` 是原生 DOM 事件 if (event) { alert(event.target.tagName) } } } })
-
内联处理器
<div id="example-3"> <button v-on:click="say('hi')">Say hi</button> <button v-on:click="say('what')">Say what</button> </div> new Vue({ el: '#example-3', methods: { say: function (message) { alert(message) } } })
-
按键修饰符
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` --> <input v-on:keyup.enter="submit">
为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名:
.enter
.tab
.delete
(捕获“删除”和“退格”键).esc
.space
.up
.down
.left
.right
v-model
https://cn.vuejs.org/v2/guide/forms.html
计算属性
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
我们刚刚有这样一个例子
在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message
的翻转字符串。当你想要在模板中多次引用此处的翻转字符串时,就会更加难以处理。
所以,对于任何复杂逻辑,你都应当使用计算属性。
例子
<div id="app">
<p>Reversed message: {{reversedMessage}}</p>
</div>
<script type="text/javascript">
var app = new Vue({
el: '#app',
data: {
message: 'hello vue',
},
computed: {
reversedMessage() {
return this.message.split('').reverse().join('');
}
},
});
</script>
这里我们声明了一个计算属性 reversedMessage
。我们提供的函数将用作属性 vm.reversedMessage
的 getter 函数:
计算属性vs方法
如果把组件中的computed改成
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
并把html改为
<p>Reversed message: "{{ reversedMessage() }}"</p>
我们发现效果是一样的
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message
还没有发生改变,多次访问 reversedMessage
计算属性会立即返回之前的计算结果,而不必再次执行函数。
通过下面例子就可以很直观看出来了
-
使用计算属性
<body> <div id="app"> <p v-once>Time: {{now}}</p> <p v-if="showTime">Time :{{now}}</p> <button @click="click">show</button> </div> <script type="text/javascript"> var app = new Vue({ el: '#app', data: { showTime: false, }, computed: { reversedMessage() { return this.message.split('').reverse().join(''); }, now:function(){ return new Date().toLocaleString(); } }, methods: { click:function(){ this.showTime = this.showTime?false:true; } }, }); </script> </body>
点击show后
不管怎么点时间都不会刷新
说明computed是把now计算出来,并保存在缓存中,之后如果还要,直接拿来用。
-
使用方法
<body> <div id="app"> <p v-once>Time: {{now()}}</p> <p v-if="showTime">Time :{{now()}}</p> <button @click="click">show</button> </div> <script type="text/javascript"> var app = new Vue({ el: '#app', data: { showTime: false, }, computed: { reversedMessage() { return this.message.split('').reverse().join(''); }, }, methods: { now:function(){ return new Date().toLocaleString(); }, click:function(){ this.showTime = this.showTime?false:true; } }, }); </script> </body>
点击show后
每次会重新调用now方法,计算出当前时间
也就是每当触发重新渲染时,调用方法将总会再次执行函数。
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
也会相应地被更新。