[TOC] >[success] # 通过案例了解key ~~~ 1.本文案例是来自极客时间vue视频课,尊重原创,欢迎大家可以去购买这个 课真的是让我受益匪浅 2.通过代码对上节文章中的场景说明 ~~~ >[info] ## 下面案例准备工作 ~~~ 1.下面的案例是通过组件的方式,来更加形象的说明刚才的图解,在上个章节已经说明了组价和dom其实是类似 的,这里选择组件的原因主要是为了可以看到生命周期更加形象的说明问题 ~~~ >[danger] ##### 下面的组件说明 ~~~ 1.下面组件中内部统一写法其实都是一样 2.当然里面打印的内容是根据组件名称来定义的例如下面案例对应其实是组件'<ChildrenC>' 3.只有组件B和其他组略微不同 4.下面代码中的'init' 和'orderAsc' 都是在data中声明的boolean值 ~~~ ~~~ <template> <div class="border1"> <h2>C 结点</h2> <slot></slot> </div> </template> <script> export default { mounted() { console.log("C mounted"); }, updated() { console.log("C updated"); }, destroyed() { console.log("C destroyed"); } }; </script> ~~~ * 特殊的组件B ~~~ <template> <div class="border1"> <h2>B 结点 {{ number }}</h2> <slot></slot> </div> </template> <script> export default { props: { number: Number }, mounted() { console.log(`B${this.number || ""} mounted`); }, updated() { console.log(`B${this.number || ""} updated`); }, destroyed() { console.log(`B${this.number || ""} destroyed`); } }; </script> ~~~ >[info] ## 正式开始案例 ~~~ 1.下面的案例都是缩写,如果想自己也实现一个可以根据上面的组件说明中的写法自己进行反推 2.下面的案例都是根据上个章节中个个图解的代码说明 ~~~ >[danger] ##### 第一场景案例 ![](https://box.kancloud.cn/319723b150ceac6b3be2b69f31b1dc6b_1228x517.png) ~~~ 1.下面案例当点击按钮的时候B,C,D 三个组件(组件和dom的方式其实是一样 的)会相互移动,下面案例的三个组件相当于场景一,三个不同类型dom元素 ,并且在上个组件中的生命周期加上console.log() 可以直观的看到他们变化情况 2.点击按钮后打印效果: 在控制台没有任何输出内容,说明就像图中展示的效果一样,这种的顺序 移动不会产生'dom重新渲染' ~~~ ~~~ <template> <div class="border"> <h1>A 结点</h1> <button @click="orderAsc = !orderAsc">移动</button> <div v-if="orderAsc"> <ChildrenB /> <ChildrenC> </ChildrenC> <ChildrenD /> </div> <div v-else> <ChildrenC> </ChildrenC> <ChildrenD /> <ChildrenB /> </div> </div> </template> ~~~ >[danger] ##### 场景二说明 ![](https://box.kancloud.cn/a12c927a8a30ccabe97a4e0639559713_1211x567.png) ~~~ 1.B,C,D组件在虚拟树上属于同一个节点,点击按钮后,将C组件结构放到, B组件内,这种情况其实就是对应'删除CEF,新建CEF' 2.打印结果: E destroyed F destroyed C destroyed E mounted F mounted C mounted B updated 3.通过打印结果可以看出来,会先删除dom树中最下面的叶子节点也就是E,F然后在删除C节点, 在依次创建E,F,C节点,最后更新B节点也就是变化节点 ~~~ ~~~ <template> <div class="border"> <h1>A 结点</h1> <button @click="init = !init">删除CEF,新建CEF</button> <div v-if="init"> <ChildrenB /> <ChildrenC> <ChildrenE /> <ChildrenF /> </ChildrenC> <ChildrenD /> </div> <div v-else> <ChildrenB> <ChildrenC> <ChildrenE /> <ChildrenF /> </ChildrenC> </ChildrenB> <ChildrenD /> </div> </div> </template> ~~~ >[danger] ##### 场景三的案例 ![](https://box.kancloud.cn/557f36da37f572353b80581cf61c5efb_1171x507.png) ~~~ 1.删除CEF,新建GEF 2.点击后打印结果: E destroyed F destroyed C destroyed E mounted F mounted G mounted ~~~ ~~~ <template> <div class="border"> <h1>A 结点</h1> <button @click="init = !init">删除CEF,新建GEF</button> <div v-if="init"> <ChildrenB /> <ChildrenC> <ChildrenE /> <ChildrenF /> </ChildrenC> <ChildrenD /> </div> <div v-else> <ChildrenB /> <ChildrenG> <ChildrenE /> <ChildrenF /> </ChildrenG> <ChildrenD /> </div> </div> </template> ~~~ >[danger] ##### 场景四(相同dom 或者相同组件) ![](https://box.kancloud.cn/c92c57527b5bd9df388359f18a2c7983_1201x456.png) ~~~ 1.相同组件或者dom 没有加'key'的情况,会先删除E,F 并且最后更新B1和B2 2.打印结果: E mounted F mounted E destroyed F destroyed B1 updated B2 updated 2.这个案例可以看出一点跟我们预期有点不同是先创建了E,F 然后又销毁了之前的E,F,最后在更新 B1和B2 ~~~ ~~~ <template> <div class="border"> <h1>A 结点</h1> <button @click="init = !init">更新删除新建</button> <div v-if="init"> <ChildrenB :number="1" /> <ChildrenB :number="2"> <ChildrenE /> <ChildrenF /> </ChildrenB> <ChildrenB :number="3" /> </div> <div v-else> <ChildrenB :number="2"> <ChildrenE /> <ChildrenF /> </ChildrenB> <ChildrenB :number="1" /> <ChildrenB :number="3" /> </div> </div> </template> ~~~ >[danger] ##### 场景五移动有key (相同dom 或者相同组件) ![](https://box.kancloud.cn/153f342c860da87d98ec2d1f8cc95d66_1148x472.png) ~~~ 1.相同dom 和相同组件加'key',发现不会像上面的案例出现,先删除E,F组件,在创建 E,F组件在更新,这次反而会直接更新B2 一步到位 2.打印结果: B2 updated 3.和上面的案例做了同样一件事明显可以看出加'key'的好处 ~~~ ~~~ <template> <div class="border"> <h1>A 结点</h1> <button @click="init = !init">移动(有key)</button> <div v-if="init"> <ChildrenB key="1" :number="1" /> <ChildrenB key="2" :number="2"> <ChildrenE /> <ChildrenF /> </ChildrenB> <ChildrenB key="3" :number="3" /> </div> <div v-else> <ChildrenB key="2" :number="2"> <ChildrenE /> <ChildrenF /> </ChildrenB> <ChildrenB key="1" :number="1" /> <ChildrenB key="3" :number="3" /> </div> </div> </template> ~~~ >[danger] ##### 场景六 无key 新增 (相同dom 或者相同组件) ![](https://box.kancloud.cn/1222ea310315de39366361741d5ddb0d_1179x331.png) ~~~ 1.当无key 插入一个新的节点的时候,会先销毁不相同的节点并且在重新更 新 2.打印结果: B3 destroyed B3 updated B2 updated ~~~ ~~~ <template> <div class="border"> <h1>A 结点</h1> <button @click="init = !init">更新新建(无key)</button> <div v-if="init"> <ChildrenB :number="1" /> <ChildrenB :number="2" /> <ChildrenB :number="3" /> </div> <div v-else> <ChildrenB :number="1" /> <ChildrenB :number="4" /> <ChildrenB :number="2" /> <ChildrenB :number="3" /> </div> </div> </template> ~~~ >[danger] ##### 场景六 有key ![](https://box.kancloud.cn/1222ea310315de39366361741d5ddb0d_1179x331.png) ~~~ 1.打印结果: B4 mounted 2.和上面案例相比如果绑定了'key',可以发现只需要创建一次B4 一步到位 ~~~