【明年找到好工作】:面试题打卡第六天(vue2篇)

一、watch 与 computed 的区别

1、computed 基本使用

<template>
     <div>
        <p>num {{num}}</p>
        <p>double1 {{double1}}</p>
        <input v-model="double2"/>
     </div>
</template>
<script>
export default {
    data() {
        return {
            num: 20
        }
    },
    computed: {
        double1() {
            return this.num * 2
        },
        double2: {
            // 获取值
            get() {
                return this.num * 2
            },
            // 设置值
            set(val) {
                this.num = val/2
            }
        }
    }
}
</script>

2、watch基本使用

<template>
    <div>
        <input v-model="name"/>
        <input v-model="info.city"/>
    </div>
</template>

<script>
export default {
    data() {
        return {
            name: '浩浩',
            info: {
                city: '汕头'
            }
        }
    },
    watch: {
        name(oldVal, val) {
            // 值类型,可正常拿到 oldVal 和 val
            console.log('watch name', oldVal, val) 
        },
        info: {
            handler(oldVal, val) {
                // 引用类型,拿不到 oldVal 。因为指针相同,此时已经指向了新的 val
                console.log('watch info', oldVal, val) 
            },
            deep: true // 深度监听
            immediate: true // 初始化时立即执行
        }
    }
}
</script>

3、区别

computed:

  • 支持缓存,只有依赖的数据发生变化,才会重新计算
  • 不支持异步,当computed内存在异步操作时,无法监听数据的变化
  • 如果属性依赖于其他属性(一对多,一对一),一般使用computed
  • computed中,属性中都有get和set方法
  • 监听的属性来自于 data中声明过或者父组件传递的props中的数据

watch:

  • 不支持缓存,数据发生变化时,直接回触发相应操作
  • 支持异步
  • 监听的函数接收俩个参数(之前的值,最新值)
  • watch两个属性值
    • immediate:组件加载立即触发回调函数执行
    • deep: 深度监听 为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变
  • deep无法监听到数组的变动和对象的新增,参考vue数组变异,只有以响应式的方式触发才会被监听到

已上面为例子,如何单纯监听 info 中 age的变化

<template>
    <div>
        <input v-model="info.age"/>
    </div>
</template>

<script>
export default {
    data() {
        return {
            name: '浩浩',
            info: {
                city: '汕头',
                age: '12'
            }
        }
    },
    watch: {
        'info.age': function(oldVal, val) {
           console.log('watch info', oldVal, val) 
        }          
    }
}
</script>

### 二、事件绑定

1、基本使用

<template>
    <div>
        <p>{{num}}</p>
        <button @click="increment1">+1</button>
        <button @click="increment2(2, $event)">+2</button>
    </div>
</template>

<script>
export default {
    data() {
        return {
            num: 0
        }
    },
    methods: {
        increment1(event) {
            // 是原生的 event 对象
            console.log('event', event, event.__proto__.constructor) 
            // 事件是被注册到当前元素的
            console.log(event.currentTarget) 
            this.num++
        },
        increment2(val, event) {
            console.log(event.target)
            this.num = this.num + val
        },
        loadHandler() {}
    },
    mounted() {
        // 自定义挂载事件
        window.addEventListener('load', this.loadHandler)
    },
    beforeDestroy() {
        window.removeEventListener('load', this.loadHandler)
    }
}
</script>

2、注意

  • 使用 vue 的方式绑定事件时,传递 event 事件对象时原生事件对象,事件时被挂载到当前对象中(跟 DOM 事件一致)
  • 使用自定义绑定事件时,在组件销毁前需要对事件进行销毁
  • 使用 vue 的方式绑定事件时,组件销毁时会自动被解绑

三、样式绑定

1、基本使用

<template>
    <div>
        <p :class="{ black: isBlack, yellow: isYellow }">使用 class</p>
        <p :class="[black, yellow]">使用 class (数组)</p>
        <p :style="styleData">使用 style</p>
    </div>
</template>

<script>
export default {
    data() {
        return {
            isBlack: true,
            isYellow: true,

            black: 'black',
            yellow: 'yellow',

            styleData: {
                fontSize: '40px', // 转换为驼峰式
                color: 'red',
                backgroundColor: '#ccc' // 转换为驼峰式
            }
        }
    }
}
</script>

<style scoped>
    .black {
        background-color: #999;
    }
    .yellow {
        color: yellow;
    }
</style>

四、v-show和v-if 的区别

1、基本使用

<template>
    <div>
        <p v-if="type === 'a'">A</p>
        <p v-else-if="type === 'b'">B</p>
        <p v-else>other</p>

        <p v-show="type === 'a'">A by v-show</p>
        <p v-show="type === 'b'">B by v-show</p>
    </div>
</template>

<script>
export default {
    data() {
        return {
            type: 'a'
        }
    }
}
</script>

2、注意

从原理看:

  • v-if:动态创建和销毁(即声明周期会重新执行一遍)

  • v-show:纯CSS样式的显示和隐藏(即声明周期不会重新执行一遍)

从性能看:

  • v-if:需要更高的切换开销

  • v-show:性能好


五、表单如何绑定变量

1、基本使用

<template>
    <div>
        <p>输入框: {{name}}</p>
        <input type="text" v-model.trim="name"/>
        <input type="text" v-model.lazy="name"/>
        <input type="text" v-model.number="age"/>

        <p>多行文本(值): {{desc}}</p>
        <textarea v-model="desc"></textarea>

        <p>复选框(布尔值) {{checked}}</p>
        <input type="checkbox" v-model="checked"/>

        <p>多个复选框 (数组){{checkedNames}}</p>
        <input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
        <label for="jack">Jack</label>
        <input type="checkbox" id="john" value="John" v-model="checkedNames">
        <label for="john">John</label>
        <input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
        <label for="mike">Mike</label>

        <p>单选(值) {{gender}}</p>
        <input type="radio" id="male" value="male" v-model="gender"/>
        <label for="male"></label>
        <input type="radio" id="female" value="female" v-model="gender"/>
        <label for="female"></label>

        <p>下拉列表选择 (值){{selected}}</p>
        <select v-model="selected">
            <option disabled value="">请选择</option>
            <option>A</option>
            <option>B</option>
            <option>C</option>
        </select>

        <p>下拉列表选择(多选)(数组) {{selectedList}}</p>
        <select v-model="selectedList" multiple>
            <option disabled value="">请选择</option>
            <option>A</option>
            <option>B</option>
            <option>C</option>
        </select>
    </div>
</template>

<script>
export default {
    data() {
        return {
            name: '浩浩',
            age: 18,
            desc: '自我介绍',

            checked: true,
            checkedNames: [],

            gender: 'male',

            selected: '',
            selectedList: []
        }
    }
}
</script>

六、遍历数组及对象

1、基本使用

<template>
    <div>
        <p>遍历数组</p>
        <ul>
            <li v-for="(item, index) in listArr" :key="item.id">
                {{index}} - {{item.id}} - {{item.title}}
            </li>
        </ul>

        <p>遍历对象</p>
        <ul >
            <!-- key 代表 a , b , c -->
            <li v-for="(val, key, index) in listObj" :key="key">
                {{index}} - {{key}} -  {{val.title}}
            </li>
        </ul>
    </div>
</template>

<script>
export default {
    data() {
        return {
            flag: false,
            listArr: [
            // 数据结构中,最好有 id ,方便使用 key
                { id: 'a', title: '标题1' }, 
                { id: 'b', title: '标题2' },
                { id: 'c', title: '标题3' }
            ],
            listObj: {
                a: { title: '标题1' },
                b: { title: '标题2' },
                c: { title: '标题3' },
            }
        }
    }
}
</script>

七、组件生命周期

  • 1、创建前 beforeCreate:data 和 methods 中的数据都还没有初始化

  • 2、创建后 created:data 和 methods 中的数据都初始化完毕(可以最早操作data或method中数据的钩子函数

  • 3、挂载前 beforeMouted:页面模板已经在内存中编译好,但尚未挂载到页面中(此时页面还是旧的)

  • 4、挂载后 mouted:此时页面和内存中都是最新的数据可以最早操作DOM的钩子函数

  • 5、更新前 beforeUpdate:此时页面中显示的数据是旧的,但data是新的(页面和数据没有进行同步过

  • 6、更新后 updated: 此时页面显示数据和最新的data数据进行同步完毕

  • 7、销毁前 beforeDestroy:在该阶段中,组件实例还没有被销毁,data,method可以进行使用

  • 8、销毁后 destroved:完全被销毁了


其他三个钩子函数

  • 9、activated:出现在当组件被keep-alive包裹时

  • 10、deactivated:出现在当组件被keep-alive包裹时

如: keep-alive包裹两个组件:组件A和组件B。

  • 当第一次切换到组件A时,组件A的created和activated生命周期函数都会被执行。
  • 在切换到组件B,这时组件A的deactivated的生命周期函数会被触发。
  • 在切换回组件A,组件A的activated生命周期函数会被触发,但是它的created生命周期函数不会被触发了
  • 11、errorCaptured:每当事件处理程序或[生命周期钩子抛出错误时,Vue 会调用该钩子

八、多组件生命周期

1、初始化阶段:

创建后(父)=> 创建后(子)=> 挂载后(子)=> 挂载后(父)

2、更新阶段:

更新前(父)=> 更新前(子)=> 更新后(子)=> 更新后(父)

3、销毁阶段:

销毁前(父)=> 销毁前(子)=> 销毁后(子)=> 销毁后(父)


九、组件通信

1、父子组件

使用 emit 和 props

例子:todo-list

// 父
<template>
    <div class="container">
       <div>
		 <input v-model="inputValue"/>
		 <button @click="handleClick">提交</button>
	   </div>
        <todo-list :data="list" @delete="handleDelete"></todo-list>
    </div>
</template>
<script>
import TodoList from './TodoList/index'
export default {
    components: {
        TodoList
    },
    data() {
        return {
            inputValue: "",
            list: []
        }
    }
    methods: {
       handleClick:function(){
        this.list.push(this.inputValue)
        this.inputValue=""
      },
      handleDelete:function(index){
		this.list.splice(index,1)//删除
	  }
    }
}
</script>
// TodoList
<template>
    <ul>
       <li v-for="(item, index) in list" :key="index" @click="handleClick(index)">{{item.content}}</li>
    </ul>
</template>
<script>
export default {
    props: {
        list: list
    },
    methods: {
        handleClick(index) {
             // 调用父组件的事件
            this.$emit('delete',index);
        }
    }
}
</script>

2、兄弟组件

使用$emit

// 父
<template>
   <div class="container">
       <component-one />
       <component-two />
   </div>
</template>
// component-one
<template>
    <div class="container"></div>
</template>
<script>
import event from "./evnet";
export default {
    methods: {
      handler(val) {
         console.log("触发", val) 
      }  
    },
    mounted: {
        // 绑定自定义事件
        event.$on('onAdd', this.handler)
    }
}
</script>
// component-two
<template>
    <div class="container" @click="clcik"></div>
</template>
<script>
import event from "./evnet";
export default {
    data() {
        return {
            val: "兄弟组件传递的信息"
        }
    }
    methods: {
      clcik() {
          event.$emit('onAdd', this.val)
      }
    }
}    
</script>
// .event
import Vue from 'vue'
export default new Vue()

十、父子组件数据的双向绑定

1、基本使用

<template>
   <div>
       <p>{{ name }}</p> 
       <Child v-model="name"></Child>
   </div>
</template>
<script>
    import Child from './Child/index';
    export default {
        components: {
            Child
        },
        data() {
            return {
                name: '浩浩'
            }
        }
    }
</script>
<template>
     <input type="text" :value="val" @input="$emit('change1', $event.target.val)" />
</template>
<script>
export default {
    props: {
        val: String,
        default() {
            return ''
        }
    }
    
    // 关键代码
    model: {
      prop: 'val',
      event: 'change1'
    }
}
</script>

现象:值来源于父组件,值修改在子组件进行修改,值显示在父组件中显示


十一、nextTick

1、基本使用

<template>
  <div id="app">
    <ul ref="ul1">
        <li v-for="(item, index) in list" :key="index">
            {{item}}
        </li>
    </ul>
    <button @click="addItem">添加一项</button>
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
      return {
        list: ['a', 'b', 'c']
      }
  },
  methods: {
    addItem() {
        this.list.push(`${Date.now()}`)
        this.list.push(`${Date.now()}`)
        this.list.push(`${Date.now()}`)

        // 方式一:
        const ulElem = this.$refs.ul1
        console.log( ulElem.childNodes.length)
        
        // 方式二:
        this.$nextTick(() => {
          const ulElem = this.$refs.ul1
          console.log( ulElem.childNodes.length )
        })
    }
  }
}
</script>

2、注意

通过方式一:每次触发 addItem() 事件,获取到的 DOM 是更新前的数据(即数据在没与页面进行同步后就获取该DOM对象)

通过方式二:每次触发 addItem() 事件,数据会先与页面进行同步后,再来获取该DOM元素


十二、solt 插槽

1、基本使用

<template>
    <a :href="url">
        <slot> 默认内容,即父组件没设置内容时,这里显示</slot>
    </a>
</template>

<script>
export default {
    props: ['url'],
}
</script>
<template>
     <SlotDemo :url="website.url">
         // 若 title 没有传递,则显示子组件(slot)设置的默认内容
            {{ title }}
     </SlotDemo>
</template>
<script>
export default {
    data() {
        return {
            title: 'vue高级特性'
        }
    }
}
</script>

2、作用域插槽

<template>
    <a :href="url">
        <slot :slotData="website">
            {{website.title}} 
        </slot>
    </a>
</template>

<script>
export default {
    props: ['url'],
    data() {
        return {
            website: {
                url: 'http://hhmax.xyz',
                title: '个人博客', 
            }
        }
    }
}
</script>

将插槽中的信息传递给调用者,使用 :slotData(自定义变量名),后面跟着要传递的参数

<ScopedSlotDemo :url="website.url">
    <template v-slot="slotProps">
         {{slotProps.slotData.title}}
    </template>
</ScopedSlotDemo>

3、具名插槽

<!-- NamedSlot -->
<template>
    <div class="container">
        <header>
             <slot name="header"></slot>
        </header>
        <main>
           <slot></slot>
        </main>
        <footer>
           <slot name="footer"></slot>
        </footer>
    </div>
</template>
<NamedSlot>
    <template v-slot:header>
        <h1>头部</h1>
    </template>
    <p>主体内容</p>
    <template #footer>
        <h1>尾部</h1>
    </template>
</NamedSlot>

十三、动态组件

1、基本使用

<template>
    <div class="container">
        <div class="item" v-for"(item, i) in newList" :key="item.id">
             <component :is="item.componentName" :data="item">
        </div>
    </div>
</template>
<script>  
import componentVideo from './componentVideo';
import componentText from './componentText';
export default {
    components: {
        componentVideo,
        componentText
    }
    data() {
        return {
            newList: [
                {
                    id: 1,
                    content: '视频组件',
                    cimponentName: 'componentText'
                },
                {
                    id: 2,
                    content: '内容组件'
                    cimponentName: 'componentText'
                }
            ]
        }
    }
}
</script>

动态组件出现在根据数据动态显示不同的组件,如(新闻APP)


十四、异步组件

1、基本使用

<template>
    <div class="container">
        <componentText v-if="showcomponent" />
        <button @click="showcomponent = true">展示组件</button>
    </div>
</template>
<script>
export default {
   components: {
       componentText: () => import('./componentText/index'),
   },
   data() {
       return {
           showcomponent: false
       }
   }
}
</script>

十五、缓存组件

1、基本使用

<template>
    <div>
        <button @click="changeState('A')">A</button>
        <button @click="changeState('B')">B</button>
        <button @click="changeState('C')">C</button>

        <keep-alive> 
            <KeepAliveStageA v-if="state === 'A'"/> 
            <KeepAliveStageB v-if="state === 'B'"/>
            <KeepAliveStageC v-if="state === 'C'"/>
        </keep-alive>
    </div>
</template>

<script>
import KeepAliveStageA from './KeepAliveStateA'
import KeepAliveStageB from './KeepAliveStateB'
import KeepAliveStageC from './KeepAliveStateC'

export default {
    components: {
        KeepAliveStageA,
        KeepAliveStageB,
        KeepAliveStageC
    },
    data() {
        return {
            state: 'A'
        }
    },
    methods: {
        changeState(state) {
            this.state = state
        }
    }
}
</script>
<!-- KeepAliveStageA -->
<template>
    <p>state A</p>
</template>

<script>
export default {
    mounted() {
        console.log('A mounted')
    },
    destroyed() {
        console.log('A destroyed')
    }
}
</script>

在切换过程中:会触发deactivated 和 activated生命周期函数。在没有使用keep-alive时,切换过程会触发 destroyed

keep-alive参数

  • include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
  • exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
  • max - 数字。最多可以缓存多少组件实例。

十六、mixin

1、基本使用

<template>
    <div>
        <p>{{name}} {{major}} {{city}}</p>
        <button @click="showName">显示姓名</button>
    </div>
</template>

<script>
import myMixin from './mixin'

export default {
    mixins: [myMixin], // 可以添加多个,会自动合并起来
    data() {
        return {
            name: '浩浩',
            major: 'web'
        }
    },
    methods: {
    },
    mounted() {
        console.log('component mounted', this.name)
    }
}
</script>
// ./mixin
export default {
    data() {
        return {
            city: '北京'
        }
    },
    methods: {
        showName() {
            console.log(this.name)
        }
    },
    mounted() {
        console.log('mixin mounted', this.name)
    }
}

把组件中共有的逻辑抽离出来到mixxin中,使用时将其引入即可,会于当前组件的变量,方法进行覆盖或合并,钩子函数则进行合并。

缺点:

  • 变量来源不明确,不利于阅读
  • 多个 mixin 可能会造成冲突
  • mixin和组件可能出现多对多的关系,复杂度较高
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你听雪飘过的声音

您的鼓励将是我前进的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值