Vue的data和计算属性的疑惑

刚接触vue不久,对里面的data和计算属性的使用有点疑惑,在跟着某视频学习Vue的过程中,产生了疑问。

想实现这样的效果

代码如下

App.vue的代码

<template>
    <div id="app">
        <topbar class="topbar"></topbar>
        <main>
            <editor :resume="resume" class="editor" />
            <preview :resume="resume" class="preview" />
        </main>
    </div>
</template>


<script>
import Topbar from './components/Topbar'
import Editor from './components/Editor'
import Preview from './components/Preview'
export default {
    components: {
        Topbar,
        Editor,
        Preview

    },
    data:function(){
       return {
           resume:{
               profile: {
                name: '',
                city: '',
                birth: ''
            },
            careers: [
                { company: '', content: '' }
            ],
            labelsForCareers: [
                '公司', '工作内容'
            ],
            titleForCareers: '工作经历',
            educationHistory: [
                { school: '', duration: '', degree: '' }
            ],
            labelsForEducationHistory: [
                '学校', '时长', '学位'
            ],
            titleForEducationHistory: '教育背景',
            projects: [
                { project: '', content: '' }
            ],
            labelsForProjects: [
                '项目名称', '具体描述'
            ],
            titleForProjects: '项目经历',
            awards: [{
                name: ''
            }],
            labelsForAwards: ['获奖名称'],
            titleForAwards: '所获奖项',
            contacts:{
                qq:'',
                wx:'',
                phone:'',
                github:'',
                email:''
            }
        }
           }
       } 
    }

</script>

然后是Editor.vue

<template>
    <div id="editor">
        <nav>
            <ol>
                <li v-for="(item,index) in items" :class="{active:currentTab === index }" @click="currentTab = index" :key="index">
                    <svg class="icon">
                        <use :xlink:href="item"></use>
                    </svg>
                </li>
             </ol>
        </nav>
        <ol class="panes">
            <!-- li{tab $} *6 -->
            <li v-if="currentTab === 0">
                <profile-editor :profile="resume.profile"></profile-editor>
            </li>
            <li v-if="currentTab === 1">
                <common-editor :items="resume.careers" :labels="resume.labelsForCareers" :title="resume.titleForCareers"></common-editor>
            </li>
            <li v-if="currentTab === 2">
                <common-editor :items="resume.educationHistory" :labels="resume.labelsForEducationHistory" :title="resume.titleForEducationHistory"></common-editor>
            </li>
            <li v-if="currentTab === 3">
                <common-editor :items="resume.projects" :labels="resume.labelsForProjects" :title="resume.titleForProjects"></common-editor>
            </li>
            <li v-if="currentTab === 4">
                <common-editor :items="resume.awards" :labels="resume.labelsForAwards" :title="resume.titleForAwards"></common-editor>
            </li>
            <li v-if="currentTab === 5">
                <h2> 个人信息</h2>
                <el-form label-position="top" label-width="80px">
                    <el-form-item label="QQ">
                        <el-input v-model="resume.contacts.qq"></el-input>
                    </el-form-item>
                    <el-form-item label="WeChat">
                        <el-input v-model="resume.contacts.wx"></el-input>
                    </el-form-item>
                    <el-form-item label="Email">
                        <el-input v-model="resume.contacts.email"></el-input>
                    </el-form-item>
                    <el-form-item label="Phone">
                        <el-input v-model="resume.contacts.phone"></el-input>
                    </el-form-item>
                    <el-form-item label="Github">
                        <el-input v-model="resume.contacts.github"></el-input>
                    </el-form-item>
                </el-form>
            </li>
        </ol>
    </div>
</template>
<script>
import ProfileEditor from './ProfileEditor'
import CommonEditor from './CommonEditor'
export default {
    props:['resume'],
    components: { ProfileEditor, CommonEditor },
    data: function() {
        return {
            currentTab: 0,
            items: ["#icon-card", "#icon-handbag", "#icon-book", "#icon-heart", "#icon-cup", "#icon-phone"],
            contents: ["Tab1", "Tab2", "Tab3", "Tab4", "Tab5", "Tab6"],
            }
        }
    }
</script>

最后是CommonEditor.vue

 <template>
    <div>
        <h2>{{title}}</h2>
        <el-form>
            <div v-for="(item,index) in copyItems" :key="index" class="container">
                <el-form-item :label="labels[index]" v-for="(key,index) in keys" :key="index">
                    <el-input v-model="item[key]"></el-input>
                </el-form-item>
                <i class="el-icon-close" @click="remove(index)"></i>
            </div>
            <el-button type="primary" @click="add">添加</el-button>
        </el-form>
    </div>
</template>


<script>
export default {
    props: ['items', 'labels', 'title'],
    computed: {
        keys: function() {
            return Object.keys(this.items[0])
        },
        // copyItems: function() {
        //     return this.items.slice(0)
        // }
    },
    data: function() {
        return {
            copyItems: this.items.slice(0)
        }
    },
    methods: {
        add: function() {
            let temp = {}
            this.keys.map((key) => {
                temp[key] = ''
            })
            this.copyItems.push(temp)
        },
        remove: function(index) {
            this.copyItems.splice(index, 1)
        }
    }
}
</script>

问题来了

  • 上面的代码存在严重问题,我在工作经历这一项点击添加按钮,却发现此时关系到CommonEditor.vue的部分也增加了。效果如下:

我明明是在“工作经历”里面点击的添加,为什么当我切换到“所获奖项”这部分的时候,里面也变为了两项?为什么似乎这几个部分的copyItems是同一个copyItems?

  • 然后我试了不用data而使用computed属性,即:
    computed: {
        keys: function() {
            return Object.keys(this.items[0])
        },
        copyItems: function() {
            return this.items.slice(0)
        }
    },
    // data: function() {
    //     return {
    //         copyItems: this.items.slice(0)
    //     }
    // },

这下两个按钮(添加和那个x按钮)点击都没反应了。我想问是不是computed属性里面的响应式数据的改变而引起的视图的更新所只与他们依赖的东西有关?以上面的例子为例,copyItems是不是就只与this.items有关?因为我通过console.log发现,点击“添加”按钮之后,add函数执行了,copyItems确实发生了变化,但是视图没有发生相应的变化。这是为什么呢?

在这两个问题上卡了很久了,希望有人能解答。

阅读 5.1k
4 个回答

解决办法,给下面这些结构相同添加key属性

<li v-if="currentTab === 2">
    <common-editor key="2"></common-editor>
</li>
<li v-if="currentTab === 3">
    <common-editor key="3"></common-editor>
</li>

这种切换情况下,结构属性完全相同的标签,vue会自动复用,加key可以指明他们是不同的,阻止复用。
commonEditor.vue里的这里

<el-form-item :label="labels[index]" v-for="(key,index) in keys" :key="index">
    <el-input v-model="item[key]"></el-input>
    {{item}}
</el-form-item>

加上{{item}},在工作经历里点完添加,切换至所获奖项,你也能看到,所渲染出来的item是

...
{ "name": "" }// 这是所获奖项里的值
...
{ "company": "", "content": "" }// 这是工作经历里的值
...

也是组件复用的结果

参考链接

有相同父元素的子元素必须有独特的 key

视频地址发一下啊。

你在子组件里面不能操作父组件的data

新手上路,请多包涵

视频可以发下

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题