昨天给组长姐姐看我写的代码,她表示很不戳,但要让我来总结一下学到什么东西,那我就把实习这段时间学到东东的放在这里吧
# 节流防抖
# 防抖 (debounce)
单位时间内,频繁触发事件,只执行最后一次
使用场景
搜索框输入,手机号,邮箱验证输入检测
思路
核心是利用 setTimeout 定时器来实现
- 声明定时器变量
- 每次事件触发时要先判断是否有定时器,如果有,先清除以前的定时器
- 如果没有定时器,则开启定时器,存入到定时器变量中
- 定时器里面写函数调用
function debounce ( fn , time ){ | |
let timer | |
//return 返回一个匿名函数 如果不返回就只会执行一次 | |
return function () { | |
// 判断是否有定时器,如果有就清除定时器 | |
if (timer) clearTimeout(timer) | |
// 没有则添加定时器 | |
timer = setTimeout(()=>{ | |
// 触发定时器里面的函数 | |
fn() | |
},time) | |
} | |
} |
# 节流 (throttle)
单位时间内,频繁触发事件,只执行一次
使用场景
高频事件:鼠标移动 mousemove, 页面尺寸缩放 resize,滚动条滚动 scroll 等等
思路
节流的同样是利用定时器来实现
- 声明一个定时器变量
- 当事件触发都先判断是否有定时器了,如果有定时器,则不开启新的定时器
- 如果没有定时器则开启定时器,记得存到变量里面
- 定时器里面调用执行的函数,定时器里面要把定时器清空
function throttle(fn,time){ | |
let timer = null | |
return function(){ | |
if (!timer) { | |
timer = setTimeout(()=>{ | |
fn() | |
//setTimeout 无法删除定时器,因为定时器还在运作,所以不用 clearTimeout | |
timer= null | |
},time) | |
} | |
} | |
} |
# 使用 lodash 节流防抖
安装 lodash
npm i --save lodash
按需导入 lodash
import { debounce } from 'lodash'
用这种姿势来使用
<button @click="addCount">+1</button> |
addCount: debounce(function () { | |
this.count++ | |
}, 1000) |
ps: 箭头函数中没有自己的 this 值,所以用普通函数来实现
# 点击按钮实现网页全屏效果
在 Vue 中你可以通过使用 @click 事件和 Fullscreen API 来实现点击按钮全屏网页的功能。以下是一个示例:
<template> | |
<div> | |
<button @click="toggleFullScreen">全屏</button> | |
</div> | |
</template> | |
<script> | |
export default { | |
methods: { | |
toggleFullScreen() { | |
var elem = document.documentElement; | |
if (!document.fullscreenElement && !document.mozFullScreenElement && | |
!document.webkitFullscreenElement && !document.msFullscreenElement) { | |
if (elem.requestFullscreen) { | |
elem.requestFullscreen(); | |
} else if (elem.mozRequestFullScreen) { // Firefox | |
elem.mozRequestFullScreen(); | |
} else if (elem.webkitRequestFullscreen) { // Chrome, Safari and Opera | |
elem.webkitRequestFullscreen(); | |
} else if (elem.msRequestFullscreen) { // IE/Edge | |
elem.msRequestFullscreen(); | |
} | |
} else { | |
if (document.exitFullscreen) { | |
document.exitFullscreen(); | |
} else if (document.mozCancelFullScreen) { // Firefox | |
document.mozCancelFullScreen(); | |
} else if (document.webkitExitFullscreen) { // Chrome, Safari and Opera | |
document.webkitExitFullscreen(); | |
} else if (document.msExitFullscreen) { // IE/Edge | |
document.msExitFullscreen(); | |
} | |
} | |
} | |
} | |
}; | |
</script> |
# 父组件调用子组件方法
- 在父组件内部给子组件标签添加
ref
属性
<son ref="son"></son> |
- 通过
ref
属性来调用子组件的方法。
this.$refs.son.sonFn() |
父组件
<template> | |
<div id="app"> | |
<div class="fatherBox"> | |
<button @click="fatherBtn">父组件的按钮</button> | |
</div> | |
<mySon ref="son"></mySon> | |
</div> | |
</template> | |
<script> | |
import mySon from '@/components/mySon.vue'; | |
export default { | |
name: 'app', | |
components: { | |
mySon | |
}, | |
methods: { | |
fatherBtn() { | |
// 使用子组件的方法 | |
this.$refs.son.addCount() | |
} | |
}, | |
} | |
</script> |
子组件
<template> | |
<div id="son"> | |
<div>我是子组件的值:<!--swig0--></div> | |
</div> | |
</template> | |
<script> | |
export default { | |
name:'mySon', | |
data() { | |
return { | |
count: 0, | |
} | |
}, | |
methods: { | |
addCount() { | |
this.count++ | |
} | |
} | |
} | |
</script> |
# Mixins 混入
作用:复用代码,Mixins 可以定义所有组件里面可以定义的东西 例如 data,methods,created 等等
使用方式:
- 首先在 src 目录下新建 Mixins 文件
- 在 Mixins 文件中输入想要实现的功能并导出
export const showBox = { | |
data() { | |
return { | |
isShow: true | |
} | |
}, | |
methods: { | |
showBox() { | |
this.isShow = !this.isShow | |
} | |
} | |
} |
- 导入 Mixins 并且注册
import mySon from '@/components/mySon.vue'; | |
export default { | |
mixins: [showBox], | |
} |
- 直接使用 Mixins 的方法即可
<button @click="showBox">显示或者隐藏盒子</button> | |
<div class="box" v-if="isShow"></div> |
- 子组件复用代码 使用同样步骤
完整代码
mixins 代码
export const showBox = { | |
data() { | |
return { | |
isShow: true | |
} | |
}, | |
methods: { | |
showBox() { | |
this.isShow = !this.isShow | |
} | |
} | |
} |
父组件使用代码
template> | |
<div id="app"> | |
<button @click="showBox">显示或者隐藏盒子 <!--swig1--></button> | |
<div class="box" v-if="isShow"></div> | |
<mySon></mySon> | |
</div> | |
</template> | |
<script> | |
import { showBox } from '@/Mixins/showBox' | |
import mySon from '@/components/mySon.vue'; | |
export default { | |
components: { mySon }, | |
name: 'app', | |
mixins: [showBox], | |
} | |
</script> |
子组件使用代码
<template> | |
<div id="son"> | |
<button @click="showBox">显示或隐藏子组件的盒子</button> | |
<div class="box" v-if="isShow">我是子组件的盒子</div> | |
</div> | |
</template> | |
<script> | |
import { showBox } from '@/Mixins/showBox' | |
export default { | |
name: 'mySon', | |
mixins: [showBox], | |
} | |
</script> |
# computed 和 watch 的区别
- 计算属性可以像普通属性一样在模板中使用,侦听器不行
- 计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值
- 侦听器监测的是属性值,如果属性值发生变化,都会触发执行回调函数来执行一系列操作,侦听器也可以监测计算属性
- 计算属性无法执行异步任务,只能同步执行,也就是说计算属性不能向服务器请求或者执行异步任务,侦听器可以。
watch: { | |
async username(newVal, oldVal) { | |
await axios.get() | |
}, | |
} |
# 闭包的使用,了解其优缺点
概念: 一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域
闭包的作用: 封闭数据,提供操作,外部也可以访问函数内部的变量
简单理解: 闭包 = 内层函数 + 外层函数的变量
闭包的缺点: 常驻内存,会增大内存使用量,使用不当会造成内存泄漏
闭包的基本格式
function outer () { | |
let a = 10 | |
function fn () { | |
console.log(a) | |
} | |
return fn() | |
} | |
// 外面要使用这个 10 | |
const fun = outer() | |
fn() | |
// 外层函数使用内层函数的变量 |
# vh vw px rem em upx rpx 的关系与区别
# vw 和 vh
vw 和 vh 是 CSS 中用于定义相对长度的单位
vw 代表视口宽度的百分比单位,1vw 等于视口宽度的 1%,例如视口宽度为 1000px,1vw 等于 10px
# em 和 rem
em 和 rem 是 CSS 中用于定义相对长度的单位,它们对于元素的字体大小进行计算。
em:相对于父元素的字体大小进行计算,如果一个元素的字体大小为 16px,设置它的宽度为 2em,那么它的宽度将是 32px (2 * 16)
rem:相对于根元素(通常指 html 元素)的字体大小进行计算,如果根元素的字体大小为 16px,设置一个元素的宽度为 2rem,那么它的宽度也将是 32px (2 * 16)。rem 单位主要用于构建响应式布局。
相比 em,rem 具有更好的可控性,当使用 em 单位时,嵌套元素会继承父元素的字体大小,并可能导致计算机混乱。而 rem 的优点就是可以通过修改 html 里面的文字大小来改变页面中元素的大小,可以整体控制。
# upx 和 rpx
upx(设备像素)和 rpx(响应式像素)是微信小程序中常用的长度单位。
upx:设备像素,是物理像素的概念,即设备屏幕上的实际像素点。在微信小程序中,1 个 upx 等于 1 个物理像素的宽度。
rpx:响应式像素,是一种相对长度单位,是微信小程序为了适配不同设备屏幕而提出的概念。在微信小程序中,1 个 rpx 可以根据屏幕宽度进行自适应转换,假设屏幕宽度为 750rpx,那么屏幕宽度为 375px 时,1rpx 即为 1 物理像素。
# DOM 事件流
事件流描述的是从页面接收事件的顺序
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即为 DOM 事件流
DOM 事件流分位 3 个阶段:
- 捕获阶段
- 当前目标阶段
- 冒泡阶段
冒泡事件
它指的是当一个元素触发了某个事件,比如点击事件,这个事件将会从最内层的元素开始触发,然后逐级向父级元素传递,直到传递到最外层的元素。
捕获事件
在捕获事件模型中,事件从最外层的元素开始传递,逐级向内层元素传递,直到传递到最内层的元素。
注意
- js 代码只能执行捕获或者冒泡其中一个阶段。
- onclick 和 attachEvent 只能得到冒泡阶段
- addEventListener 第三个参数如果是 true 表示在事件捕获阶段调用事件处理程序,如果是 false 或者不写 (默认 false), 表示在事件冒泡阶段调用事件处理程序
- 实际开发中更关注事件冒泡
案例
捕获阶段:
- document → html → body → father → son
- 点击子盒子,先执行父盒子事件,后执行子盒子事件
冒泡阶段:
- son → father → body → html → document
- 点击子盒子,先执行子盒子事件,再执行父盒子事件
阻止冒泡事件
可以在事件处理程序中使用 event.stopPropagation()
方法。该方法可以阻止事件进一步向上冒泡传递,使事件仅在当前元素上触发。
son.addEventListener('click', function (e) { | |
alert('son') | |
e.stopPropagation() | |
}) | |
father.addEventListener('click', function () { | |
alert('father') | |
}) |
vue 中阻止冒泡事件
vue 可以通过使用修饰符 .stop
来阻止事件冒泡。
<template> | |
<div class="test"> | |
<div class="father" @click="father"> | |
<div @click.stop="son" class="son"></div> | |
</div> | |
</div> | |
</template> | |
<script> | |
export default { | |
name: 'welcomeIndex', | |
methods: { | |
father () { | |
alert('father') | |
}, | |
son () { | |
alert('son') | |
} | |
} | |
} | |
</script> |
# 接口请求方式
使用 axios 代码进行演示
GET
用于从服务器获取数据。GET 请求将数据作为 URL 的一部分发送到服务器,并返回相应的响应数据。GET 请求通常用于获取资源,不应该用于发送敏感数据,因为数据会以明文形式出现在 URL 中。
axios.get('http://localhost:3000/user').then(res => { | |
console.log(res) | |
}) |
POST
用于向服务器发送数据。POST 请求将数据作为请求的正文发送到服务器,通常用于提交表单数据或者发送较大的数据。POST 请求相对安全,因为数据不会以明文形式出现在 URL 中
axios.post('http://localhost:3000/user', { | |
username: 'sadMan', | |
name: '木子李', | |
age: 20 | |
}).then(res => { | |
console.log(res) | |
}) |
PUT
用于向服务器更新资源。PUT 请求将数据作为请求的正文发送到服务器,用于更新特定 URL 指定的资源。PUT 请求通常用于更新整个资源
DELETE
用于从服务器删除资源。DELETE 请求用于删除特定 URL 指定的资源。
PATCH
用于对服务器资源进行部分更新。PATCH 请求用于更新特定 URL 指定的资源的一部分内容。
# 状态码
在 JavaScript 中,可以通过 HTTP 响应的状态码来获取请求的执行结果。HTTP 状态码是一个三位数的数字,用于表示服务器响应的状态和结果
200: 请求成功,服务器成功处理了请求,并返回所需的数据
201: 请求已创建,服务器成功处理了请求,并创建了新的资源
204: 请求成功,服务器成功处理了请求,但没有返回任何内容
400: 请求错误,服务器无法理解请求,通常是由于请求参数有误或语法错误导致
401: 未授权,需要进行身份验证或缺乏有效凭据
403: 访问被禁止,服务器理解请求,但拒绝执行,通常由于权限问题导致
404: 资源未找到,服务器未找到请求的资源
500: 服务器内部错误,服务器在请求时遇到了错误
503: 服务不可用。服务器当前无法处理请求,通常是由于临时过载或维护导致的。
# 请求头
HTTP 头字段(HTTP header fields), 是指在超文本传输协议(HTTP)的请求和响应消息中的消息头部分
它们定义了一个超文本传输协议事务中的操作参数
HTTP 头部字段可以自己根据需要定义,因此可能在 Web 服务器和浏览器上发现非标准的头字段
字段名 | 说明 | 示例 |
---|---|---|
Authorization | 用于进行身份验证,传递访问令牌或凭据。常见的值为 Bearer token ,其中 toke 为访问令牌 | Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
Accept | 能够接受的回应内容类型(Content-Types) | Accept: text/plain |
Accept-Charset | 能够接受的字符集 | Accept-Charset: utf-8 |
Cookie | 服务器通过 Set- Cookie (下文详述)发送的一个 超文本传输协议 Cookie | Cookie: $Version=1; Skin=new; |
Date | 发送该消息的日期和时间 | Date: Tue, 15 Nov 1994 08:12:31 GMT |
# Git
# Git 仓库
Git 仓库是记录文件状态内容的地方,存储着修改的历史记录
- 可以把本地文件夹转化为 Git 仓库
git init
- 从其他地方克隆 git 仓库
git clone xxx.com
# Git 的三个区域
- 工作区:实际开发时操作的文件夹
- 缓存区:保存之前的准备区域 (暂存改动过的文件)
- 版本库:提交并保存暂存区中的内容,产生一个版本快照
命令 | 作用 |
---|---|
git add 文件名 | 暂存指定文件 |
git add . | 暂存所有改动的文件 |
git ls-files | 查看当前项目下暂存区记录了那些文件 |
git commit -m "注释说明" | 提交并保存,产生版本快照 |
# Git 文件状态
git status -s
可以查看文件状态
文件状态 | 概念 | 场景 |
---|---|---|
未跟踪 U | 从未被 Git 管理过 | 新文件 |
新添加 A | 第一次被 Git 缓存 | 之前版本记录没有此文件 |
已修改 M | 工作区内容变化 | 修改了内容产生 |
# 回退版本
把版本库某个版本对应的内容快照,恢复到工作区和暂存区
查看提交历史: git log --oneline
查看完整提交日志 git reflog --oneline
git reset --soft 版本号
git reset --hard 版本号
强制覆盖工作区和暂存区的命令git reset --mixed 版本号
# 分支
创建分支命令: git branch 分支名
切换分支命令: git checkout 分支名
查看当前分支: git branch
合并其他分支: git merge 分支名
删除合并后的分支指针: git branch -d login-bug
# 远程仓库
- 新建仓库得到远程仓库 git 地址
- 本地 git 仓库添加远程仓库原点地址 命令:
git remote add 远程仓库别名 远程仓库地址
- 本地 git 仓库推送版本记录到远程仓库 命令:
git push -u 远程仓库别名 本地和远程分支名
# 上传图片并预览
这里有个上传文件的按钮
<input type="file" id="fileInput" accept="image/*" onchange="previewImage(event)"> | |
<img id="preview" src="#" alt="预览图片" style="max-width: 300px; max-height: 300px;"> |
<script> | |
function previewImage(e) { | |
// 获取选择的文件 | |
var file = e.target.files[0]; | |
if (file) { | |
// 创建一个 FileReader 对象 | |
var reader = new FileReader() | |
// 当文件读取完成时,触发 onload 事件 | |
reader.onload = function(e) { | |
// 获取预览图片的元素 | |
var previewImage = document.getElementById('preview'); | |
// 将读取到的文件内容(base64 编码)赋值给预览图片的 src 属性 | |
previewImage.src = e.target.result; | |
} | |
// 读取文件内容,结果将在 onload 事件中处理 | |
reader.readAsDataURL(file); | |
} | |
} | |
</script> |
# elementui 中的 upload
由于饿了么自带的上传图片非常好看,所以我大概看了一下文档,发现他和我想要的上传图片不太一样。
饿了么的上传,点击文件就会立马上传
不想让它立马上传的话,可以将组件属性 action 改为空 ,再将 auto-upload 设置成 false 就行
<el-upload action=" " :auto-upload="false"> | |
<img v-if="imageUrl" :src="imageUrl" class="avatar"> | |
<i v-else class="el-icon-plus avatar-uploader-icon"></i> | |
</el-upload> |
下一步是预览图片,我们可以使用组件中的 on-change
钩子 官方文档说:文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用,它里面有两个参数 function(file, fileList)
所以我们可以通过 on-change 钩子来获得上传的图片文件
以下是完整代码
<!-- action 必选参数,上传的地址 --> | |
<!-- on-change 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用 --> | |
<!-- auto-upload 是否在选取文件后立即进行上传 --> | |
<!-- accept 接受上传的文件类型 --> | |
<!-- show-file-list 是否显示已上传文件列表 --> | |
<el-form-item label="文章封面" prop="cover_img"> | |
<el-upload class="avatar-uploader" action="" :on-change="getFile" :auto-upload="false" accept=".jpg, .png":show-file-list="false"> | |
<img v-if="imageUrl" :src="imageUrl" class="avatar"> | |
<i v-else class="el-icon-plus avatar-uploader-icon"></i> | |
</el-upload> | |
</el-form-item> |
同时在方法中判断文件类型,大小,也可以将文件转换成 base64 格式的字符串
// 文件状态改变时的钩子 | |
getFile (file) { | |
// 判断文件类型是否为图片 | |
const isJPG = file.raw.type === 'image/jpeg' || 'image/png' | |
// 判断图片类型是否小于 2m | |
const isLt2M = file.size / 1024 / 1024 < 2 | |
if (!isJPG) { | |
return this.$message.error('上传头像图片只能是 JPG 或 PNG 格式!') | |
} | |
if (!isLt2M) { | |
return this.$message.error('上传头像图片大小不能超过 2MB!') | |
} | |
// 设置图片预览 | |
this.imageUrl = URL.createObjectURL(file.raw) | |
this.pubForm.cover_img = file.raw | |
// 重新校验表单 | |
this.$refs.pubFormRef.validateField('cover_img') | |
// 如果你想的话还可以将文件转换成 base64 格式的 | |
const fr = new FileReader() | |
fr.readAsDataURL(file.raw) | |
fr.onload = function (e) { | |
// 这个就是 base64 的字符串啦 | |
console.log(e.target.result) | |
} | |
} |
# FormData
FormData 类是 HTML5 新出的专门为了装文件和其他的参数的一个容器
后端接口 form-data
pubForm: { | |
// 表单的数据对象 | |
title: '', | |
cate_id: '', | |
content: '', // 文章内容 | |
cover_img: null, // 用户选择的封面图片(null 表示没有选择任何封面) | |
state: '' // 文章的发布状态,可选值有两个:草稿、已发布 | |
}, | |
const fd = new FormData() | |
fd.append('title', this.pubForm.title) | |
fd.append('cate_id', this.pubForm.cate_id) | |
fd.append('content', this.pubForm.content) | |
fd.append('state', this.pubForm.state) | |
fd.append('cover_img', this.pubForm.cover_img) | |
// 在对 formData 使用 append 方法添加数据后,console 输出仍为空 | |
// console.log(fd) | |
// 需要用 formData.get ("键") 的方法获取值 | |
// console.log(fd.get("title")) | |
const { data: res } = await addArticleApi(fd) | |
console.log(res) |
简写
使用 Object.keys(this.pubForm)
获取 this.pubForm
对象的所有属性名,并使用 forEach 方法遍历这些属性名。
在遍历过程中,对于每个属性名 key,使用 fd.append(key, this.pubForm[key])
将属性名和对应的属性值添加到 FormData 对象 fd 中。
Object.keys(this.pubForm).forEach((key) => { | |
fd.append(key, this.pubForm[key]) | |
}) |