当前位置: 首页 > news >正文

Vue3(二)计算属性Computed,监视属性watch,watchEffect,标签的ref属性,propos属性,生命周期,自定义hook

文章目录

  • 一 、计算属性
    • 1. 简写
    • 2. 完整写法
  • 二、监视watch
    • 1. 监视【ref】定义的【基本类型】数据
    • 2. 监视【ref】定义的【对象类型】数据
    • 3. 监视【reactive】定义的【对象类型】数据
    • 4. 监视【ref】或【reactive】定义的【对象类型】数据中的某个属性
    • 5. 监视多个数据
    • 总结
  • 三、watchEffect
  • 四、标签的ref属性
  • 五、Propos
    • 1.子组件接收,接收并保存
    • 2. 限制接收的类型,限制必要性
    • 3. 子组件给默认值
  • 六、生命周期
  • 七、自定义Hook

一 、计算属性

computed:根据已有数据计算出新数据(和Vue2中的computed作用一致).
在这里插入图片描述
还是那个案例,当输入框里的内容变化时,全名跟着修改。

1. 简写

语法:computed(()=>{....})

// 引入computed 
import { ref, computed } from "vue"let firstName = ref("zhang")
let lastName = ref("san")// 这么定义的fullName是一个计算属性,且是只读的(也就是简写形式)
let fullName = computed(() => {return firstName.value + "-" + lastName.value
})

2. 完整写法

语法:computed({ get() {...}, set(val) {... }, })

给按钮绑定事件,修改姓名

 <button @click="changeFullName">全名改为:li-si</button>

在这里插入图片描述
代码:

let fullName = computed({get() {return firstName.value + "-" + lastName.value},set(val) {const [str1, str2] = val.split("-")firstName.value = str1lastName.value = str2},
})

二、监视watch

  • 作用:监视数据的变化(和Vue2中的watch作用一致)
  • 特点:Vue3中的watch只能监视以下四种数据
  1. ref定义的数据(基本数据类型,对象类型)。
  2. reactive定义的数据。
  3. 对象类型数据里的某个属性。
  4. 一个包含上述内容的数组。

1. 监视【ref】定义的【基本类型】数据

语法watch(监视对象,回调函数)
监视对象直接写变量名即可,监视的是其value值的改变。

import { ref, watch } from "vue"
// 数据
let sum = ref(0)
// 方法
function changeSum() {sum.value += 1
}
// 监视 watch(监视对象,回调函数);
const stopWatch = watch(sum, (newValue, oldValue) => {if (newValue > 10) {stopWatch()}
})

如果想停止监视,则接收watch的返回值,watch返回的值是一个函数stopWatch,调用该函数就停止监视sum

2. 监视【ref】定义的【对象类型】数据

在这里插入图片描述

import { ref, watch } from "vue"
// 数据
let person = ref({name: "tom",age: 18,
})
function changeName() {person.value.name += "~"
}
function changeAge() {person.value.age += 1
}
function changePerson() {person.value = { name: "李四", age: 50 }
}
watch(person, (newValue, oldValue) => {console.log("person变化", newValue, oldValue)
})

点击这三个按钮发现,只有当点击修改整个人时,watch监视才起作用。因为对于ref定义的对象数据类型,watch监视的是地址值。若想监视对象里面的值,则开启深度监视

watch(person,(newValue, oldValue) => {console.log("person变化", newValue, oldValue)},{ deep: true } // 也可配置 immediate: true
)

另外,点击前两个按钮发现newValueoldValue是同一个值。点击修改整个人时,两个值才不一样。
在这里插入图片描述
因为修改名字和年龄只是修改了对象的属性,而修改整个人是修改了对象的地址。地址不一样了,newValueoldValue值也就不一样了。

总结:

  1. 语法:watch(监视对象,回调函数,{配置对象deep、immediate等等.....})
  2. 监视【ref】定义的【对象类型】数据时,监视的是对象的地址值,若想监视对象内部属性的变化,需要手动开启深度监视
  3. 若修改的是ref定义的对象中的属性,newValueoldValue 都是新值,因为它们是同一个对象。
    若修改整个ref定义的对象,newValue 是新值, oldValue 是旧值,因为不是同一个对象了。

3. 监视【reactive】定义的【对象类型】数据

把上边的代码改了改:
在这里插入图片描述
点击三个按钮,控制台打印的结果:
在这里插入图片描述

  1. 不用写deep:true,默认开启了深度监听。并且这个深度监听是不可关闭的。
  2. 注意到这三行打印的结果里newValueoldValue都是一样的值,这是因为person对象的地址始终没有发生变化。

总结:

监视reactive定义的【对象类型】数据时,默认开启了深度监视且无法关闭。

4. 监视【ref】或【reactive】定义的【对象类型】数据中的某个属性

上边都是监视一个对象,这里监视对象的某属性。
在这里插入图片描述

let person = reactive({name: "张三",age: 18,car: {c1: "奔驰",c2: "宝马",},
})
// 方法
function changeName() {person.name += "~"
}
function changeAge() {person.age += 1
}
function changeC1() {person.car.c1 = "奥迪"
}
function changeC2() {person.car.c2 = "大众"
}
function changeCar() {
// reactive对象里属性对象可以这样改person.car = { c1: "雅迪", c2: "爱玛" }
}

只监视对象里的某一个基本数据类型age

// 第一个参数写成函数的形式,指定监听的属性;
// 如果写的是person,则无论点击哪个按钮,都会被监听到
watch(() => person.age,  (newValue, oldValue) => {console.log("age改变", newValue, oldValue) // age改变 19 18}
)

监视对象里的对象类型属性car

watch(() => person.car,(newValue, oldValue) => {console.log("person.car变化了", newValue, oldValue)},{ deep: true }
)

不写deep:true,则监视的是car这个对象,当其地址发生变化时才会执行console.log打印(也就是不会默认开始深度监听)。
deep:true,则也监视到car内部的属性c1,c2。属性或对象发生变化都会打印
在这里插入图片描述

总结:

  1. 监视响应式对象中的某个属性,且该属性是基本类型的,要写成函数式
  2. 监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写,也能写函数,更推荐写函数
  3. 监视reactive响应式对象中的某个属性,且该属性是对象类型的(car),不会默认开始深度监听

5. 监视多个数据

还是4中的案例,需求是监视人的姓名和车

watch([() => person.name, person.car],(newValue, oldValue) => {console.log("person.car变化了", newValue, oldValue)},{ deep: true }
)

总结

  1. 对于ref定义的基本数据类型,监视的是其value
  2. 对于ref定义的对象数据类型,监视的是对象的地址值,若要监视对象里的属性,需要手动开启深度监视
  3. 对于reactive定义的对象数据类型,默认开启了深度监视
  4. 对于refreactive对象类型里的某个属性,如果是该属性是对象(car),监视的是其地址值,不会默认开始深度监视

三、watchEffect

watch对比watchEffect
(1) 都能监听响应式数据的变化,不同的是监听数据变化的方式不同
(2) watch:要明确指出监视的数据
(3) watchEffect:不用明确指出监视的数据(函数中用到哪些属性,就监视哪些属性)

在这里插入图片描述

import { ref, watch, watchEffect } from "vue"
// 数据
let temp = ref(0)
let height = ref(0)// 方法
function changeTemp() {temp.value += 10
}
function changeHeight() {height.value += 10
}// 用watch实现
watch([temp, height], (val) => {// 从value中获取最新的temp值、height值let [newTemp, newHeight] = valif (newTemp >= 50 || newHeight >= 20) {console.log("发送请求")}
})// 用watchEffect
watchEffect(() => {if (temp.value >= 50 || height.value >= 20) {console.log("发请求")}
})

四、标签的ref属性

ref用于给标签打标识。
1. 用在普通DOM标签上

<template><div class="person"><h2 ref="title">hello</h2><h2 ref="title2">hello again</h2><h2>world</h2><button @click="showTitle">获取所有hello</button></div>
</template><script lang="ts" setup name="Person">
import { ref } from "vue"
// 似乎是解构赋值,这里接收的变量名需要和ref定义的一致
let title = ref()
let title2 = ref()
// let title1 = ref()  这样打印出title1是undefined
function showTitle() {console.log('ref', ref());console.log('title', title.value)console.log('title2', title2.value)
}
</script>

变量名需要的ref的名称一致,否则undefined
在这里插入图片描述
(这个ref打印出来也不知道是什么东西,看不懂,有点子神奇了)

2. 用在组件标签上
(竟然可以实现子组件给父组件传值)

父组件

<template><Person ref="ren" /><button @click="showInfo">点击获取组件信息</button>
</template><script lang="ts" setup name="APP">
import Person from "./components/Person.vue"
import { ref } from "vue"
let ren = ref()
function showInfo() {console.log(ren.value.name)console.log(ren.value.age)
}
</script>

子组件

// 不引入也行,definexxx都是一些宏函数,会自动引入
import { defineExpose } from "vue"
let name = "tom"
let age = 18
// 使用defineExpose将组件中的数据交给外部
defineExpose({ name, age })

五、Propos

propos用于父组件向子组件传值

父组件的数据:

import Person from './components/Person.vue'
import { reactive } from 'vue';
import { Persons } from './type';
// 父组件
let persons = reactive<Persons>([{ id: '12345', name: 'tom', age: 18 },{ id: '123456', name: 'jerry', age: 18 },{ id: '1234567', name: 'lucy', age: 18 }
])

1.子组件接收,接收并保存

<!--父组件--><Person :list=persons />
<!--子组件-->
<script>// 只接收defineProps(['list'])console.log(list[0]); // 没保存,直接读取会提示“找不到list”// 接收并保存let x = defineProps(['list'])console.log(x.list[0]); // Proxy(Object) {id: '12345', name: 'tom', age: 18}
</script>

2. 限制接收的类型,限制必要性

<!--父组件-->
<Person :list=persons /><Person :list="5" />  <!-- 不是Person类型会报错 --><Person />            <!-- 没有给子组件传递数据也会报错,解决方法:添加`?` --><!--子组件-->
<script>// 接收+限制类型,只能接收Persons类型的数据defineProps<{ list: Persons }>()
</script>

当添加一个?之后,则父组件可传可不传。不传递也不会报错

 defineProps<{ list?: Persons }>()

3. 子组件给默认值

import { withDefaults } from 'vue';
// 接收+限制类型+限制必要性+指定默认值
withDefaults(defineProps<{ list?: Persons }>(), {list: () => [{ id: '默认', name: '默认', age: 10 }]
})

六、生命周期

回顾vue2的生命周期,vue2(三) vue生命周期

Vue2的生命周期

创建阶段:beforeCreatecreated

挂载阶段:beforeMountmounted

更新阶段:beforeUpdateupdated

销毁阶段:beforeDestroydestroyed

Vue3的生命周期

创建阶段:setup

挂载阶段:onBeforeMountonMounted

更新阶段:onBeforeUpdateonUpdated

卸载阶段:onBeforeUnmountonUnmounted

常用:onMounted(挂载完毕),onUpdated(更新完毕),onBeforeUnmount(卸载之前)

解释一下这个创建阶段,用代码表示就是:

<script lang="ts" setup name="Person">
import { onBeforeMount,  onMounted,  } from 'vue';console.log('创建');onBeforeMount(() => {console.log('挂载之前');
})onMounted(() => {console.log('挂载完毕');
})
</script>

在这里插入图片描述

七、自定义Hook

一直在说vue3是组合式API,每个功能的数据,方法都放在一起。目前在<script>标签里还是上边写一堆功能的数据,下面写一堆功能的方法,和vue2没啥区别了。
hook其实就是将各个功能的数据和方法单独封装到一个hook文件中。以后修改功能就直接去这个hook文件里改就好了。

  • 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装,类似于vue2.x中的mixin
  • 自定义hook的优势:复用代码, 让setup中的逻辑更清楚易懂。

现在页面上有两个功能:
在这里插入图片描述
src/hook/useSum.ts

import {  ref, computed } from 'vue'
export default function(){
// 功能一的数据let sum = ref(0)
// 功能一的函数let bigSum = computed(() => {return 10 * sum.value})function addSum() {sum.value += 1}return {sum,addSum,bigSum}
}

src/hook/useDog.ts

import { reactive, ref, computed } from 'vue'
import axios from 'axios';
export default function(){
// 功能二的数据
let dogList = reactive<string[]>([])
// 功能二的函数
async function getDog() {try {// 发请求let { data } = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')// 维护数据dogList.push(data.message)} catch (error) {// 处理错误console.log(error.message)}
}
return {dogList,getDog}
}

组件内:
src/components/Person.vue

<template><div class="person"><h2> 求和为{{ sum }},扩大十倍后{{ bigSum }}</h2><button @click="addSum">sum+1</button><br><br><img v-for="(u, index) in dogList" :key="index" :src="(u as string)"><br><button @click="getDog">再来一只狗</button></div>
</template><script lang="ts" setup name="Person">
// 1. 引入
import useDog from '../hook/useDog.ts'
import useSum from '../hook/useSum.ts'
// 2. 解构获取信息
const { dogList, getDog } = useDog()
const { sum, addSum, bigSum } = useSum()
</script>

相关文章:

Vue3(二)计算属性Computed,监视属性watch,watchEffect,标签的ref属性,propos属性,生命周期,自定义hook

文章目录 一 、计算属性1. 简写2. 完整写法 二、监视watch1. 监视【ref】定义的【基本类型】数据2. 监视【ref】定义的【对象类型】数据3. 监视【reactive】定义的【对象类型】数据4. 监视【ref】或【reactive】定义的【对象类型】数据中的某个属性5. 监视多个数据总结 三、wat…...

栈:只允许在一端进行插入或删除操作的线性表

一、重要术语&#xff1a; 栈顶、栈底、空栈 二、线性表的基本操作 三、栈的相关操作&#xff1a; 把线性表中的list改成stack insert改成 push delete 改成 pop 总结&#xff1a;“后进先出” 四、顺序栈&#xff1a; 缺点&#xff1a;栈的大小不可变 1.定义&#xff1a; …...

spring boot 热部署

热部署的主要作用是在服务器运行的时候可以在不关闭服务器的情况下修改代码 可以很大的提高开发效率 热部署的步骤很简单 首先&#xff0c;需要在 pom.xml 文件中引入热部署需要的依赖 <dependency><groupId>org.springframework.boot</groupId><artif…...

携手阿里云CEN:共创SD-WAN融合广域网

在9月19日举行的阿里云云栖大会上&#xff0c;犀思云作为SD-WAN领域的杰出代表及阿里云的SD-WAN重要合作伙伴&#xff0c;携手阿里云共同推出了创新的企业上云方案——Fusion WAN智连阿里云解决方案。这一创新方案不仅彰显了犀思云在SD-WAN技术领域的深厚积累&#xff0c;更体现…...

kettle从入门到精通 第八十七课 ETL之kettle kettle文件上传

1、kettle本身文件上传功能不是很友好&#xff0c;甚至是不能直接使用&#xff0c;需要调整文件上传接口才可以正常接收到文件&#xff0c;本次讲解内容主要是通过自定义插件解决这个问题。 2、通过springboot 编写简单demo&#xff0c;模拟文件上传&#xff0c;接口支持三个参…...

Algo-Lab 2 Stack Queue ADT

Lab 2: Stack & Queue ADT Part 1 ​ 这里只说一下最小栈的思路&#xff0c;我们可以在定义一个栈&#xff0c;来同步存储当前情况下的占的最小值。最小栈第一时间的想法可能是设定一个变量&#xff0c;每次push进来栈中的元素进行对比&#xff0c;保持最小值&#xff0c;…...

MySQL索引详解

前言 在数据库管理中&#xff0c;索引是提高数据检索速度的重要工具。MySQL作为流行的关系型数据库管理系统&#xff0c;提供了多种类型的索引来优化查询性能。本文将深入探讨MySQL索引的工作原理、类型、创建方法以及最佳实践。 索引简介 MySQL中的索引是一种数据库对象&am…...

fastadmin 根据选择数据来传参给selectpage输入框

文章目录 js代码php代码&#xff1a;完结 js代码 $(document).on(change,#table .bs-checkbox [type"checkbox"],function(){let url$(#chuancan).attr(data-url)urlurl.split(?)[0]let idsTable.api.selectedids(table)if(ids.length){let u_id[]ids.forEach(eleme…...

【算法】堆与优先级队列

【ps】本篇有 4 道 leetcode OJ。 目录 一、算法简介 二、相关例题 1&#xff09;最后一块石头的重量 .1- 题目解析 .2- 代码编写 2&#xff09;数据流中的第 K 大元素 .1- 题目解析 .2- 代码编写 3&#xff09;前K个高频单词 .1- 题目解析 .2- 代码编写 4&#xf…...

Java基础尚硅谷85-面向对象特征一:封装性

曾国藩说&#xff0c;基础不牢&#xff0c;很难走得远。 所以时时回顾一下Java基础&#xff0c;打好地基&#xff0c;让自己走得更稳&#xff0c;更远。 今天这节课&#xff0c;学到对自己有点价值的东西是&#xff1a; 为什么要封装&#xff1f;保护数据安全。只对外暴露极少…...

828华为云征文 | 将Vue项目部署到Flexus云服务器X实例并实现公网访问

一、Flexus云服务器X实例简介 1.1 概述 华为云Flexus X实例是华为云推出的一款创新云服务器产品&#xff0c;它主要面向中小企业和开发者&#xff0c;旨在解决传统云服务中的痛点&#xff0c;提供更加灵活、高效的云服务体验。 华为深刻洞察了中小企业和开发者在云服务应用中遇…...

828华为云征文|华为云Flexus云服务器X实例部署Xnote笔记应用

828华为云征文&#xff5c;华为云Flexus云服务器X实例部署Xnote笔记应用 前言一、Flexus云服务器X实例介绍1.1 Flexus云服务器X实例简介1.2 Flexus云服务器X实例特点1.3 Flexus云服务器X实例使用场景 二、Note Mark 介绍2.1 Xnote简介2.2 Xnote特点2.3 主要使用场景 三、本次实…...

手写数字识别案例分析(torch,深度学习入门)

在人工智能和机器学习的广阔领域中&#xff0c;手写数字识别是一个经典的入门级问题&#xff0c;它不仅能够帮助我们理解深度学习的基本原理&#xff0c;还能作为实践编程和模型训练的良好起点。本文将带您踏上手写数字识别的深度学习之旅&#xff0c;从数据集介绍、模型构建到…...

应用密码学第一次作业(9.23)

一、Please briefly describe the objectives of information and network security,such as confidentiality, integrity, availability , authenticity , and accountability The objectives of information and network security include: Confidentiality: Protecting se…...

JSON合并工具

JSON合并工具 1. 项目概述 本项目旨在开发一个强大而灵活的JSON合并工具&#xff0c;能够合并多个JSON文件&#xff0c;处理复杂的嵌套结构&#xff0c;提供详细的合并报告&#xff0c;并实现全面的验证和错误处理机制。 2. 功能需求 2.1 基本合并功能 支持合并两个或多个…...

【网络编程】网页的显示过程

文章目录 1.URL 解析2.DNS 解析3.TCP三次握手4.服务器接收请求5.客户端接收响应 首先我们知道网页经过网络总共有应用层&#xff0c;传输层&#xff0c;网络层&#xff0c;数据链路层&#xff0c;物理层 1.URL 解析 将获得的网址解析出协议&#xff0c;主机名&#xff0c;域名…...

用nginx-rtmp-win32-master及ffmpeg模拟rtmp视频流

效果 使用nginx-rtmp-win32-master搭建RTMP服务 双击exe就可以了。切记整个目录不能有中文 README.md ,启用后本地的RTM路径: rtmp://192.168.1.186/live/xxx ffmpeg将地本地视频推RMTP F:\rtsp\ffmpeg-7.0.2-essentials_build\bin>ffmpeg -re -i F:\rtsp\123.mp4 -c c…...

使用python-pptx将PPT转换为图片:将每张幻灯片保存为单独的图片文件

哈喽,大家好,我是木头左! 本文将详细介绍如何使用python-pptx将PPT的每一张幻灯片保存为单独的图片文件。 安装python-pptx库 需要确保已经安装了python-pptx库。可以通过以下命令使用pip进行安装: pip install python-pptx导入所需库 接下来,需要导入一些必要的库,包…...

聊聊企业的低代码实践背景与成效

数字化转型的道路充满挑战是大家的普遍共识&#xff0c;许多企业仍未完全步入数字化的行列&#xff0c;它们面临的是系统的碎片化和操作的复杂性。在数字优先的今天&#xff0c;企业要想维持竞争力&#xff0c;比任何时期都更需要实施某种程度的数字化升级。如果一个组织难以提…...

zookeeper面试题

1. 什么是zookeeper zookeeper是一个开源的 分布式协调服务。他是一个为分布式应用提供一致性服务的软件&#xff0c;分布式应用程序可以基于Zookeeper实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等功能。 Zooke…...

linux——消息队列发送和读取函数

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); //读取消息&#xff0c;成功返回消息数据的长度&#xff0c;失败返回‐1 参数&#xff1a; msgid:消息队列的ID msgp:指向消息的指针&#xff0c;常用结构体msgbuf如下&#xff1a; struct msgbuf { lon…...

Pixel Aurora Engine作品集:基于大气/明亮/交互哲学的100+原创像素图

Pixel Aurora Engine作品集&#xff1a;基于大气/明亮/交互哲学的100原创像素图 1. 像素极光引擎概览 Pixel Aurora Engine是一款专为像素艺术创作设计的AI绘图工作站。它采用复古游戏机风格的界面设计&#xff0c;将现代AI技术与经典8-bit美学完美融合。通过简单的文字描述&…...

系统级音频均衡器如何提升macOS音质:开源eqMac完全指南

系统级音频均衡器如何提升macOS音质&#xff1a;开源eqMac完全指南 【免费下载链接】eqMac macOS System-wide Audio Equalizer & Volume Mixer &#x1f3a7; 项目地址: https://gitcode.com/gh_mirrors/eq/eqMac eqMac是一款开源的macOS系统级音频均衡器与音量混合…...

《Linux网络编程》2.Socket编程(UDP/TCP)

&#x1f4a1;Yupureki:个人主页 ✨个人专栏:《C》 《算法》《Linux系统编程》《高并发内存池》《MySQL数据库》 《个人在线OJ平台》《Linux网络编程》 &#x1f338;Yupureki&#x1f338;的简介: 目录 1. UDP编程 1.1 常用接口 1.1.1 socket() – 创建套接字 1.1.2 bin…...

Java Web 入门学习笔记:Servlet 请求响应与登录功能实战

一、开篇&#xff1a;今日学习核心与目标学习背景&#xff1a;Java Web 入门阶段核心 ——Servlet 与 HTTP 请求响应交互学习目标&#xff1a;掌握 HttpServletRequest/HttpServletResponse 核心用法实现「登录表单提交→Servlet 验证→页面重定向」完整流程解决实操中 404、中…...

comsol燃料电池堆冷却:模型对聚合物电解质膜 (PEM) 燃料电池堆的热管理进行建模 对电...

comsol燃料电池堆冷却&#xff1a;模型对聚合物电解质膜 (PEM) 燃料电池堆的热管理进行建模 对电池堆的所有电池单元来说&#xff0c;以相似的温度曲线进行操作非常重要&#xff0c;因为非均匀的温度分布可能会导致非均匀的水蒸气冷凝&#xff0c;以及电池单元之间出现较大的性…...

Qwen3.5-9B-AWQ-4bit操作系统概念学习与实验指导

Qwen3.5-9B-AWQ-4bit操作系统概念学习与实验指导 1. 当AI成为你的操作系统课助教 想象一下&#xff0c;凌晨两点你正在赶操作系统课程的作业&#xff0c;突然卡在进程调度算法上。这时候如果有个随时在线的助教&#xff0c;能清晰解释概念、提供实验思路&#xff0c;甚至给出…...

javaweb驾校考试车辆预约系统

目录同行可拿货,招校园代理 ,本人源头供货商功能模块划分预约功能设计考试管理模块系统辅助功能技术实现参考项目技术支持源码获取详细视频演示 &#xff1a;文章底部获取博主联系方式&#xff01;同行可合作同行可拿货,招校园代理 ,本人源头供货商 功能模块划分 用户管理模块…...

颠覆体验:Mac鼠标滚动优化完全指南——从卡顿到丝滑的蜕变之路

颠覆体验&#xff1a;Mac鼠标滚动优化完全指南——从卡顿到丝滑的蜕变之路 【免费下载链接】Mos 一个用于在 macOS 上平滑你的鼠标滚动效果或单独设置滚动方向的小工具, 让你的滚轮爽如触控板 | A lightweight tool used to smooth scrolling and set scroll direction indepen…...

北海网红美食有哪些

行业现象观察&#xff1a;北海海鲜餐饮的消费图谱在北海&#xff0c;尤其是侨港镇区域&#xff0c;海鲜餐饮呈现出鲜明的“游客本地”双轨特征。晚间时段&#xff0c;从侨港风情街延伸至文化中心一带&#xff0c;用餐高峰时段常出现人流密集、烟火气十足的景象。本地居民多选择…...