自学几个月前端,为什么感觉什么都没学到??
这种现象在很多的初学者和自学前端的同学中是比较的常见的。
因为自学走的弯路是比较的多的,会踩很多的坑,学习的过程中是比较的迷茫的。
最重要的是,在学习的过程中,不知道每个部分该学哪些知识点,学到什么程度才算好,学了能做什么。
很多自学的朋友往往都是自己去找资料学习的,资料上有的或许就学到了,资料上没有的或许就没有学到。
这就会给人一个错误的信息就是,我把资料上的学完了,估计也-就差不多的了。
但是真的是这样的吗?非也,因为很多人找的资料就是很基础的。学完了也就是掌握一点基础的东西。分享给你一份前端分析路线,你可以参考。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
还有很多的同学在学习的过程中一味的追求学的速度,很快速的刷视频,写了后面忘了前面,最后什么都没有学到,什么都知道,但是什么都不懂,要具体说,也说不出个所以然。
所以学习编程一定要注重实践操作,练习敲代码的时间一定要多余看视频的时间。
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
- 对外mes系统首页index文件:
- 对内mes系统首页index文件:
当从对外mes首页点击按钮前往对内mes系统后,对内mes首页就会显示出对外mes传输过来的数据
复制代码
#### e. 插槽
[vue插槽官方文档](https://bbs.csdn.net/topics/618166371)
-
在子组件A中使用
<slot></slot>
进行占位
,父组件引入子组件A,在A下添加的内容
会自动转移
到插槽占位的地方
; -
如果
<slot></slot>
中有内容,如果在父组件不给子组件添加内容,那么就会展示插槽的默认内容;
需要特别注意的一点是,v-slot是无法挂载在slot上面的,有两种方式给默认插槽加上作用域:
a. <slot :row="personObj"></slot>
b. <slot v-bind="personObj"></slot>
区别在于,a方式传递给父组件的数据,外层由row包裹,而b方式传递给父组件的数据没有
-
如果未给
<slot></slot>
提供名称,那么该插槽的默认名称是default
; -
如果需要给
插槽
指定名称,直接对子组件使用name
命名即可;
<slot name="footer"></slot>
如果在父组件中,需要给对应
插槽
添加内容,则可以使用如下三种写法:此处如果不加:footer,则表示默认插槽,即vue会将父组件中未命名插槽的html代码隐式加上默认插槽。
<template #footer>
-
插槽
是有作用域
的,父组件中的插槽
内容无法访问子组件的内容,除非通过作用域插槽
的方式进行传递:子组件:
<slot name="footer" :row="row"></slot>
父组件:
<template #footer=“{ row }”>
在这里
扩展
一个代码的优化点,v-if
的代码可以使用template
包裹,语义
会更加清晰。 -
如果组件只有一个
插槽
,则在父组件上,可以直接使用插槽
语法,而不需要template
标签嵌套。 -
自定义组件内部的
$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语法糖
-
在未加
.sync
的情形下,:props
是单向绑定。在不自定义v-model
的前提下,v-model
其实是v-model:value
的简写 -
等价于
<input :value=“searchText” @input=“searchText = $event.target.value” />
-
二次封装组件
时,如果需要对双向绑定的值做处理,可以将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
组件内的路由守卫进行讨论:
- 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)]