工作笔记--css文本溢出,盒子模型,NextTick回顾、console调试、样式穿透deep全家,el-upload文件上传,.9图

记录工作中的一些笔记,以便日后回顾

1. 单行文本溢出省略

  1. 核心 CSS 语句
  • overflow: hidden;(文字长度超出限定宽度,则隐藏超出的内容)
  • white-space: nowrap;(设置文字在一行显示,不能换行)
  • text-overflow: ellipsis;(规定当文本溢出时,显示省略符号来代表被修剪的文本)
css 复制代码
<div class="wrap">
  <div class="content">测试数据:css单行文本超出长度显示省略号--明天也要努力</div>
  <div class="content text-ellipsis">测试数据:css单行文本超出长度显示省略号--明天也要努力</div>
</div>

<style>
.wrap{
  width: 200px;
  height: 200px;
}
.content{
  width: 100%;
  margin-top: 6px;
}
.text-ellipsis{
  overflow:hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  -o-text-overflow:ellipsis;
}
</style>
  1. v-html使用时,省略样式失效,可以使用filter。
js 复制代码
filters: {
    //当渲染的文字超出9字后显示省略号
    ellipsis(value) {
      value = value.replace(/<.*?>/gi, '') //把v-html的格式标签替换掉
      if (value.length > 50) {
        return value.slice(0, 50) + '...'
      } else {
        return value
      }
    },
  },

2. 回顾CSS盒子模型

标准模型和IE模型的区别,如何设置标准模型、IE模型

  1. 两者的区别在于content的不同,
  • IE盒模型的content包括border、padding
  • 标准模型元素宽度width=content,高度计算相同
  1. 切换
css 复制代码
html * {
	box-sizing: border-box;  // 设置为IE模型
  box-sizing: contetn-box; // 设置为标准模型
}

3. 关于vue的 this.$refs.xxx 打印为undefined解决办法

今天做项目的时候发现,明明this.$refs里面有东西,如下图,但是打印this.$refs.xxx的时候却是undefined,

ref 注册子组件,父组件可以通过this.$refs.xx.fn调用子组件里的函数, vue 官网中ref 下有一段话

  • 关于ref 注册时间的重要说明:因为 ref 本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们
  • 它们还不存在!$refs 也不是响应式的,因此你不应该试图用它在模板中做数据绑定。

解决方法:

1、如果你在mounted或者created里获取this.$refs,因为dom还未完全加载,所以你是拿不到的, updated 阶段则是完成了数据更新到 DOM 的阶段(对加载回来的数据进行处理),此时,就可以使用this.$refs了。

2、如果写在(方法)method中,那么可以使用 this.$nextTick(() => {})等页面渲染好再调用this.$refs.xxx,这样就可以了。如下图

4. NextTick回顾

官方对其的定义

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM

什么意思呢?

我们可以理解成,Vue 在更新 DOM 时是异步执行的。当数据发生变化,Vue将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新 举例一下

Html结构

html 复制代码
<div id="app"> {{ message }} </div>

构建一个vue实例

js 复制代码
const vm = new Vue({
  el: '#app',
  data: {
    message: '原始值'
  }
})

修改message

js 复制代码
this.message = '修改后的值1'
this.message = '修改后的值2'
this.message = '修改后的值3'

这时候想获取页面最新的DOM节点,却发现获取到的是旧值

arduino 复制代码
console.log(vm.$el.textContent) // 原始值

这是因为message数据在发现变化的时候,vue并不会立刻去更新Dom,而是将修改数据的操作放在了一个异步操作队列中

如果我们一直修改相同数据,异步操作队列还会进行去重

等待同一事件循环中的所有数据变化完成之后,会将队列中的事件拿来进行处理,进行DOM的更新

4.1为什么要有nexttick

举个例子

js 复制代码
{{num}}
for(let i=0; i<100000; i++){
    num = i
}

如果没有 nextTick 更新机制,那么 num 每次更新值都会触发视图更新(上面这段代码也就是会更新10万次视图),有了nextTick机制,只需要更新一次,所以nextTick本质是一种优化策略

4.2使用场景

如果想要在修改数据后立刻得到更新后的DOM结构,可以使用Vue.nextTick()

第一个参数为:回调函数(可以获取最近的DOM结构)

第二个参数为:执行函数上下文

js 复制代码
// 修改数据
vm.message = '修改后的值'
// DOM 还没有更新
console.log(vm.$el.textContent) // 原始的值
Vue.nextTick(function () {
  // DOM 更新了
  console.log(vm.$el.textContent) // 修改后的值
})

组件内使用 vm.$nextTick() 实例方法只需要通过this.$nextTick(),并且回调函数中的 this 将自动绑定到当前的 Vue 实例上

js 复制代码
this.message = '修改后的值'
console.log(this.$el.textContent) // => '原始的值'
this.$nextTick(function () {
    console.log(this.$el.textContent) // => '修改后的值'
})

$nextTick() 会返回一个 Promise 对象,可以是用async/await完成相同作用的事情

js 复制代码
this.message = '修改后的值'
console.log(this.$el.textContent) // => '原始的值'
await this.$nextTick()
console.log(this.$el.textContent) // => '修改后的值'

4.3实现原理

源码位置:/src/core/util/next-tick.js

callbacks也就是异步操作队列

callbacks新增回调函数后又执行了timerFunc函数,pending是用来标识同一个时间只能执行一次

js 复制代码
export function nextTick(cb?: Function, ctx?: Object) {
  let _resolve;

  // cb 回调函数会经统一处理压入 callbacks 数组
  callbacks.push(() => {
    if (cb) {
      // 给 cb 回调函数执行加上了 try-catch 错误处理
      try {
        cb.call(ctx);
      } catch (e) {
        handleError(e, ctx, 'nextTick');
      }
    } else if (_resolve) {
      _resolve(ctx);
    }
  });

  // 执行异步延迟函数 timerFunc
  if (!pending) {
    pending = true;
    timerFunc();
  }

  // 当 nextTick 没有传入函数参数的时候,返回一个 Promise 化的调用
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve;
    });
  }
}

timerFunc函数定义,这里是根据当前环境支持什么方法则确定调用哪个,分别有:

Promise.thenMutationObserversetImmediatesetTimeout

通过上面任意一种方法,进行降级操作

js 复制代码
export let isUsingMicroTask = false
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  //判断1:是否原生支持Promise
  const p = Promise.resolve()
  timerFunc = () => {
    p.then(flushCallbacks)
    if (isIOS) setTimeout(noop)
  }
  isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
  isNative(MutationObserver) ||
  MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
  //判断2:是否原生支持MutationObserver
  let counter = 1
  const observer = new MutationObserver(flushCallbacks)
  const textNode = document.createTextNode(String(counter))
  observer.observe(textNode, {
    characterData: true
  })
  timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
  }
  isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  //判断3:是否原生支持setImmediate
  timerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else {
  //判断4:上面都不行,直接用setTimeout
  timerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}

无论是微任务还是宏任务,都会放到flushCallbacks使用

这里将callbacks里面的函数复制一份,同时callbacks置空

依次执行callbacks里面的函数

js 复制代码
function flushCallbacks () {
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}

小结:

  1. 把回调函数放入callbacks等待执行
  2. 将执行函数放到微任务或者宏任务中
  3. 事件循环到了微任务或者宏任务,执行函数依次执行callbacks中的回调

这个知识整理来源于

5. Vue 子组件使用 this.$parent 无法访问到父组件数据和方法

  • 实际场景是父组件有一个dialog弹出框,包含几个子组件,根据标识只展示一个子组件。
  • 现在要想用this.$parent直接调父组件的方法,但没有生效。

解决方法:

后面对着this.$parent 实例一顿找,发现根本就没有父组件的数据和方法,并不是父组件的实例。

因为在子组件外面包了一层dialog弹出框,所以this.$parentelement实例,this.$parent.$parent是父组件实例,

子组件在<template></template>没有任何包裹时通过this.$parent可以直接访问到数据方法

子组件用了dialog弹出框包裹时通过this.$parent.$parent访问数据和方法(具体看实际情况包了几层)

6. consloe 小技巧

6.1. 使用 console.dir() 打印对象

console.dir() 是一个专门打印 对象 的 API。

console.dir() 方法可以显示指定 JavaScript 对象的属性列表,并以交互式的形式展现。输出结果呈现为分层列表,包含展开/折叠的三角形图标,可用于查看子对象的内容。

因此,当我们打印对象时,可以使用 dir 代替 log

  • console.log()输出的是对象源代码,
  • console.dir()则输出该对象的内容,所有属性和方法。

6.2. 使用 console.table() 打印数组

如果想要打印数组的话,那么 table() 是首选方法:

7.>>>、/deep/、::v-deep、::v-deep()和:deep()的区别与用法

1. >>>CSS原生中的深度选择器语法,用于穿透样式封装。

仅在某些特定环境(如Webpackcss-loader配置中)和原生CSS中有效,Vue单文件组件中通常需要特定配置才能使用。

css 复制代码
<style scoped>
.parent >>> .child {
  /* 样式规则 */
}
</style>

2. /deep/

/deep/曾经是CSS中实际提出的新增功能,但之后被删除,所以不建议使用

css 复制代码
<style scoped>
.parent /deep/ .child {
  /* 样式规则 */
}
</style>

3. ::v-deep是/deep/的别名深度选择器

支持Vue2,但在Vue3中不推荐使用。

css 复制代码
<style scoped>
.parent::v-deep .child {
  /* 样式规则 */
}
</style>

4.::v-deep()

::v-deep()深度选择器Vue2Vue3演化过程中的一个过渡性组合器。

css 复制代码
<style scoped>
.parent ::v-deep(.child) {
  /* 样式规则 */
}
</style>

5. :deep()

:deep()Vue3官方推荐的深度选择器,不建议使用>>>/deep/以及::v-deep包括::v-deep()

css 复制代码
<style scoped>
.parent :deep(.child) {
  /* 样式规则 */
}
</style>
  1. Vue2 中使用::v-deep;
  2. Vue3 中使用:deep();
  3. /deep/需要与特定浏览器版本搭配使用,不推荐使用
  4. 部分CSS预处理器>>>支持不佳,在不使用CSS预处理器时可使用,否则不推荐使用

一步到位,直接展示代码。

8. el-upload图片、视频上传

1. 直接显示上传代码

1.封装上传组件UpLoadFile

js 复制代码
<template>
  <div>
   <el-upload 
  class="avatar-uploader" 
  :action="uploadUrl" // 上传文件的地址
  :headers="{ token: token }" // 上传时需要的请求头,包含token
  :show-file-list="false" // 不显示文件列表
  :on-success="handleSuccess" // 上传成功后的回调函数
  :before-upload="beforeUpload" // 上传前的钩子函数
  :on-progress="uploadProcess" // 上传进度的钩子函数
>
  <div v-if="!fileFlag && fileType === 'image'"> // 如果不是正在上传的状态且文件类型为图片
    <img v-if="fileUrl" :src="fileUrl" class="avatar" /> // 如果有文件URL,则显示图片
    <i v-else class="el-icon-plus avatar-uploader-icon" /> // 如果没有文件URL,则显示加号图标
  </div>
  <div v-if="!fileFlag && fileType === 'video'"> // 如果不是正在上传的状态且文件类型为视频
    <video v-if="fileUrl" :src="fileUrl" class="avatar-video" controls="controls"> // 如果有文件URL,则播放视频
      您的浏览器不支持视频播放
    </video>
    <i v-else class="el-icon-plus avatar-uploader-icon" /> // 如果没有文件URL,则显示加号图标
  </div>
  <el-progress v-if="fileFlag" style="margin-top:10px;" type="circle" :percentage="uploadPercent" /> // 如果正在上传,则显示圆形进度条,显示上传百分比
</el-upload>

  </div>
</template>
<script>
export default {
  props: {
    token: {
      type: String,
      default: '',
    },
    uploadUrl: {
      type: String,
      required: true,
    },
    fileType: {
      type: String,
      default: 'image',
    },
    hasFile: {
      type: String,
      default: '',
    },
  },
  data() {
    return {
      fileUrl: '', // 上传成功后接口返回的地址
      uploadPercent: 0, // 上传时进度条显示的进度
      fileFlag: false, // 显示文件还是进度条的标识
    }
  },
  methods: {
  // 上传成功的回调
handleSuccess(res, file) {
  console.log('res', res) // 打印上传返回的结果
  this.fileFlag = false // 上传结束,重置文件上传标记
  this.uploadPercent = 0 // 重置上传进度
  if (res.IsSuccess) { // 判断上传是否成功
    // 这里是回显的地址自定义拼接
    this.fileUrl = '/Content/HomeCarousel/' + res.Data // 拼接上传成功后的文件地址
  } else {
    // 上传失败,提示用户
    this.$message.error('视频上传失败,请重新上传!') // 弹出错误信息
  }
},

// 进度条更新方法
uploadProcess(event, file, fileList) {
  this.fileFlag = true // 表示文件正在上传中
  this.uploadPercent = Math.floor(event.percent) // 更新上传进度
},

// 上传前的文件检查
beforeUpload(file) {
  // 检查文件类型是否为图片
  const isImage = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg'
  // 检查文件类型是否为视频
  const isVideo = file.type === 'video/mp4' || file.type === 'video/avi' || file.type === 'video/flv'
  // 检查文件大小是否小于 100MB
  const isLt10M = file.size / 1024 / 1024 < 100
  if (!isLt10M) { // 如果文件超过 100MB
    this.$message.error('上传文件大小不能超过 100MB!') // 提示文件大小错误
    return false // 取消上传
  }
  if (!isImage && this.fileType === 'image') { // 如果文件不是图片格式
    this.$message.error('上传文件格式有误!') // 提示文件格式错误
    return false // 取消上传
  }
  if (!isVideo && this.fileType === 'video') { // 如果文件不是视频格式
    this.$message.error('上传文件格式有误!') // 提示文件格式错误
    return false // 取消上传
  }
},

  },
  watch: {
hasFile(val) {
  // 检查val是否存在,代表是否上传了文件
  if (val) {
    // 如果val存在,将fileUrl设置为val,即文件的URL
    this.fileUrl = val
  } else {
    // 如果val不存在,将fileUrl清空
    this.fileUrl = ''
  }
},

  },
}
</script>
<style lang="scss" scope>
.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
.avatar-uploader .el-upload:hover {
  border-color: #409eff;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 148px;
  height: 148px;
  line-height: 148px;
  text-align: center;
}
.avatar {
  width: 148px;
  max-height: 148px;
  display: block;
}
.avatar-video {
  width: 200px;
  max-height: 200px;
  display: block;
}
</style>

props 中的tokenuploadUrl根据需要在父组件中传入,因为有时候后台接口要求上传文件时 需要在请求头headers里加入登录标识token,uploadUrl为上传文件的接口。fileType传入imagevideo,在视图中就根据fileType判断显示img图片标签还是video视频标签。

2. 父组件

直接看图

  • 如果需要回显,让子组件直接 uploadFile.vue 直接显示图片或视频,则再多加一个props参数。

父组件parent.vue中添加hasFile,如果需要回显直接传入需要回显的链接,不需要则为空 代码如下:

js 复制代码
 <div class="cus-img-upload">
        <UpLoadFile :token="token" 
        :upload-url="uploadUrl" 
        file-type="video"
        :has-file="hasFile">
        </UpLoadFile>
  </div>
  
  data() {
    return {
   
      // 视频上传
      token: '',
      uploadUrl: 'api/Information/UploadFile',
      hasFile: '',
    }
     
     
mounted() {
    this.token = this.$cookies.get(this.$member.TOKEN)
  
  },

9. .9

在开发提示气泡时,气泡背景要随着文字数量的增加而进行延伸,而这气泡背景便是使用到了.9图,由UI提供。

点九图(9 patch image) ,是Android开发中用到的一种特殊格式的图片,文件名以" .9.png "命名。这种图片能告诉开发,图像哪一部分可以被拉伸,哪一部分不能被拉伸需要保持原有比例。运用点九图可以保证图片在不模糊变形的前提下做到自适应。点九图常用于对话框和聊天气泡背景图片中

  1. 格式
  • .9图的后缀格式为:.9.png
  1. 作用
  • .9图可以进行拉伸,如水平、竖直方向的延长,而清晰度不变。
  • 令一张图片实现多种拉伸效果,减少UI切图的使用,降低包体积。

PS:工作中用过的一些知识点,自己记录的笔记和整理来网络上的资源 ,方便以后复习使用。

相关推荐
雪碧聊技术13 分钟前
01-Ajax入门与axios使用、URL知识
前端·javascript·ajax·url·axios库
adminIvan18 分钟前
Element plus使用menu时候如何在折叠时候隐藏掉组件自带的小箭头
前端·javascript·vue.js
会发光的猪。37 分钟前
【 ElementUI 组件Steps 步骤条使用新手详细教程】
前端·javascript·vue.js·elementui·前端框架
我家媳妇儿萌哒哒38 分钟前
el-table合并单元格之后,再进行隔行换色的且覆盖表格行鼠标移入的背景色的实现
前端·javascript·elementui
baiduguoyun1 小时前
react的import 导入语句中的特殊符号
前端·react.js
前端青山1 小时前
webpack指南
开发语言·前端·javascript·webpack·前端框架
NiNg_1_2341 小时前
ECharts实现数据可视化入门详解
前端·信息可视化·echarts
程序媛小果2 小时前
基于java+SpringBoot+Vue的桂林旅游景点导游平台设计与实现
java·vue.js·spring boot
励志前端小黑哥2 小时前
有了Miniconda,再也不用担心nodejs、python、go的版本问题了
前端·python
喵叔哟2 小时前
重构代码之取消临时字段
java·前端·重构