【面试题】你都必须得掌握的vue知识_vuejs 插槽会不会影响内存回收(1)

自学几个月前端,为什么感觉什么都没学到??


这种现象在很多的初学者和自学前端的同学中是比较的常见的。

因为自学走的弯路是比较的多的,会踩很多的坑,学习的过程中是比较的迷茫的。

最重要的是,在学习的过程中,不知道每个部分该学哪些知识点,学到什么程度才算好,学了能做什么。

很多自学的朋友往往都是自己去找资料学习的,资料上有的或许就学到了,资料上没有的或许就没有学到。

这就会给人一个错误的信息就是,我把资料上的学完了,估计也-就差不多的了。

但是真的是这样的吗?非也,因为很多人找的资料就是很基础的。学完了也就是掌握一点基础的东西。分享给你一份前端分析路线,你可以参考。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

还有很多的同学在学习的过程中一味的追求学的速度,很快速的刷视频,写了后面忘了前面,最后什么都没有学到,什么都知道,但是什么都不懂,要具体说,也说不出个所以然。

所以学习编程一定要注重实践操作,练习敲代码的时间一定要多余看视频的时间。

    parent = parent.$parent
    if (parent) name = parent.$options.name
  }

  if (parent) {
    parent.$emit(eventName, params)
  }
},

broadcast(componentName, eventName, params) {
  broadcast.call(this, componentName, eventName, params)
}

}
}
复制代码



子组件:

复制代码



父组件:

复制代码



  4. 两个页面之间进行通讯 ( 使用`postMessage`或者`实时通讯websocket`等):



前端实时通信的方式: https://www.jb51.net/article/246674.htm
postMessage通信方式详解: https://blog.csdn.net/huangpb123/article/details/83692019
阮一峰websocket详解:http://www.ruanyifeng.com/blog/2017/05/websocket.html
websocket插件:https://github.com/joewalnes/reconnecting-websocket
黑马websocket:https://www.bilibili.com/video/BV14K411T7cd?p=6&spm_id_from=pageDriver&vd_source=a540d41ff453db4580db0168b87afe38

  1. 对外mes系统首页index文件:
欢迎来到对外系统
  1. 对内mes系统首页index文件:
    当从对外mes首页点击按钮前往对内mes系统后,对内mes首页就会显示出对外mes传输过来的数据
欢迎来到对内系统
{{ receivedData }}

复制代码


#### e. 插槽


[vue插槽官方文档](https://bbs.csdn.net/topics/618166371)



  1. 在子组件A中使用<slot></slot>进行占位,父组件引入子组件A,在A下添加的内容自动转移插槽占位的地方

  2. 如果<slot></slot>中有内容,如果在父组件不给子组件添加内容,那么就会展示插槽的默认内容;
    需要特别注意的一点是,v-slot是无法挂载在slot上面的,有两种方式给默认插槽加上作用域:
    a. <slot :row="personObj"></slot>
    b. <slot v-bind="personObj"></slot>
    区别在于,a方式传递给父组件的数据,外层由row包裹,而b方式传递给父组件的数据没有

  3. 如果未给<slot></slot>提供名称,那么该插槽的默认名称是default;

  4. 如果需要给插槽指定名称,直接对子组件使用name命名即可;
    <slot name="footer"></slot>

    如果在父组件中,需要给对应插槽添加内容,则可以使用如下三种写法:

    此处如果不加:footer,则表示默认插槽,即vue会将父组件中未命名插槽的html代码隐式加上默认插槽。


<template #footer>

  1. 插槽是有作用域的,父组件中的插槽内容无法访问子组件的内容,除非通过作用域插槽的方式进行传递:

    子组件:
    <slot name="footer" :row="row"></slot>

    父组件:

    <template #footer=“{ row }”>

    在这里扩展一个代码的优化点,v-if的代码可以使用template包裹,语义会更加清晰。

  2. 如果组件只有一个插槽,则在父组件上,可以直接使用插槽语法,而不需要template标签嵌套。

  3. 自定义组件内部的$scopedSlots记载了组件的作用域插槽信息,以key(插槽名)-value(对应函数。指定key值, 执行得到Vnode数组,对应$slots,一般更推荐使用$scopedSlots)的形式出现。因此,根据这个特性,
    有多个具有插槽的组件定义在一个自定义组件中时,可以通过遍历的方式动态添加插槽

(1) 使用$scopedSlots封装组件,动态遍历插槽(以多个具有插槽的组件为例):

const INPUT_SLOT_LIST = [‘prefix’, ‘empty’]

computed: {
// 获取select组件和tree组件的插槽
allSlots({ $scopedSlots }) {
const inputScopedSlots = {}
const treeScopedSlots = {}
for (let key in $scopedSlots) {
const val = $scopedSlots[key]
if (INPUT_SLOT_LIST.includes(key)) inputScopedSlots[key] = val
else treeScopedSlots[key] = val
}
return {
inputScopedSlots,
treeScopedSlots
}
}
}

(2) 使用$slots封装组件,动态遍历插槽(以el-input组件的二次封装为例):

  `a. 动态插槽:`
  
  <el-input v-bind='$attrs' v-on="$listeners">
    <template #[slotName] v-for="(slot, slotName) in $slots">
      <slot :name="slotName" /> 
    </template>
  </el-input>
  
  `使用el-input中定义好的插槽:`
  
  <customInput placeholder="请输入内容" v-model="value">
    <el-button slot="append" icon="el-icon-search"> </el-button> 
  </customInput>
  
  `如果需要给slot插槽上的点击事件传递本组件的方法,直接绑定点击事件是行不通的,现有两种方法:
  
   方法一:在slot插槽上添加一个div父级容器,并绑定点击事件@click="需要传入的本组件中的方法" 
   方法二:直接在slot上传递一个:onClick="定义的函数clickHandler"。父组件引入后,在插槽中解构出
   clickHandler,然后再绑定点击事件@click="点击事件方法(定义的函数clickHandler)"
   `
  
   `children.vue
   
    <template>
      <div>
        <h3>插槽$slots的用法</h3>
        <slot name="header"></slot>
        <slot name="main"></slot>
        <slot name="footer"></slot>
      </div>
    </template>
   `
  
   `parent.vue
   
   <template>
     <children>
       <template #[slotName] v-for="(slot, slotName) in $scopedSlots">
         <slot :name="slotName" :clickHandler="clickHandler" />
       </template>
     </children>
   </template>

   <script>
   import children from './children'

   export default {
     components: { children },
     
     methods: {
       clickHandler() {
         console.log('插槽被调用了呢')
       }
     }
   }
   </script>
   `
  
   `index.vue
   <template>
     <parent>
       <template #main>主体区域</template>
       <template #footer="{ clickHandler }">
         <div @click="click('footer', clickHandler)">尾部区域</div>
       </template>
     </parent>
   </template>

    <script>
    import parent from './parent.vue'

    export default {
      components: { parent },

      methods: {
        click(type, clickHandler) {
          console.log(`我是${type}插槽`)
          clickHandler()
        }
      }
    }
    </script>
   `
  
  `b. 动态作用域插槽:(特别注意) $slots无法获取具名作用域插槽, 作用域插槽只能用$scopedSlots获取`
  
  <el-input v-bind='$attrs' v-on="$listeners">
    <template #[slotName]="slotProps" v-for="(slot, slotName) in $scopedSlots">
      <slot :name="slotName"  v-bind="slotProps"/> 
    </template>
  </el-input>

复制代码


#### f. vueX


  [vueX黑马笔记](https://bbs.csdn.net/topics/618166371)



在平时的项目中,为了代码看上去不是那么臃肿,一般会使用多个store文件来维护vueX,比如product.js, order.js...
并可以通过函数的方式拿到vueX中存储的数据

computed: {
…mapState({
has_image_gallery: (state) => state.customizer.has_image_gallery,
library: (state) => state.myImage.library.list,
meta: (state) => state.myImage.library.pagination,
last_page: (state) => state.myImage.library.pagination.last_page
})
}
复制代码


#### g. 指令(以回到顶部组件说明)


  [指令的官方介绍](https://bbs.csdn.net/topics/618166371)



自定义指令中的第三个参数vnode的context记载了组件的一些信息,这个是我们比较需要关注的

1. 使用自定义指令,实现回到顶部的效果:

添加全局公共样式:
.scroll-top-class {
position: fixed;
bottom: 120px;
right: 30px;
opacity: 0;
height: 40px;
width: 40px;
line-height: 40px;
font-size: 30px;
text-align: center;
color: #ddd;
opacity: 0;
z-index: 2021;
cursor: pointer;
border-radius: 50%;
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进行全局注册: 使用`vue.use(xx)`,就会调用`xx`里面的`install`方法



lodopPrintPdf.js

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

手动将PrintBtn这个js对象转换为vue实例,这也是vue内部将对象文件转换为vue实例的过程
export default async function lodopPrintPdf(option) {
使用vue构造器,创建一个vue的子类,及子类构造器
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
})
复制代码



需要在main.js中引入该js,并使用vue.use(xx), 就可以将该组件注册为全局组件

import CrudInput from ‘…/crud/src/crud-input’

/* istanbul ignore next */
CrudInput.install = function (Vue) {
Vue.component(CrudInput.name, CrudInput)
}

export default CrudInput
复制代码


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


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


  **类:** **在混入中定义的变量和方法,很容易与`vue`文件中定义的变量和方法冲突,从而被`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`(**孙子组件和子组件中定义的props会排除在外**)。
* `v-on="$listeners"`是把父组件的事件绑定在子组件上。因此,会有一种减少代码的小技巧。假定有这么一种情形:有祖先组件`A`, 父组件`B`和子组件`C`,在组件`c`中某个元素被点击时,需要将事件逐级向上传递到组件组件`A`中。常规解决思路是,逐级向上`$emit`事件。但有一种简便的思路是,我们可以利用`v-on="$listeners"`,将它绑定在父组件`B`上,这样就可以不用在父组件`B`上再监听子组件`C`传递而来的事件。






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



  1. 在未加.sync的情形下,:props是单向绑定。在不自定义v-model的前提下,v-model其实是v-model:value的简写

  2. 等价于

    <input :value=“searchText” @input=“searchText = $event.target.value” />

  3. 二次封装组件时,如果需要对双向绑定的值做处理,可以将v-model拆开:
    比如有这么一种需求,需要el-input-number组件,在给null默认值或空字符串时,会显示为0

使用:num和子组件的是value双向绑定的
<cz-input-number placeholder=‘请输入数量’ @change=“change” v-model=“num”>
复制代码


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


 


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



#### n. 递归组件 && 动态组件


* **递归组件:**


  何为`递归组件`? `递归组件`就是通过`调用组件自身`来实现递归。因此递归组件需要提供`name`属性和`递归条件`**(比如是否为数组)**,方便自己调用。这种组件主要用于处理一些需要递归的数据,最普遍的比如`树状结构`。



利用递归组件实现el-tree的基础功能:https://juejin.cn/post/7056922161788747789

1. 子组件:

2. 父组件:

<my-tree :tree-data=“treeData” @node-click=“nodeClick”>

复制代码


**效果浏览:**


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


* **动态组件:**


  任意标签上添加`:is`属性,就会成为一个`动态组件`。此时,给`is`属性添加上引入的组件名称,就会根据`is`的当前值来动态切换组件。但为了语义化,我们最好将这个标签命名为`component`。**值得注意的是,组件切换的过程中会销毁上一个组件,每次进入新组件,都会触发新组件的生命周期。为了解决这一问题,我们可以使用`keep-alive`对动态组件进行缓存。**



复制代码 ```
o. 路由守卫

vue官网——路由守卫

在此,只对vue组件内的路由守卫进行讨论:

  • beforeRouteEnter:

(1) 进入组件前调用,此时组件还未渲染,所以该路由守卫中不存在this;

(2) 但是如果我们一定要获取vue的实例,我们可以给该路由守卫中的参数next,添加一个函数回调,这个函数回调的参数就是当前vue的实例,也就是this

  beforeRouteEnter(to, from, next) {
    next((vm)=>{
      console.log(vm);
    })
  }
复制代码
  • beforeRouteLeave: 离开当前组件后触发,此时存在this
beforeRouteLeave(to, from, next) { 
  // console.log(this); 
  next(); 
}
复制代码
  • beforeRouteUpdate: 同一组件路由传参发生变化时才触发, 也存在this
beforeRouteUpdate(to, from, next) { 
  // console.log(this); 
  next(); 
}
复制代码
p. 关于vue中this的基础知识(特别基础)

总结

技术学到手后,就要开始准备面试了,找工作的时候一定要好好准备简历,毕竟简历是找工作的敲门砖,还有就是要多做面试题,复习巩固。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

:is=“component”>

复制代码


#### o. 路由守卫


[vue官网——路由守卫](https://bbs.csdn.net/topics/618166371)


  在此,只对`vue`组件内的路由守卫进行讨论:


* beforeRouteEnter:


   **(1) 进入组件前调用,此时组件还未渲染,所以该路由守卫中不存在`this`;**


   **(2) 但是如果我们一定要获取`vue`的实例,我们可以给该路由守卫中的参数`next`,添加一个函数回调,这个函数回调的参数就是当前`vue`的实例,也就是`this`。**



beforeRouteEnter(to, from, next) {
next((vm)=>{
console.log(vm);
})
}
复制代码


* beforeRouteLeave: **离开当前组件后触发,此时存在`this`**



beforeRouteLeave(to, from, next) {
// console.log(this);
next();
}
复制代码


* beforeRouteUpdate: **同一组件路由传参发生变化时才触发, 也存在`this`**



beforeRouteUpdate(to, from, next) {
// console.log(this);
next();
}
复制代码


#### p. 关于vue中this的基础知识(特别基础)


### 总结

>技术学到手后,就要开始准备面试了,找工作的时候一定要好好准备简历,毕竟简历是找工作的敲门砖,还有就是要多做面试题,复习巩固。

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

[外链图片转存中...(img-ZPa4BEUB-1715220860252)]

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值