【面试题】面试不面试,你都必须得掌握的vue知识_<template v-for="(slot, name) in $slots" key="nam(1)

box-shadow: 0px 0px 8px 1px #ccc;
background-color: rgba($color: #666, $alpha: 0.5);
transition: all 1s;
}

指令挂载方法:在有滚动条的容器上,添加v-scrollTop指令, 并提供相应的值即可。如果不提供,则使用默认值

指令注册方法: 同第k点, 先install, 在本文件暴露出去。然后在main.js文件中引入,并使用vue.use(引入的名称)全局注册

第一种方法:直接使用binding.value判断回到顶部图标出现的位置(相对推荐)

Vue.directive(‘scrollTop’, {
inserted(el, binding) {
如果未设置binding.value的值,则默认为200
滚动条移动超过200px的距离就显示,反之则隐藏
if (!binding.value) binding.value = 200

el.style.scrollBehavior = 'smooth'

const backEl = document.createElement('div')
backEl.className = 'scroll-top-class el-icon-top'
el.appendChild(backEl)
backEl.addEventListener('click', () => (el.scrollTop = 0))

el.addEventListener('scroll', () => {
  if (el.scrollTop >= binding.value) backEl.style.opacity = 1
  else backEl.style.opacity = 0
})

}
})

第二种方法:使用binding.value,根据滚动条的总高度所占比例,间接判断回到顶部图标出现的位置
(不推荐,因为在产品列表无限滚动情况下,滚动条高度是动态变化的,无法适用,而且倍数不好控制)

// 滚动条指令
Vue.directive(‘scrollTop’, {
inserted(el, binding) {
if (binding.value >= 1 || binding.value <= 0) return new Error(‘v-scrollTop的绑定值需要介于0到1之间’)

`获取元素的整体高度`
const elH = el.offsetHeight

`也可以给visibilityHeight定值(不推荐,无法兼容所有需要滚动条的页面)`
let visibilityHeight = 0
if (binding.value) visibilityHeight = binding.value * elH
`阈值默认为滚动区域整体高度的0.2倍`
else visibilityHeight = 0.2 * elH

`为滚动条返回顶部添加平滑的过渡效果`
el.style.scrollBehavior = 'smooth'

const backEl = document.createElement('div')
backEl.className = 'scroll-top-class el-icon-top'
`将创建的回到顶部图标作为孩子插入到el中`
el.appendChild(backEl)

backEl.addEventListener('click', () => (el.scrollTop = 0))

el.addEventListener('scroll', () => {
  if (el.scrollTop >= visibilityHeight) backEl.style.opacity = 1
  else backEl.style.opacity = 0
})

}
})

2. 自定义组件,实现回到顶部的效果:
使用这种方式,需要在每个有回到顶部需求的文件中引入该自定义组件,并指定高度阈值及滚动条dom容器对应的字符串


#### h. 使用install和use进行全局注册



lodopPrintPdf.js

import Vue from ‘vue’
import PrintBtn from ‘./printBtn’
import merge from ‘element-ui/src/utils/merge’

export default async function lodopPrintPdf(option) {
const ExtendPrintBtn = Vue.extend(PrintBtn)
继承打印组件并初始化vue实例
const vm = new ExtendPrintBtn({})
合并option,等价于Object.assign(vm, option)
相当于遍历添加传入vm的prop参数
merge(vm, option)
调用实例的方法,js动态加载完成
return vm.printHandler()
}
复制代码



globalConst.js

import lodopPrintPdf from ‘@/components/lodopPrint/lodopPrintPdf.js’

export default {
install(Vue) {
在vue的原型对象上挂载$lodopPrintPdf,并暴露出去
Vue.prototype.$lodopPrintPdf = lodopPrintPdf //lodop打印pdf
}
}



main.js

Vue.use的用法: 安装Vue插件。 如果插件是一个对象,必须提供 install 方法。 如果插件是一个函数,它会被作为 install 方法。

import globalConst from ‘@/commons/globalConst’

Vue.use(globalConst)
复制代码



$lodopPrintPdf方法的使用

传入的五个参数就是前面定义的函数所接收的option值,相当于调用打印组件,传入对应的五个props
this.KaTeX parse error: Expected '}', got 'EOF' at end of input: …rintable: this.refs.label.$el,
paperSize: [841.89, 595.28],
onSuccess: this.resetLoading,
onError: this.resetLoading
})


#### i. 混入 && 继承


  **混入:** 对于具有相同逻辑的`vue`文件,其实可以抽取成一个`混入`,存放公共的`js`代码。在使用`混入`的`vue`文件中,可以定义相同的变量或者方法来`覆盖`混入中的变量或者方法。


  **继承:** 相比`混入`,`继承`更加霸道,可以`继承`整个`vue`文件。同时在`继承`文件中,可以添加一些额外的`js`代码。如果在`被继承`的组件中存在这些`js`变量和方法,那么`继承`组件就会覆盖这些变量和方法,如果不存在则为添加。如果在`继承`组件中添加`html`和`css`代码,不管这些代码之前是否和`被继承`组件的`html`和`css`代码冲突,`继承`组件的`html`和`css`代码都会以自身代码为主,不会继承`被继承`组件的`html`和`css`代码。




#### j. $props三兄弟和inherits属性


  **$props**:当前组件接收到的 props 对象。Vue 实例代理了对其 props 对象属性的访问。


  **$attrs**: 包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。


  **$listeners**:包含了父作用域中的(不含.native 修饰器的)v-on事件监听器。


  `inherits属性`的作用是**禁止传入的属性添加到组件的根元素上。默认为true,即将传入的属性添加到组件的根元素上。**


   **应用**:`v-bind="$attrs"`和`v-on="$listeners"`一般用于组件封装上,必须得绑定在组件上。`v-bind="$attrs"`相当于一个**展开运算符**,将从父组件传来的`props`且未被当前组件`props`接收的`prop`挂载到组件上,使得组件具有可扩展性。如果未绑定,孙子组件可以通过`this.$attrs`拿到子组件的`props`,但是无法拿到父组件的`props`。如果要拿到父组件的`props`,则需要在子组件上绑定`v-bind="$attrs"`,这样孙子组件中的`this.$attrs`就指向父组件的`props`。


[inherits属性-官方说明](https://bbs.csdn.net/topics/618166371)


[inherits属性-掘金文章说明](https://bbs.csdn.net/topics/618166371)


[$props-掘金文章说明](https://bbs.csdn.net/topics/618166371)


[$listeners-掘金文章说明](https://bbs.csdn.net/topics/618166371)


#### k. v-model语法糖


[官网双向绑定原理详解](https://bbs.csdn.net/topics/618166371)


#### l. 修饰符的顺序及理解


  [修饰符详解1](https://bbs.csdn.net/topics/618166371)


  [修饰符详解2](https://bbs.csdn.net/topics/618166371)


#### m. render函数 && 函数式组件


#### n. 路由守卫


### 2. vue的冷知识(`vue间谍`,大部分内容来自`Sunshine_Lin`的掘金博客。部分内容有自己的思考和扩展, 以`扩展`两字进行标注)


#### a. 为什么不建议v-for和v-if同时存在?



{{item}}

拓展:
vue2中的v-for优先级高于v-if, vue3则相反。首先会把7个元素都遍历出来,然后再一个个判断是否为3,并把3的dom给隐藏掉, 这样的坏处就是,渲染了无用的3节点,增加无用的dom操作,建议使用computed来解决这个问题:

{{item}}

computed() {
list() {
return [1, 2, 3, 4, 5, 6, 7].filter(item => item !== 3)
}
}


#### b. 为什么不建议用index做key,为什么不建议用随机数做key?



{{item.name}}

list: [
{ name: ‘小明’, id: ‘123’ },
{ name: ‘小红’, id: ‘124’ },
{ name: ‘小花’, id: ‘125’ }
]

渲染为

小明
小红
小花

现在我执行 list.unshift({ name: ‘小林’, id: ‘122’ })

渲染为

小林
小明
小红
小花

新旧对比

小明
小林
小红
小明
小花
小红
小花

可以看出,如果用index做key的话,其实是更新了原有的三项,并新增了小花,虽然达到了渲染目的,但是损耗性能

现在我们使用id来做key,渲染为

小明
小红
小花

现在我执行 list.unshift({ name: ‘小林’, id: ‘122’ }),渲染为

小林
小明
小红
小花

新旧对比

                       <div key="122">小林</div>
小明
小明
小红
小红
小花
小花

可以看出,原有的三项都不变,只是新增了小林这个人,这才是最理想的结果


  用`index`和用`随机数`都是同理,`随机数`每次都在变,做不到专一性,很`渣男`,也很消耗性能,所以,拒绝`渣男`,选择`老实人`


#### c. 为什么data是个函数并且返回一个对象呢?



data之所以是一个函数,是因为一个组件可能会多处调用,而每一次调用就会执行data函数并返回新的数据对象。
这样,可以避免多处调用的数据污染


#### d. vue2的内部指令


![](https://img-blog.csdnimg.cn/img_convert/76e126d45b1e702cc42e8f38f43becc7.webp?x-oss-process=image/format,png)



拓展:

  1. v-text的用法:

本质其实和插值表达式一样。不同的是,v-text后面必须得赋值,它只与赋予的变量的值有关。比如:

{{ unsignedCountries }}

此处只渲染123, 而不会渲染计算属性的值。

  1. v-once:指令所绑定的html标签组件中的变量只会渲染一次,不会随着变量的变化而变化,利用好这一点可以优化

#### e. vue2的生命周期


![](https://img-blog.csdnimg.cn/img_convert/d22ccf9ab6bb402cf4bc11664bb75f8e.webp?x-oss-process=image/format,png)


 



拓展:
1. vue生命周期钩子函数的组成:

vue的生命周期是一个组件/实例创建到销毁的过程中自动执行的函数,主要分为:创建、挂载、更新、销毁四个模块。

挂载$el$mount都是为了将实例化后的vue挂载到指定的dom元素中,但是$el的优先级要高于$mount

如果实例化vue的时候指定el,则vue将会渲染到此el对应的dom中,
反之,若没有指定el,则vue实例会处于一种未挂载的状态,此时可以通过$mount来手动执行挂载

2. 被keep-alive缓存的组件的生命周期:

第一次进入,created -> mounted -> activated
退出时触发deactivated。当再次进入时,只触发activated


#### f. 如何设置动态class,动态style?


(1) 动态`class`(对象形式):


`<div :class="{ 'is-active': true, 'red': isRed }"></div>`


(2) 动态`class`(数组形式):


**拓展:** `<div :class="['is-active', isRed && 'red' ]"></div>`


(3) 动态`style`对象(其中的`css属性`必须使用`驼峰命名`):


`<div :style="{ color: textColor, fontSize: '18px' }"></div>`


(4) 动态`style`数组(使用多个`对象`包裹,`对象`之间的`css属性`使用`逗号`隔开):


`<div :style="[{ color: textColor, fontSize: '18px' }, { fontWeight: '300' }]"></div>`


#### g. 如何处理非响应式数据?


  在我们的Vue开发中,会有一些数据,从始至终都`未曾改变过`,这种`死数据`,既然`不改变`,那也就`不需要对他做响应式处理`了,不然只会做一些无用功消耗性能,比如一些写死的下拉框,写死的表格数据,这些数据量大的`死数据`,如果都进行响应式处理,那会消耗大量性能。



// 方法一:将数据定义在data之外
data () {
拓展:这种非响应式的数据可以直接定义,包括在methods方法里
this.list1 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
this.list2 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
this.list3 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
this.list4 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
this.list5 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
return {}
}

// 方法二:Object.freeze()
data () {
return {
list1: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
list2: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
list3: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
list4: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
list5: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
}
}


#### h. 父子组件的生命周期?


  **挂载阶段:** `父beforeCreate` >`父created` > `父beforeMount` > `子beforeCreate`>`子created`> `子beforeMount` > `子mounted` >`父mounted`


  **扩展:**


  **子组件更新过程:** `父beforeUpdate` > `子beforeUpdate` > `子updated` > `父updated`


  **父组件更新过程(影响到子组件):** `父beforeUpdate` > `子beforeUpdate` > `子updated` > `父updated`


  **父组件更新过程(不影响子组件):** `父beforeUpdate` > `父updated`


  **销毁过程:** `父beforeDestroy` > `子beforeDestroy` > `子destroyed` > `父destroyed`


#### h. Vue采用的是`异步更新`的策略,通俗点说就是,`同一事件循环内`多次修改,会`统一`进行一次`视图更新`,这样才会节省性能。


#### i. 关于props的自定义校验



props: {
num: {
default: 1,
// type:Number, 指定props的类型
// required: true, 指定props是否为必填项
validator: function (value) {
// 返回值为false则验证不通过,报错
return [1, 2, 3, 4, 5].indexOf(value) !== -1
}
}
}

拓展:

如果props没有默认值,一般的写法是:
props: {
num: Number
}

如果props有默认值,一般的写法是:
props: {
num: {
type: Number,
default: 1
}
}

特别地,如果需要给对象或者数组指定默认值,一般的写法是:
props: {
selectList: {
type: Array,
default: () => []
}
}

默认值使用函数的形式返回的原因,其实和data为什么需要返回一个函数大同小异。
多个父组件引用同一个子组件时,引用类型props相互隔离


#### i. 使用this.$options.data()获取`vue文件`data函数返回的`初始对象`状态


#### j. 自定义v-model


  默认情况下,`v-model` 是 `@input 事件侦听器`和 `:value 属性`上的语法糖。但是,你可以在你的`Vue组件`中指定一个`模型属性`来定义`使用什么事件`和`value属性`——非常棒!



export default: {
model: {
event: ‘change’,
prop: ‘checked’
}
}


#### k. 给组件绑定`动态key值`,当`key`值变化时,可以`刷新组件`,重新走组件的生命周期


#### l. 动态指令和动态参数的使用



拓展:使用 @[响应式变量名] 实现动态自定义事件, 使用 :[响应式变量名] 实现动态props

...

#### m. (拓展) hook和$once的使用



`1. $once的介绍:

(1) $once是一个函数,可以为Vue组件实例绑定一个自定义事件,但该事件只能被触发一次,触发之后随即被移除。
(2) $once有两个参数,第一个参数为字符串类型,用来指定绑定的事件名称,第二个参数设置事件的回调函数。
(3) $once可以多次为同一个事件绑定多个回调,触发时,回调函数按照绑定顺序依次执行。
(4) once可以作为修饰符,.once只会触发一次 `

2. 使用$once清除定时器:

通常的代码:使用这种方式会多定义一个响应式变量timer,而且需要分别在两个生命周期里定义定时器以及清除定时器。

export default{
data(){
timer: null
},
mounted(){
this.timer = setInterval(() => {
//具体执行内容
console.log(‘1’)
},1000)
}
beforeDestory(){
clearInterval(this.timer)
this.timer = null
}
}

使用$once的方法解决问题会更加优雅:
export default{
mounted(){
let timer = setInterval(() => {
//具体执行内容
console.log(‘1’)
},1000);
this.$once(‘hook:beforeDestroy’,() => {
clearInterval(timer)
timer = null
})
}
}

3. 使用监听生命周期钩子的hook进行父子组件之间的事件传递:

通常的代码:

//父组件
<rl-child @childMounted=“childMountedHandle”
/>
method () {
childMountedHandle() {
// do something…
}
},

// 子组件
mounted () {
this.$emit(‘childMounted’)
},

使用hook写出的优雅代码:

//父组件
<rl-child @hook:mounted=“childMountedHandle”
/>
method () {
childMountedHandle() {
// do something…
}
}


#### n. (拓展) v-for的使用 ( 非必须,指定`key`值方便`diff算法`对`新旧虚拟dom`进行比较,提升效率)



  1. v-for在数组中的遍历(item 是数组的每一项,index 对应数组的索引,同时支持解构)
  1. v-for在对象中的遍历(value, key, index分别对应值、键、索引)
  1. v-for迭代数字(从1开始打印,1 2 3 4)
  1. v-for迭代数字(输出每一个字符)
{{ str }}

#### o. (拓展) vue2组件为什么只能有一个根节点?



结论: vue2组件只能有一个根节点,但是在vue3组件中,可以有多个根节点

原因:

(1) vue2虚拟dom是一颗单根树形结构patch方法在遍历的时候从根节点开始遍历和比较
它要求组件只有一个根节点,组件会转换为一个虚拟dom

(2) vue3引入了Fragment的概念。这是一个抽象的节点,如果发现组件有多个根,就创建一个Fragment节点
多个根节点作为它的children


#### p. (拓展) 使用`watch`监听路由变化



watch: {
  ‘ r o u t e . q u e r y . i d ′ ( )   . . .    , / / 或者 : ′ route.query.id'() { ...   }, //或者: ' route.query.id() ...  ,//或者route.query.id’: {
handler(new, old) {

  },
  immediate: true
 ...

},
$route(to, from) {
// 监听路由变化
console.log(“watch”, to.query.id)
}
}


#### q. (拓展) 使用`vue.config`全局配置,在`开发阶段`获取组件的错误信息



vue.config是一个对象,包含vue的很多全局配置,这里不一一展开介绍,有兴趣的朋友请移步vue官网
不过vue已经对组件名称进行处理,我们并不清楚具体指代,所以这个扩展我们看看就好,知道就行,最好的是sentry配置

.在main.js中进行全局配置:

Vue.config.errorHandler = function(err, vm, info) {
console.log(组件${vm.$vnode.tag}发生错误:${err.message},${info})
}


      


![](https://img-blog.csdnimg.cn/img_convert/72e7f534aeeae369ede8b4f9796ce37a.webp?x-oss-process=image/format,png)



sentry上注册一个账号,并创建一个vue项目

1.在main.js中,引入配置sentry的index文件, 获取对应的dsn地址
import ‘@/sentry’

2.index.js配置(参照官网)

import Vue from ‘vue’
import router from ‘…/router’
import * as Sentry from ‘@sentry/vue’
import { BrowserTracing } from ‘@sentry/tracing’
const needSentry = [‘pro’].includes(process.env.VUE_APP_ENV_STAGE)

// Sentry会捕获用户操作系统过程中在控制台上产生的错误信息
// 判断是否为生产环境,只有在生产环境才配置
if (!needSentry) return
Sentry.init({
Vue,
dsn: ‘填写Sentry对应的dsn地址’,
integrations: [
new BrowserTracing({
routingInstrumentation: Sentry.vueRouterInstrumentation(router),
// VUE_APP_BASE_URL对应后端api地址
tracingOrigins: [process.env.VUE_APP_BASE_URL]
})
],
tracesSampleRate: 1.0
})


### 3.`vue2.x`的底层原理(`vue刺客`)


#### a. `vue2.x`在初始化的过程中做了什么事?



下面是我在学习HTML和CSS的时候整理的一些笔记,有兴趣的可以看下:

![HTML、CSS部分截图](https://img-blog.csdnimg.cn/img_convert/2bb6b442a4432372fe5b9ccea2ad93d0.png)

### 进阶阶段



进阶阶段,开始攻 JS,对于刚接触 JS 的初学者,确实比学习 HTML 和 CSS 有难度,但是只要肯下功夫,这部分对于你来说,也不是什么大问题。

JS 内容涉及到的知识点较多,看到网上有很多人建议你从头到尾抱着那本《JavaScript高级程序设计》学,我是不建议的,毕竟刚接触 JS 谁能看得下去,当时我也不能,也没那样做。



我这部分的学习技巧是,增加次数,减少单次看的内容。就是说,第一遍学习 JS 走马观花的看,看个大概,去找视频以及网站学习,不建议直接看书。因为看书看不下去的时候很打击你学下去的信心。

然后通过一些网站的小例子,开始动手敲代码,一定要去实践、实践、实践,这一遍是为了更好的去熟悉 JS 的语法。别只顾着来回的看知识点,眼高手低可不是个好习惯,我在这吃过亏,你懂的。



**1、JavaScript 和 ES6**



在这个过程你会发现,有很多 JS 知识点你并不能更好的理解为什么这么设计,以及这样设计的好处是什么,这就逼着让你去学习这单个知识点的来龙去脉,去哪学?第一,书籍,我知道你不喜欢看,我最近通过刷大厂面试题整理了一份前端核心知识笔记,比较书籍更精简,一句废话都没有,这份笔记也让我通过跳槽从8k涨成20k。



![JavaScript部分截图](https://img-blog.csdnimg.cn/img_convert/cac778dc45492a41e2f3e7cd6b0134e5.png)



**2、前端框架**

前端框架太多了,真的学不动了,别慌,其实对于前端的三大马车,Angular、React、Vue 只要把其中一种框架学明白,底层原理实现,其他两个学起来不会很吃力,这也取决于你以后就职的公司要求你会哪一个框架了,当然,会的越多越好,但是往往每个人的时间是有限的,对于自学的学生,或者即将面试找工作的人,当然要选择一门框架深挖原理。

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/topics/618166371)**

以 Vue 为例,我整理了如下的面试题。



![Vue部分截图](https://img-blog.csdnimg.cn/img_convert/c6738a80c94640db83f7ffbf487ac5f0.png)

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值