在处理大量数据列表时,直接渲染所有数据会导致页面卡顿、内存占用过高。虚拟滚动是一种优化技术,只渲染可视区域内的元素,大大提升了性能。
为什么需要虚拟滚动?
- 性能问题:当列表数据量过大时,一次性渲染所有DOM元素会造成页面卡顿
- 内存占用:大量DOM节点会占用过多内存
- 用户体验:滚动不流畅,影响用户操作
vue-virtual-scroller
vue-virtual-scroller 是一个专门为Vue设计的虚拟滚动组件库,支持Vue 3。它提供了多种虚拟滚动组件:
易笔记在处理大量数据列表时,直接渲染所有数据会导致页面卡顿、内存占用过高。虚拟滚动是一种优化技术,只渲染可视区域内的元素,大大提升了性能。
vue-virtual-scroller 是一个专门为Vue设计的虚拟滚动组件库,支持Vue 3。它提供了多种虚拟滚动组件:
一份适合零基础开发者的 LangChain 学习指南,助你高效构建基于大语言模型(LLM)的智能应用。
完成本路径后,你将能够独立开发以下应用:
在CSS布局中,垂直水平居中是一个非常常见的需求,但也是让很多开发者头疼的问题。本文将介绍七种常用的CSS垂直水平居中方法,帮助您在不同场景下选择最合适的解决方案。
Flexbox是最现代且强大的居中方法,兼容性良好,使用简单。
<div class="container">
<div class="item">居中元素</div>
</div>
在前端开发中,我们经常会遇到一些频繁触发的事件,如窗口的 resize、scroll,输入框的 input,鼠标的 mousemove 等。如果这些事件的处理函数执行频率过高,可能会导致页面卡顿、性能下降等问题。为了解决这些问题,我们可以使用两种常见的优化技术:防抖(Debounce)和节流(Throttle)。
防抖是一种限制函数执行频率的技术,它确保函数在一段时间内只执行一次。当事件被触发时,函数并不会立即执行,而是等待一段时间,如果在这段时间内事件再次被触发,则重新计时。只有当事件停止触发一段时间后,函数才会真正执行。
1、布尔类型(boolean):
let flag:boolean=[false|true]
2、数字类型(number):
let num:number=12
3、字符串类型(string):
let str:string='hello world'
4、数组类型(array):
let array:number[]=[1,2]
let list: Array<number> = [1, 2, 3];
5、对象类型:
const obj:{ x:number; y:number;} = { x: 1, y: 1 };
const obj : {[key:string] : string | number} = {name:'str',age:18};
const {id, name, price}:{ id: string; name: string; price: number} = product;
6、元组类型(tuple),元组类型是固定长度的数组,可以指定数组中每个元素的类型:
let tup:[string,number,boolean]=['we',21,false]
7、枚举类型(enum):
定义:enum 枚举变量名{ 枚举类名=枚举值, 枚举类名1=枚举值1}
enum flag{ success=1, errorM=-1 }
使用:var 变量:枚举类型=枚举变量名.枚举名 || var 变量名=枚举变量名.枚举名
var F:flag=flag.success //var F=flag.success
例如: enum Weeks {Mon, Tue, Wed, Thu, Fri, Sat, Sun};
console.log(Weeks['Mon']); // => 0
console.log(Weeks[0]); // => 'Mon'
console.log(Weeks.Tue); // => 1
8、任意类型(any)可以赋值给任意变量:
let dom:any=document.getElementById('app')
9、未知(unknown):
let num:unknown
10、null和undefined:
let num:number|undefined|null
let num:number|undefined|nul
11、void类型(函数无返回值):
function fn():void{
console.log('q');
}
12、never类型:never表示其他类型的(包括null和underfined)子类型,表示从未出现过的值,是一个隐含的类型。
注意:大写Boolean Number String 等,用于创建对应的包装对象,一般不用。
在Vue开发中,我们经常会遇到这样的场景:修改数据后立即操作DOM却发现DOM还未更新。这时我们就需要使用nextTick来解决这个问题。本文将深入探讨Vue中nextTick的实现原理。
Vue在更新DOM时是异步执行的,只要侦听到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作是非常重要的。
然后,在下一个的事件循环"tick"中,Vue刷新队列并执行实际(已去重的)工作。Vue在内部对异步队列尝试使用原生的Promise.then、MutationObserver和setImmediate,如果执行环境不支持,则会采用setTimeout(fn, 0)代替。
在Vue 3的Composition API中,ref和reactive是创建响应式数据的两种主要方式。虽然它们都能实现响应式,但在使用场景和行为上存在一些重要区别。本文将详细探讨这两种API的异同点,帮助开发者更好地理解和使用它们。
ref用于创建一个响应式的引用对象,它可以包装任何类型的值(基本类型或对象类型)。ref返回的对象有一个.value属性,指向内部的值。
在Vue开发中,我们经常会遇到这样的问题:直接通过索引设置数组项或者给对象添加新属性时,视图并没有更新。这时我们就需要使用Vue提供的$set方法来解决这个问题。本文将深入探讨Vue中$set的实现原理。
在Vue的响应式系统中,并不是所有数据变化都能被检测到。主要有以下两种情况:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
// 这种方式不会触发响应
vm.items[1] = 'x'
首先定义一个子组件my-component
<template>
<view class="dddd"></view>
</template>
高度塌陷(Height Collapse)是 CSS 布局里常见的问题,通常在父元素包含浮动子元素时出现。下面详细解释该问题的成因、表现及解决办法。
在正常的文档流中,父元素的高度默认会根据其包含的子元素内容自动调整,以包裹住所有子元素。不过,当子元素设置了 float 属性(如 float: left 或 float: right)后,这些子元素会脱离正常的文档流,不再占据原有的空间。此时,父元素在计算自身高度时,会忽略这些浮动的子元素,进而导致父元素高度变为 0 或者只包含非浮动子元素的高度,这就是高度塌陷问题。
pnpm install lodash-es --save
import {
createApp,
h,
ref,
Component,
ComponentInternalInstance
} from 'vue';
import AddDialog from './AddDialog.vue';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
/**
* 打开添加对话框
* @param currentInstance - 当前组件实例
* @param url - 请求接口地址
* @param title - 对话框标题
* @param config - 表单配置
*/
export const openAddDialog = (currentInstance: ComponentInternalInstance | null, url: string, title: string, config: any[]) => {
// 创建用于挂载对话框的容器
const container = document.createElement('div');
document.body.appendChild(container);
const parentApp = currentInstance?.appContext.app;
// 定义对话框组件引用
const dialogRef = ref<InstanceType<typeof AddDialog>>();
// 创建 Vue 应用实例
const app = createApp({
setup() {
// 渲染 AddDialog 组件
return () => h(AddDialog, {
ref: dialogRef,
title,
url,
config,
onClose: () => {
destroyDialog();
}
});
}
});
if (parentApp) {
// 继承全局属性
Object.assign(app.config.globalProperties, parentApp.config.globalProperties);
// 继承局部注册组件
if (currentInstance) {
// @ts-ignore 绕过 TypeScript 类型检查,获取内部组件定义
const localComponents = currentInstance.type?.components || {};
for (const [name, component] of Object.entries(localComponents)) {
app.component(name, component as Component);
}
}
}
// 在应用实例中注册 Element Plus
app.use(ElementPlus);
// 定义销毁对话框的方法
const destroyDialog = () => {
app.unmount();
document.body.removeChild(container);
};
// 挂载应用实例
app.mount(container);
// 打开对话框
if (dialogRef.value) {
dialogRef.value.open();
}
return {
destroy: destroyDialog
};
};
/**
* 获取移动位置
* @param {Cesium.Cartesian3} start 开始位置
* @param {Cesium.Cartesian3} direction 移动方向和距离
* @returns {*}
*/
export const getMovePosition = (start, direction) => {
return Cesium.Matrix4.multiplyByPoint(
Cesium.Transforms.eastNorthUpToFixedFrame(
start
),
direction,
new Cesium.Cartesian3()
)
}
翻遍Cesium文档未能发现border相关内容,但src支持canvas格式索性通过canvas添加图片。
/**
* 为图形添加边框
* @param entity cesium实体
* @param url 图片地址
* @param color 边框颜色
*/
export const addBorderToImage = (entity, url,color = 'green') => {
const canvas = document.createElement('canvas');
canvas.width = entity.billboard.width
canvas.height = entity.billboard.height
const ctx = canvas.getContext('2d')
let image = new Image()
image.crossOrigin = "anonymous";
image.width = canvas.width
image.height = canvas.height
image.onload = () => {
ctx.drawImage(image,0,0, canvas.width, canvas.height)
ctx.strokeStyle = color
ctx.lineWidth = 6
ctx.rect(0,0,canvas.width,canvas.height)
ctx.stroke()
entity.billboard.image = canvas
}
image.src = url
}
function insert() {
let element = document.getElementById('text')
let value = '_'
// IE support
if (document.selection) {
console.log('ie')
element.focus()
let sel = document.selection.createRange()
sel.text = value
} else if (element.selectionStart || element.selectionStart === '0') { // MOZILLA and others
console.log('modern')
let startPos = element.selectionStart
let endPos = element.selectionEnd
element.value = element.value.substring(0, startPos) + value + element.value.substring(endPos, element.value.length)
element.selectionStart = startPos + value.length
element.selectionEnd = startPos + value.length
} else {
element.value += value
}
}
使用vs code 打开,保存时会提示用管理员权限。
C:\Windows\System32\drivers\etc\hosts
文件头部加入一下代码
if "%1"=="h" goto begin
start mshta vbscript:createobject("wscript.shell").run("""%~nx0"" h",0)(window.close)&&exit
:begin
你好,感谢您联系微软社区!
了解到您远程桌面登入的问题,请尝试 Win + R 键,在运行框中输入 gpedit.msc 之后确定,打开组策略编辑器。
Windows 设置 -> 安全设置 -> 本地策略 -> 安全选项 -> 网络访问: 本地帐户的共享和安全模型。从 “仅来宾” 修改为 “经典” ,重启看看效果。
希望以上信息能帮助您!
原理:将头部referer字段修改为网站信任的白名单,比如:百度
import { app, BrowserWindow, shell, session } from 'electron'
const filter = {
urls: ['*.hdslb.com/*']
}
session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, cb) => {
console.log(details)
details.requestHeaders['referer'] = 'https://www.baidu.com'
let data = { requestHeaders: details.requestHeaders }
cb(data)
})
height:calc(100% - 32px);
现有两个div,上面的div设置margin-bottom:200px,下面的设置margin-top:100px,最终两个div距离却是200px。
当两个在标准流中相邻(兄弟或父子关系)的块级元素的外边距,组合在一起变成单个外边距,但只有上下外边距会发生塌陷。 计算方式: 1. 两个块margin都为正,取其较大的一个 2. 两个块magin都为负,取其绝对值较大的一个 3. 一个块为负,一个块为正,取两个块margin之和
window.addEventListener("onDrawSucceeded", function (c) {
window.removeEventListener("onDrawSucceeded", arguments.callee)
})
git clone https://github.com/myanaloglife/py-kms.git
发送请求这个动作本身也是一个请求,必须等之前的请求发送完才会执行接下来请求,这就构成了死循环。
开启多个php端口
upstream backend{
server 127.0.0.1:9123;
server 127.0.0.1:9124;
}
server
{
...
location ~ \.php$ {
...
fastcgi_pass backend;
...
}
...
}
&::-webkit-scrollbar-button{
background-color: #074337;
background-size: 18px;
background-position: center;
background-repeat: no-repeat;
&:horizontal {
&:start:decrement{ // 水平左侧(垂直上侧)“减”按钮
display: block;
background-image: url("~@/assets/images/icon/icon_sanjiao_left.png");
}
&:start:increment{ // 水平左侧(垂直上侧)“加”按钮
display: none;
}
&:end:decrement{ // 水平右侧(垂直下侧)“减”按钮
display: none;
}
&:end:increment{ // 水平右侧(垂直下侧“加”按钮
display: block;
background-image: url("~@/assets/images/icon/icon_sanjiao_right.png");
}
}
}
&::-webkit-scrollbar {
/*滚动条整体样式*/
height: 2rem;
display: block;
}
&::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
background : #097b64;
border-bottom: solid 2px #074337;
border-top: solid 2px #074337;
}
&::-webkit-scrollbar-track {
/*滚动条里面轨道*/
padding: 2px 0;
background : #074337;
}
.content{
border-image-source: url(data:image/png;base64,...); // 图片路径
border-image-slice: 10 10 10 10 fill; // 每个区域截取宽度为 10px
border-image-width: 10px 10px 10px 10px; // 设置各个区域的图片宽度
border-image-repeat: repeat; // 图片重复或拉伸模式
border-image-outset: 10px 10px 10px 10px; //
}
对一个房子模型其中一扇门操作时,发现所有门都被选中,打印后发现所有门是一个Mesh,因此需要Blender工具对门进行拆分。
当素材尺寸(宽和高)不是2的n次方时,贴图效果会较为模糊。 控制台会有如下警告:
THREE.WebGLRenderer: image is not power of two (1638x2166). Resized to 1024x2048
解决方案:对贴图做如下设置
texture.generateMipmaps = false;
texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping;
texture.minFilter = THREE.LinearFilter