Object.freeze()
<template>
<div class="hello">
{{msg.name}}
<input type="text" :value="msg.name" @change="(event) => {msg.name = event.target.value}">
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data() {
let a = {name:123}
Object.freeze(a)
return {
msg:a
}
},
methods: {}
}
</script>
[Vue warn]: Error in v-on handler: "TypeError: Cannot assign to read only property 'name' of object '#<Object>'"
计算属性
计算属性与方法
计算属性是基于他们响应式依赖进行缓存的,只有相关响应式依赖发生变化时他们才会重新求值,方法每次都需要调用
计算属性与监听方法
当某个变量是由多个变量响应式依赖的话,使用watch会造成代码的冗余;
修饰符
-
handler(str) { console.log(str) },
-
.stop: 阻止冒泡,即当子标签存在点击事件,父标签也存在点击事件,当点击子标签的时候,父标签的点击事件也会触发,因此使用.stop来阻止冒泡行为
<!-- 使用 .stop 阻止冒泡 --> <div class="inner" @click="handler('div')"> <input type="button" value="戳他" @click.stop="handler('btn')"> </div>
-
.prevent 阻止默认行为,即如下例子,添加了.prevent修饰符,,会阻止页面跳转,使a标签失效
<!-- 使用 .prevent 阻止默认行为 --> <a href="http://www.baidu.com" @click.prevent="linkClick">有问题,先去百度</a>
-
.capture
<!-- 使用 .capture 实现捕获触发事件的机制 --> <div @click.capture="handler('div1')"> <div class="inner" @click.capture="handler('div2')"> <input type="button" value="戳他" @click="handler('btn')"/> </div> </div> <!-- 不加.captrue时,结果为 btn, div2, div1 加了.captrue时,结果为 div2 , btn , div1 -->
-
.self
<!-- 使用 .self 实现只有点击当前元素时候,才会触发事件处理函数 --> <div class="inner" @click.self="handler('div1')"> <input type="button" value="戳他" @click="handler('btn')"> </div> <!-- 不加.self时,结果为: btn, btn1 加了.self时,结果为: btn -->
-
.once
<!-- 使用 .once 只触发一次事件处理函数 --> <a href="http://www.baidu.com" @click.prevent.once="linkClick">有问题,先去百度</a> <!-- 使.prevent修饰符,只成功一次,第二次a标签的默认动作,就会恢复 -->
-
.passive
<!-- passive这个修饰符会执行默认方法。你们可能会问,明明默认执行为什么会设置这样一个修饰符。这就要说一下这个修饰符的本意了。 【浏览器只有等内核线程执行到事件监听器对应的JavaScript代码时,才能知道内部是否会调用preventDefault函数来阻止事件的默认行为,所以浏览器本身是没有办法对这种场景进行优化的。这种场景下,用户的手势事件无法快速产生,会导致页面无法快速执行滑动逻辑,从而让用户感觉到页面卡顿。】 通俗点说就是每次事件产生,浏览器都会去查询一下是否有preventDefault阻止该次事件的默认动作。我们加上passive就是为了告诉浏览器,不用查询了,我们没用preventDefault阻止默认动作。 这里一般用在滚动监听,@scoll,@touchmove 。因为滚动监听过程中,移动每个像素都会产生一次事件,每次都使用内核线程查询prevent会使滑动卡顿。我们通过passive将内核线程查询跳过,可以大大提升滑动的流畅度 passive和prevent冲突,不能同时绑定在一个监听器上 -->
-
.sync
修饰符
data
必须是一个函数
一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:
非Prop的Attribute
Attribute继承
当组件返回的单个根节点时,非prop attribute 将自动添加到根节点的attribute中
app.component('date-picker', {
template: `
<div class="date-picker">
<input type="datetime" />
</div>
`
})
<date-picker data-status="activated"></date-picker>
// 渲染
<div class="date-picker" data-status="activated">
<input type="datetime" />
</div>
同样的规则使用于事件监听器
<date-select @change="showChange"></date-select>
let app = Vue.createApp({
methods: {
showChange(event) {
console.log("event", event)
console.log(event.target.value)
}
}
})
app.component('date-select', {
template: `
<select>
<option value="1">Yesterday</option>
<option value="2">Today</option>
<option value="3">Tomorrow</option>
</select>
`
})
禁用Attribute继承
app.component('date-picker', {
inheritAttrs: false,
template: `
<div class="date-picker">
<input type="datetime" v-bind="$attrs" />
</div>
`
})
<date-picker data-status="activated"></date-picker>
// 渲染
<div class="date-picker">
<input type="datetime" data-status="activated" />
</div>
多个根节点上的Attribute继承
与单个根节点组件不同,具有多个根节点的组件不具有自动attribute回退行为。如果未显式绑定$attrs,将发生运行时警告。
<custom-layout id="custom-layout" @click="changeValue"></custom-layout>
// 警告
app.component('custom-layout', {
template: `
<div class="one">one</div>
<div class="two">two</div>
<div class="three">three</div>
`
})
// 没有警告,$attrs被传递到two元素
app.component('custom-layout', {
template: `
<div class="one">one</div>
<div class="two" v-bind="$attrs">two</div>
<div class="three">three</div>
`
})
将原生事件绑定到组件
在组件上使用事件的时候,事件会默认绑定在组件内部最外城的根标签上面;
解决方案:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app">
<hello @change="handleChange"></hello>
</div>
<script>
Vue.component('hello', {
data: function () {
return {
msg: ''
}
},
template: '<div>{{msg}}<input type="text" v-model="msg" v-on="inputListeners"></div>',
computed: {
inputListeners: function () {
var vm = this
// `Object.assign` 将所有的对象合并为一个新对象
return Object.assign({},
// 我们从父级添加所有的监听器
this.$listeners,
// 然后我们添加自定义监听器,
// 或覆写一些监听器的行为
{
// 这里确保组件配合 `v-model` 的工作
input: function (event) {
vm.$emit('input', event.target.value)
}
}
)
}
}
})
var app = new Vue({
el: '#app',
methods: {
handleChange() {
console.log(123)
}
}
})
</script>
</body>
</html>
keep-alive
每次切换,组件不会重置
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app">
<button @click="handleClick">切换</button>
```{{msg}}````
<keep-alive>
<component :is="com"></component>
</keep-alive>
</div>
<script>
Vue.component('hello', {
data: function () {
return {
msg: ''
}
},
template: '<div>{{msg}}<input type="text" v-model="msg" @change="handleChange"></div>',
methods: {
handleChange() {
this.$emit('heng',this.msg)
}
}
})
Vue.component('hello2', {
data: function () {
return {
msg: ''
}
},
template: '<div>{{123}}</div>',
methods: {
handleChange() {
this.$emit('heng',this.msg)
}
}
})
var app = new Vue({
el: '#app',
data: {
msg:'123',
com:'hello'
},
methods: {
handleChange() {
console.log(123)
},
handleClick() {
this.com = this.com == 'hello' ?'hello2':'hello'
}
}
})
</script>
</body>
</html>
依赖注入
父传孙子的便捷方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app">
<button @click="handleClick">切换</button>
```{{msg}}````
<keep-alive>
<component :is="com"></component>
</keep-alive>
</div>
<script>
Vue.component('hello', {
data: function () {
return {
msg: ''
}
},
template: '<div><hello2></hello2><input type="text" v-model="msg" @change="handleChange"></div>',
methods: {
handleChange() {
this.$emit('heng',this.msg)
}
}
})
Vue.component('hello2', {
data: function () {
return {
msg: ''
}
},
template: '<div>我是hello2{{test}}</div>',
methods: {
handleChange() {
this.$emit('heng',this.msg)
}
},
inject:['test']
})
var app = new Vue({
el: '#app',
data: {
msg:'123',
com:'hello'
},
methods: {
handleChange() {
console.log(123)
},
handleClick() {
this.com = this.com == 'hello' ?'hello2':'hello'
}
},
provide: function() {
return {
test:'--我是provide传递过来的--'
}
}
})
</script>
</body>
</html>
强制更新
过滤
{{ msg | capitalize}}
filters: {
capitalize: function (value) {
if (value == '123') return '我被锅炉了'
return value
}
}