vue 修饰符sync的功能是:当一个子组件改变了一个 prop 的值时,这个变化也会同步到父组件中所绑定。如果我们不用.sync,我们想做上面的那个弹窗功能,我们也可以props传初始值,然后事件监听,实现起来也不算复杂。这里用sync实现,只是给大家提供一个思路,让其明白他的实现原理,可能有其它复杂的功能适用sync。
这只是一个简单的例子,看完这个不知你是不觉得有个指令跟这个很相似,v-model?对,就是v-model在组件上使用的时候。
这样会把 doc 对象中的每一个 property (如 title) 都作为一个独立的 prop 传进去,然后各自添加用于更新的 v-on 监听器。
在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以变更父组件,且在父组件和子组件两侧都没有明显的变更来源。
这也是为什么我们推荐以 update:myPropName 的模式触发事件取而代之。举个例子,在一个包含 title prop 的假设的组件中,我们可以用以下方法表达对其赋新值的意图:
vue的实例属性和方法
除了数据属性,Vue 实例还暴露了一些有用的实例属性与方法。它们都有前缀 $,以便与用户定义的属性区分开来。
ref 属性
1、ref 加在普通的元素上,用this.ref.name 获取到的是dom元素
2、ref 加在子组件上,用this.ref.name 获取到的是组件实例,可以使用组件的所有方法。
3、如何利用 v-for 和 ref 获取一组数组或者dom 节点
this.$refs 获取了所有设置ref属性的元素(如有重名那么会被覆盖)
如果是普通的元素那返回的是一个普通的dom,如果ref放在vue组件里,那么返回的是一个vue对象,这个对象包含了这个组件的各种信息
<template>
<div>
<NotepadHeader/>
<div class="empty" v-show="!isShow">
<div class="no-data">
<img src="@/assets/no-data.png" class="no-data-img"/>
<span class="no-data-text">暂无数据</span>
</div>
</div>
<div class="notepad-list" v-show="isShow">
<div v-for="(note,index) of this.$store.state.notepadList" v-bind:key="index" class="notepad-list-item">
<div class="notepad-list-item-content">{{note.thing}}<button @click="delItem(note.id)" style="float:right ">del</button></div>
<div class="notepad-list-item-label">
{{note.address}} {{note.time}}
</div>
</div>
</div>
<div class="add-item" @click="addItem">
<img class="add-item-icon" src="@/assets/add.png" style="border-radius: 50%"/>
</div>
<NotepadFooter/>
</div>
</template>
<script>
import NotepadHeader from '@/components/NotepadHeader'
import NotepadFooter from '@/components/NotepadFooter'
export default {
name: 'StartPage',
components: {
NotepadHeader,
NotepadFooter
},
data () {
return {
isShow: this.$store.state.notepadList.length > 0
}
},
methods: {
addItem () {
this.$router.push('/add')
},
delItem (id) {
for (let i = 0; i < this.$store.state.notepadList.length; i++) {
if (this.$store.state.notepadList[i].id === id) {
console.log(i)
this.$store.state.notepadList.splice(i, 1)
}
}
},
editItem (id) {
for (let i = 0; i < this.$store.state.notepadList.length; i++) {
if (this.$store.state.notepadList[i].id === id) {
console.log(i)
this.$store.state.notepadList.splice(i, 1)
}
}
}
},
watch: {
}
}
</script>
<style scoped>
.no-data {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.no-data-img {
display: block;
margin-top: 30px;
width: 50%;
}
.no-data-text {
margin-top: 10px;
font-size: 20px;
color: #999;
}
.add-item {
z-index: 100;
width: 60px;
height: 60px;
position: absolute;
bottom: 30px;
left: 50%;
transform: translate(-50%);
}
.add-item-icon {
display: block;
width: 100%;
height: 100%;
}
.notepad-list-item{
height: 100px;
background: #eee;
margin: 10px 20px;
}
.notepad-list-item-content{
position: relative;
left: 10px;
top: 10px;
color: #222 ;
}
.notepad-list-item-label{
position: relative;
left: 50px;
top: 50px;
color: #aaa;
}
</style>
<template>
<div>
<NotepadHeader/>
<div class="empty" v-show="!isShow">
<div class="no-data">
<img src="@/assets/no-data.png" class="no-data-img"/>
<span class="no-data-text">暂无数据</span>
</div>
</div>
<div class="notepad-list" v-show="isShow">
<div v-for="(note,index) of this.$store.state.notepadList" v-bind:key="index" class="notepad-list-item">
<div class="notepad-list-item-content">{{note.thing}}<button @click="delItem(note.id)" style="float:right ">del</button></div>
<div class="notepad-list-item-label">
{{note.address}} {{note.time}}
</div>
</div>
</div>
<div class="add-item" @click="addItem">
<img class="add-item-icon" src="@/assets/add.png" style="border-radius: 50%"/>
</div>
<NotepadFooter/>
</div>
</template>
<script>
import NotepadHeader from '@/components/NotepadHeader'
import NotepadFooter from '@/components/NotepadFooter'
export default {
name: 'StartPage',
components: {
NotepadHeader,
NotepadFooter
},
data () {
return {
isShow: this.$store.state.notepadList.length > 0
}
},
methods: {
addItem () {
this.$router.push('/add')
},
delItem (id) {
for (let i = 0; i < this.$store.state.notepadList.length; i++) {
if (this.$store.state.notepadList[i].id === id) {
console.log(i)
this.$store.state.notepadList.splice(i, 1)
}
}
},
editItem (id) {
for (let i = 0; i < this.$store.state.notepadList.length; i++) {
if (this.$store.state.notepadList[i].id === id) {
console.log(i)
this.$store.state.notepadList.splice(i, 1)
}
}
}
},
watch: {
}
}
</script>
<style scoped>
.no-data {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.no-data-img {
display: block;
margin-top: 30px;
width: 50%;
}
.no-data-text {
margin-top: 10px;
font-size: 20px;
color: #999;
}
.add-item {
z-index: 100;
width: 60px;
height: 60px;
position: absolute;
bottom: 30px;
left: 50%;
transform: translate(-50%);
}
.add-item-icon {
display: block;
width: 100%;
height: 100%;
}
.notepad-list-item{
height: 100px;
background: #eee;
margin: 10px 20px;
}
.notepad-list-item-content{
position: relative;
left: 10px;
top: 10px;
color: #222 ;
}
.notepad-list-item-label{
position: relative;
left: 50px;
top: 50px;
color: #aaa;
}
</style>
NotepadFooter.vue
<template>
<div class="footer">
</div>
</template>
<script>
export default {
name: 'NotepadFooter'
}
</script>
<style scoped>
.footer {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 60px;
background-image: linear-gradient(45deg, #6699ff, #00cc99);
}
</style>
NotepadHeader.vue
<template>
<div class="header">
<span class="title">记事本</span>
</div>
</template>
<script>
export default {
name: 'NotepadHeader'
}
</script>
<style scoped>
.header {
width: 100%;
height: 80px;
background-image: linear-gradient(45deg, #6699ff, #00cc99);
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
.title {
color: #fff;
font-size: 26px;
}
</style>