vue面试(一)
1.watch如何深度监听,watch监听引用类型数据,拿不到oldVal
<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) { // eslint-disable-next-line console.log('watch name', oldVal, val) // 值类型,可正常拿到 oldVal 和 val }, info: { handler(oldVal, val) { // eslint-disable-next-line console.log('watch info', oldVal, val) // 引用类型,拿不到 oldVal 。因为指针相同,此时已经指向了新的 val }, deep: true // 深度监听,可以监听到引用类型中的属性city。 } } } </script>
2. v-if和v-show
跟新不是很频繁,用v-if,
跟新很频繁,用v-show,利用的display:none
3,v-for 循环遍历,key的重要性,v-for 和v-if是不能一起使用
加了key(一定要具有唯一性),vue中列表循环需加:key="唯一标识" 唯一标识可以是item里面id index等,因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM
<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 v-if="flag"> <!-- v-if不能和v-for在同一个标签使用,v-for渲染模板的优先级比v-if高,遍历一遍v-if判断一次,效率不高,在 在父级容器加个v-if判断即可 --> <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: 'a', title: '标题1' }, // 数据结构中,最好有 id ,方便使用 key { id: 'b', title: '标题2' }, { id: 'c', title: '标题3' } ], listObj: { a: { title: '标题1' }, b: { title: '标题2' }, c: { title: '标题3' }, } } } } </script>
4. 事件, event, 事件修饰符, 事件绑定到哪里
在Vue中,事件修饰符处理了许多DOM事件的细节,让我们不再需要花大量的时间去处理这些烦恼的事情,而能有更多的精力专注于程序的逻辑处理。在Vue中事件修饰符主要有:
.stop:等同于JavaScript中的event.stopPropagation(),防止事件冒泡
.prevent:等同于JavaScript中的event.preventDefault(),防止执行预设的行为(如果事件可取消,则取消该事件,而不停止事件的进一步传播)
.capture:与事件冒泡的方向相反,事件捕获由外到内
.self:只会触发自己范围内的事件,不包含子元素
.once:只会触发一次
<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) { console.log('event', event, event.__proto__.constructor) // 是原生的 event 对象 //event.target 事件挂载到那个节点上 console.log(event.target) console.log(event.currentTarget) // 注意,事件是被注册到当前元素的,和 React 不一样 this.num++ // 1. event 是原生的 // 2. 事件被挂载到当前元素 // 和 DOM 事件一样 }, increment2(val, event) { // eslint-disable-next-line console.log(event.target) this.num = this.num + val }, loadHandler() { // do some thing } }, mounted() { window.addEventListener('load', this.loadHandler) }, beforeDestroy() { //【注意】用 vue 绑定的事件,组建销毁时会自动被解绑 // 自己绑定的事件,需要自己销毁!!! window.removeEventListener('load', this.loadHandler) } } </script>
5. v-model,常见表单项, textarear checkbox, radio, select,
修饰符, lazy number trim
<template> <div> <p>输入框: {{name}}</p> <!-- 修饰符trim,清除前后空格 --> <input type="text" v-model.trim="name"/> <!-- 修饰符lazy,只有在input输入框发生一个blur时才触发 --> <input type="text" v-model.lazy="name"/> <!-- 修饰符number, 将用户输入的字符串转换成number --> <input type="text" v-model.number="age"/> <p>多行文本: {{desc}}</p> <textarea v-model="desc"></textarea> <!-- 注意,<textarea>{{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>
6.props, $emit, 组件通讯,自定义事件
父子传递,自定义事件
Vue兄弟组件之间通信 eventBus
新建event.js,兄弟组件映入该组件,兄弟组件通过event.$on, 或者event.$emit来通信
import Vue from 'vue' export default new Vue()
或者在main.js中设置全局事件总线,然后兄弟组件this.$bus.$on, this.$bus.$emit来通信
// new Vue()实例化了一个Vue的实例化对象 //因为只有组件对象或者Vue的实例化对象才能调用$on和$emit //想要成为事件总线的条件: //1、所有的组件对象必须都能看得到这个总线对象,因此我们把这个对象放在了Vue原型 //2、这个事件总线对象必须能调用$on和$emit方法(总线对象必须是Vue的实例化对象或者是组件对象) new Vue({ beforeCreate(){ Vue.prototype.$bus = this }, el:'#root', render: h => h(App) })
父组件
<template> <div> <Input @add="addHandler"/> <List :list="list" @delete="deleteHandler"/> </div> </template> <script> import Input from './Input' import List from './List' export default { components: { Input, List }, data() { return { list: [ { id: 'id-1', title: '标题1' }, { id: 'id-2', title: '标题2' } ] } }, methods: { addHandler(title) { this.list.push({ id: `id-${Date.now()}`, title }) }, deleteHandler(id) { this.list = this.list.filter(item => item.id !== id) } }, created() { // eslint-disable-next-line console.log('index created') }, mounted() { // eslint-disable-next-line console.log('index mounted') }, beforeUpdate() { // eslint-disable-next-line console.log('index before update') }, updated() { // eslint-disable-next-line console.log('index updated') }, } </script>
子组件input
<template> <div> <input type="text" v-model="title"/> <button @click="addTitle">add</button> </div> </template> <script> import event from './event' export default { data() { return { title: '' } }, methods: { addTitle() { // 调用父组件的事件 this.$emit('add', this.title) // 调用自定义事件 event.$emit('onAddTitle', this.title) this.title = '' } } } </script>
子组件List
<template> <div> <ul> <li v-for="item in list" :key="item.id"> {{item.title}} <button @click="deleteItem(item.id)">删除</button> </li> </ul> </div> </template> <script> import event from './event' export default { // props: ['list'] props: { // prop 类型和默认值 list: { type: Array, default() { return [] } } }, data() { return { } }, methods: { deleteItem(id) { this.$emit('delete', id) }, addTitleHandler(title) { // eslint-disable-next-line console.log('on add title', title) } }, created() { // eslint-disable-next-line console.log('list created') }, mounted() { // eslint-disable-next-line console.log('list mounted') // 绑定自定义事件 event.$on('onAddTitle', this.addTitleHandler) }, beforeUpdate() { // eslint-disable-next-line console.log('list before update') }, updated() { // eslint-disable-next-line console.log('list updated') }, beforeDestroy() { // 及时销毁,否则可能造成内存泄露 event.$off('onAddTitle', this.addTitleHandler) } } </script>
父子组件生命周期的顺序, 父组件created创建了实例,子组件list也创建了实例,并且mounted渲染了模板,然后父组件才mounted渲染模板
子组件更新完,父组件才会更新完
生命周期过程;https://www.cnblogs.com/fsg6/p/14468597.html