HTML5中IndexedDB前端本地数据库

一、indexedDB为何替代了Web SQL Database?

        跟小朋友的教育从来没有什么“赢在起跑线”这种说法一样,在前端领域,也不是哪来先出来哪个就在日后引领风骚的。

        HTML5 indexedDB和Web SQL Database都是本地数据库数据存储,Web SQL Database数据库要出来的更早,然并卵。从2010年11月18日W3C宣布舍弃Web SQL database草案开始,就已经注定Web SQL Database数据库是明日黄花。

未来一定是indexedDB的,从目前浏览器的兼容性来看,也表明了这种结果:

        可以看到IE和Firefox并不支持Web SQL Database,基本上可以断定永远也不会支持,规范都不认可,实在没有浪费精力去支持的理由。

        好,现在我们知道了indexedDB取代Web SQL Database大局已定,那大家知道为何Web SQL Database会被舍弃吗?

下面是一段Web SQL Database代码:

database.transaction(function(tx) {tx.executeSql("CREATE TABLE IF NOT EXISTS tasks (id REAL UNIQUE, text TEXT)", []);
}

        可以看到直接把SQL语句弄到JS中了,主流的关系型数据库即视感,这么设计看上去似乎无可厚非,但恰恰这个设计成为了Web SQL Database被舍弃的重要原因之一:一是学习成本高了很多,SQL虽然本身并不复杂,但跨度较大,例如我司玩JS的工程师和玩SQL的工程师中间还隔了个玩php的工程师;二是本身使用很不方便,需要把JS对象转换成关系型的字符串语句,很啰嗦的。

而indexedDB直接JS对象入库,无缝对接。

下表为更详细的indexedDB和Web SQL Database对比内容:

WebSQL

IndexedDB

优点

真正意义上的关系型数据库,类似SQLite(SQLite是遵守ACID的轻型的关系型数据库管理系统)

  • 允许对象的快速索引和搜索,因此在Web应用程序场景中,您可以非常快速地管理数据以及读取/写入数据。
  • 由于是NoSQL数据库,因此我们可以根据实际需求设定我们的JavaScript对象和索引。
  • 在异步模式下工作,每个事务具有适度的粒状锁。这允许您在JavaScript的事件驱动模块内工作。

不足

  • 规范不支持啦
  • 由于使用SQL语言,因此我们需要掌握和转换我们的JavaScript对象为对应的查询语句。
  • 非对象驱动。

如果你的世界观里面只有关系型数据库,恐怕不太容易理解。

位置

包含行和列的表。

包含JavaScript对象和键的存储对象。

查询机制

SQL

Cursor APIs,

Key Range APIs,应用程序代码

事务

锁可以发生在数据库,表,行的“读写”时候。

锁可以发生在数据库版本变更事务,或是存储对象“只读”和“读写”事务时候。

事务提交

事务创建是显式的。默认是回滚,除非我们调用提交。

事务创建是显式的。默认是提交,除非我们调用中止或有一个错误没有被捕获。

STOP!

不能再继续说下去了,估计看完上面对比表中的内容,已经有很多同学已经有些不知所以然了,因为其中涉及到了很多数据库相关的概念,要想轻松理解上面内容以及更轻松掌握indexedDB的相关知识,这些概念还是需要掌握的。

二、务必先了解数据库的一些概念

对于前端同学,数据库概念没必要理解十分精准,只需要知道大概怎么回事就好了,因此,下面的一些解释会其意,勿嚼字。

1. 关系型数据库和非关系型数据库

“关系型数据库”是历史悠久,已经有半个世纪,占据主流江山的数据库模型,估计诸位公司的网站多半都是使用的这种数据库模型。而“非关系型数据库”(NoSQL数据库)则要年轻很多,根据资料显示是Carlo Strozzi在1998年提出来的,20年不到,使用键值对存储数据,且结构不固定,非常类似JavaScript中的纯对象。

var obj = { key1: 'value1', key2: 'value2', key3: 'value3', ... };

“关系型数据库”对一致性要求非常严格,例如要写入100个数据,前99个成功了,结果第100个不合法,此时事务会回滚到最初状态。这样保证事务结束和开始时候的存储数据处于一致状态。非常适合银行这种对数据一致性要求非常高的场景。但是,这种一致性保证是牺牲了一部分性能的。

但是,对于微博,QQ空间这类web2.0应用,一致性却不是显得那么重要,比方说朋友A看我的主页和朋友B看我的主页信息有不一样,没什么大不了的,有个几秒差异很OK的。虽然这类应用对一致性要求不高,但对性能要求却很高,因为读写实在太频繁了。如果使用“关系型数据库”,一致性的好处没怎么收益,反而性能问题比较明显,此时,不保证一致性但性能更优的“非关系型数据库”则更合适。同时,由于“非关系型数据库”的数据结构不固定,非常容易扩展。由于对于社交网站,需求变动那是一日三餐常有的事,添加新字段在所难免,“非关系型数据库”轻松上阵,如果使用“关系型数据库”,多半要数据大变动,要好好琢磨琢磨了。

从气质上讲,“关系型数据库”稳重持久,“非关系型数据库”迅速灵动。

在前端领域,Web SQL Database是“关系型数据库”,indexedDB是“非关系型数据库”。

2. 了解数据库中的事务-transaction

数据库的事务(英文为’transaction’),我们可以理解为对数据库的操作,而且专指一个序列上的操作。举个例子,银行转账,一个账号钱少了然后另一个账号钱多了,这两个操作要么都执行,要么都不执行。像这种操作就可以看成一个事务。

事务的提出主要是为了保证并发情况下保持数据一致性。关系型数据库中的事务具有下面4个基本特征:

  1. 原子性(Atomicity):事务中的所有操作作为一个整体提交或回滚。
  2. 一致性(Consistemcy):事物完成时,数据必须是一致的,以保证数据的无损。
  3. 隔离性(Isolation):对数据进行修改的多个事务是彼此隔离的。
  4. 持久性(Durability):事务完成之后,它对于系统的影响是永久的,该修改即使出现系统故障也将一直保留。

通常专业描述中会抽取这4个基本特性的首字母,统称为“ACID特性”。事务执行过程可以粗浅地理解为:开始事务,巴拉巴拉操作,如果错误,回滚(rollback),如果没问题,提交(commit),结束事务。

3. 了解数据库中的游标-cursor

现实世界中的“游标”相关联的常见事物就是“游标卡尺”,有刻度有区域:

数据库中的游标其实与之有共同之处。内存条本质上就像一把尺子,我们可以想象上面有很多刻度,然后内存大小就是由这些刻度一个一个堆砌起来的。数据库的事务为了保证数据可以回滚,显然需要有一片内存区域放置那些即将受影响是数据,这个内存区域中的虚表就是数据库的“游标”。

和现实世界的“游标卡尺”相映射就是:一个刻度表示一行数据,游标就是尺子上的一片区域,想要获得数据库一行一行的数据,我们可以遍历这个游标就好了。

4. 数据库中的“锁”-lock

数据库中的“锁”是保证数据库数据高并发时候数据一致性的一种机制。举个例子:现有两处火车票售票点,同时读取某发现上海到北京车票余额为5。此时两处售票点同时卖出一张车票,同时修改余额为5-1也就是4写回数据库,这样就造成了实际卖出两张火车票而数据库中的记录却只少了1张。为了避免发生这种状况,就有了锁机制,也就是执行多线程时用于强行限制资源访问。

三、从简单实用的实例开始学习indexedDB

要想系统学习indexedDB相关知识,可以去MDN文档啃API,假以时日就可以成为前端indexedDB方面的专家。但是这种学习方法周期长,过于痛苦,无法立竿见影,因为API实在太多,天天花个把小时,估计也需要数周时间才能全部通透。

一想到投入产出比这么低,于是我决定从实例入手开始学习,能够实现基本的数据库增删改查功能就好,基本上80%+的相关需求都能从容搞定;等日后有机会再慢慢深入。

花了几个晚上折腾,一个具有增删改无查的实例页面终于弄出来了,您可以狠狠地点击这里:HTMl5 indexedDB存储编辑和删除数据demo

我们第一次进去是没有数据的,显示如下:

1. 此时,我们填写表单,创建一条数据,如下:

点击“确定创建”按钮后,就得到如下图所示的结果:

此时我们刷新页面,依然是上图所示的结果。

打开控制台,在Application→indexedDB中,我们可以看到当前存储的数据字段和值等信息:

2. 我们可以直接对创建的数据进行修改,例如修改备注信息:

失焦后直接就修改了本地数据库信息了,我们刷新一下,可以看到备注文案已经变成“是个帅哥~”

3. 我们还可以对数据库数据进行删除,例如点击删除按钮:

结果这条数据库数据就被删掉了,界面重新显示为“暂无数据”。

以上就是demo页面所实现的indexedDB的增加数据,编辑数据和删除数据功能。

如果您当前的需求急用,可以把demo页面上的JS源代码直接复制过去改改;如果你希望对indexedDB相关API及其使用有所了解,就继续往下看。

1. 首先打开indexedDB数据库

indexedDB数据库打开非常简单,语法就是字面意思:

window.indexedDB.open(dbName, version);

dbName就是数据库名称,例如demo中使用的是'project';version表示数据库的版本,根据我的理解,当我们对数据库的字段进行增加或修改时候,需要增加个版本。

通常,打开indexedDB数据库是和一些回调方法一起出现的,代码套路比较固定,基本上如果大家在实际项目中使用的话,都可以使用类似的代码:

// 数据库数据结果
var db;
// 打开数据库
var DBOpenRequest = window.indexedDB.open('project', 1);
// 数据库打开成功后
DBOpenRequest.onsuccess = function(event) {        // 存储数据结果db = DBOpenRequest.result;// 做其他事情...
};// 下面事情执行于:数据库首次创建版本,或者window.indexedDB.open传递的新版本(版本数值要比现在的高)
DBOpenRequest.onupgradeneeded = function(event) {// 通常对主键,字段等进行重定义,具体参见demo
};

其中,最重要就是红色高亮的db变量,我们后面所有的数据库操作都离不开它。

2. 创建indexedDB数据库的主键和字段

在我们开始数据库的增加删除操作之前,首先要把我们数据库的主键和一些字段先建好。是在onupgradeneeded这个回调方法中设置的,这个回调方法执行于数据库首次创建版本或者数据库indexedDB.open()方法中传递的版本号比当前版本号要高。

我们对数据库某一行数据进行增加删除操作,我们是没有必要对数据库的版本号进行修改的。但是对于字段修改就不一样了,比方说原来是5列数据,我们现在改成6列,由于相关设置是在onupgradeneeded回调中,因此,我们需要增加版本号来触发字段修改。demo页面这部分代码如下:

DBOpenRequest.onupgradeneeded = function(event) {var db = event.target.result;// 创建一个数据库存储对象var objectStore = db.createObjectStore(dbName, { keyPath: 'id',autoIncrement: true});// 定义存储对象的数据项objectStore.createIndex('id', 'id', {unique: true    });objectStore.createIndex('name', 'name');objectStore.createIndex('begin', 'begin');objectStore.createIndex('end', 'end');objectStore.createIndex('person', 'person');objectStore.createIndex('remark', 'remark');
};

这里出现了一个比较重要的概念,叫做objectStore,这是indexedDB可以替代Web SQL Database的重要优势所在,我称之为“存储对象”。

objectStore.add()可以向数据库添加数据,objectStore.delete()可以删除数据,objectStore.clear()可以清空数据库,objectStore.put()可以替换数据等还有很多很多操作API。

在这里,我们使用objectStore来创建数据库的主键和普通字段。

db.createObjectStore在创建主键同时返回“存储对象”,本演示中使用自动递增的id字段作为关键路径。createIndex()方法可以用来创建索引,可以理解为创建字段,语法为:

objectStore.createIndex(indexName, keyPath, objectParameters)

其中:

indexName

创建的索引名称,可以使用空名称作为索引。

keyPath

索引使用的关键路径,可以使用空的keyPath, 或者keyPath传为数组keyPath也是可以的。

objectParameters

可选参数。常用参数之一是unique,表示该字段值是否唯一,不能重复。例如,本demo中id是不能重复的,于是有设置:objectStore.createIndex('id', 'id', { unique: true });

3. indexedDB数据库添加数据

字段建好了之后,我们就可以给indexedDB数据库添加数据了。

由于数据库的操作都是基于事务(transaction)来进行,于是,无论是添加编辑还是删除数据库,我们都要先建立一个事务(transaction),然后才能继续下面的操作。语法就是名称:

var transaction = db.transaction(dbName, "readwrite");

dbName就是数据库的名称,于是我们的数据库添加数据操作变得很简单:

// 新建一个事务
var transaction = db.transaction('project', "readwrite");
// 打开存储对象
var objectStore = transaction.objectStore('project');
// 添加到数据对象中
objectStore.add(newItem);

使用一行语句表示就是:

db.transaction('project', "readwrite").objectStore('project').add(newItem);

这里的newItem直接就是一个原生的纯粹的JavaScript对象,在本demo中,newItem数据类似下面这样:

{"name": "第一个项目","begin": "2024-07-16","end": "2054-07-16","person": "樊振东","remark": "恭喜大满贯"
}

可以发现,当我们使用indexedDB数据库添加数据的时候,根本就不用去额外学习SQL语句,原始的JavaScript对象直接无缝对接,,

4. indexedDB数据库的编辑

原理为,先根据id获得对应行的存储对象,方法为objectStore.get(id),然后在原存储对象上进行替换,再使用objectStore.put(record)进行数据库数据替换。

代码示意:

function edit(id, data) {// 新建事务var transaction = db.transaction('project', "readwrite");// 打开已经存储的数据对象var objectStore = transaction.objectStore(project);// 获取存储的对应键的存储对象var objectStoreRequest = objectStore.get(id);// 获取成功后替换当前数据objectStoreRequest.onsuccess = function(event) {// 当前数据var myRecord = objectStoreRequest.result;// 遍历替换for (var key in data) {if (typeof myRecord[key] != 'undefined') {myRecord[key] = data[key];}}// 更新数据库存储数据             objectStore.put(myRecord);};
}

其中,id就是要替换的数据库行的id字段值,因为唯一的主键,可以保证准确和高性能;data则是需要替换的数据对象,例如从“测试测试”修改为“是个帅哥~”,则调用:

edit(1, {remark: '是个帅哥~'
});

就可以了,跟我们平常写JS代码就没有什么区别。

5. indexedDB数据库的删除

和添加操作正好相反,但代码结构却是类似的,一行表示法:

db.transaction('project', "readwrite").objectStore('project').delete(id);

id表示需要删除行id字段对应的值。

6. indexedDB数据库的获取

indexedDB数据库的获取使用Cursor APIs和Key Range APIs。

也就是使用“游标API”和“范围API”。在本文的演示页面中,只使用了“游标API”,直接显示全部数据,对于“范围API”并没有使用,但这里也会简单介绍下。

“游标API”可以让我们一行一行读取数据库数据,代码示意:

var objectStore = db.transaction(dbName).objectStore(dbName);
objectStore.openCursor().onsuccess = function(event) {var cursor = event.target.result;if (cursor) {// cursor.value就是数据对象// 游标没有遍历完,继续cursor.continue();} else {// 如果全部遍历完毕...}
}

可以看到,我们使用存储对象的openCursor()打开游标,在onsuccess回调中就可以遍历我们的游标对象了。其中cursor.value就是完整的数据对象,纯JS对象,就像下面这样:

{"id":"1","name": "第一个项目","begin": "2024-07-16","end": "2054-07-16","person": "樊振东","remark": "恭喜大满贯"
}

我们就可以按照需求随意显示我们的数据了。

“范围API”是和“游标API”一起使用的,看下面这个例子,只打开id从4~10之间的数据,则有:

// 确定打开的游标的主键范围
var keyRangeValue = IDBKeyRange.bound(4, 10);
// 打开对应范围的游标
var objectStore = db.transaction(dbName).objectStore(dbName);
objectStore.openCursor(keyRangeValue).onsuccess = function(event) {var cursor = event.target.result;// ...
}

其中,有bound(), only(), lowerBound()和upperBound()这几个方法,意思就是方法名字面意思,“范围内”,“仅仅是”,“小于某值”和“大于某值”。

方法最后还支持两个布尔值参数,例如:

IDBKeyRange.bound(4, 10, true, true)

则表示范围3~9,也就是为true的时候不能和范围边界相等。

四、indexedDB存储和localStorage存储对比

  • indexedDB存储IE10+支持,localStorage存储IE8+支持,后者兼容性更好;
  • indexedDB存储比较适合键值对较多的数据,我之前不少项目需要存储多个字段,使用的是localStorage存储,结果每次写入和写出都要字符串化和对象化,很麻烦,如果使用indexedDB会轻松很多,因为无需数据转换。
  • indexedDB存储可以在workers中使用,localStorage貌似不可以。这就使得在进行PWA开发的时候,数据存储的技术选型落在了indexedDB存储上面。

总结下就是,如果是浏览器主窗体线程开发,同时存储数据结构简单,例如,就存个true/false,显然localStorage上上选;如果数据结构比较复杂,同时对浏览器兼容性没什么要求,可以考虑使用indexedDB;如果是在Service Workers中开发应用,只能使用indexedDB数据存储.

五、结束语

indexedDB数据库的使用目前可以直接在http协议下使用,这个和cacheStorage缓存存储必须使用https协议不一样。所以就应用场景来讲,indexedDB数据库还是挺广的,考虑到IE10也支持,所以基本可以确定在实际项目中应用是绝对不成问题的。

例如,页面中一些不常变动的结构化数据,我们就可以使用indexedDB数据库存储在本地,有助于增强页面的交互性能。cacheStorage缓存页面,indexedDB数据库缓存数据,两者一结合而就可以实现百分百的离线开发,听上去还是很厉害的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/53584.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于axios同步获取数据的问题

axios同步获取数据 Axios介绍问题代码修改 总结 Axios介绍 Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http 模块, 而在客户端 (浏览端) 则使用 X…

[SCOI2014] 方伯伯的玉米田(dp+树状数组维护行列)

https://www.luogu.com.cn/problem/P3287 显然每次操作的区间一定是一个后缀 我们直接令 d p ( x , i ) dp(x,i) dp(x,i) 表示最后一个数是 x x x(加之后),加了 i i i 次的最长长度,转移显然 max ⁡ d p ( y ≤ x , j ≤ i …

鸿蒙开发相册获取一张或多张照片,预览

import { photoAccessHelper } from kit.MediaLibraryKit; import { image } from kit.ImageKit; import { fileIo as fs } from kit.CoreFileKit;Entry Component struct Index {State getAlbum: string 显示相册中的图片;State pixelsArray : Array<image.PixelMap> |…

python测试开发---vue的常见指令

在 Vue.js 中&#xff0c;指令是用于在模板中绑定数据和 DOM 元素之间关系的特殊属性。指令通常以 v- 开头&#xff0c;后面跟上指令的名字。了解和使用这些指令是掌握 Vue.js 的关键之一。下面是一些 Vue.js 中常见的指令及其用法。 1. v-bind 作用&#xff1a;动态地绑定 H…

Spring Boot 集成 Redisson 实现消息队列

包含组件内容 RedisQueue&#xff1a;消息队列监听标识RedisQueueInit&#xff1a;Redis队列监听器RedisQueueListener&#xff1a;Redis消息队列监听实现RedisQueueService&#xff1a;Redis消息队列服务工具 代码实现 RedisQueue import java.lang.annotation.ElementTyp…

第2章 方法

本书作者起初以为仅靠研究命令行工具和指标就能提高性能。他认为这样不对。他从头到尾读了一遍手册&#xff0c;看懂了缺页故障、上下文切换和其他各种系统指标的定义&#xff0c;但不知道如何处理它们&#xff1a;如何从发现信号到找到解决方案。 他注意到&#xff0c;每当出…

18、公司信贷管理|贷款额度的测算|贷款期限及其定价的设定逻辑!

银行在综合权衡贷款的第一还款来源和第二还款来源、风险和收益的基础上&#xff0c;应明确提出贷与不贷的意见。经调查审查同意的贷款&#xff0c;应提出最终的融资方案。 合理的融资方案既要有利于提升本行的竞争力&#xff0c;又要有利于控制贷款风险。完整的融资方案一般包…

计算机网络 --- 【2】计算机网络的组成、功能

目录 一、计算机网络的组成 1.1 从组成部分看 1.2 从工作方式看 1.3 从逻辑功能看 1.4 总结 二、计算机网络的功能 2.1 数据通信 2.2 资源共享​编辑 2.3 分布式处理 2.4 提高可靠性 2.5 负载均衡 一、计算机网络的组成 1.1 从组成部分看 我们举例分析计算机网络从…

【人工智能学习笔记】4_4 深度学习基础之生成对抗网络

生成对抗网络&#xff08;Generative Adversarial Network, GAN&#xff09; 一种深度学习模型&#xff0c;通过判别模型&#xff08;Discriminative Model&#xff09;和生成模型&#xff08;Generative Model&#xff09;的相互博弈学习&#xff0c;生成接近真实数据的数据分…

19章 泛型

1.修改程序清单19-1中的GenericStack类&#xff0c;使用数组而不是ArrayList来实现它。你应该在给栈添加新元素之前检查数组的大小如果数组满了&#xff0c;就创建一个新数组。该数组是当前数组大小的两倍&#xff0c;然后将当前数组的元素复制到新数组中。 public class Gene…

​ArcGIS Pro和ArcGIS的10大区别

本文来源&#xff1a;水经注GIS公众号 如果你经常使用ArcGIS 进行制图和分析&#xff0c;那么你一定听说过ArcGIS Pro&#xff0c;这款软件是Esri未来主打的一款桌面GIS软件&#xff0c;那么这款软件和ArcGIS相比有什么不同呢&#xff0c;这里为你列举了两款软件的10大区别&am…

QScopedPointer的了解

QT 智能指针 QPointer QScopedPointer QSharedPointer QWeakPointer QSharedDataPointer 隐式共享 显示共享-CSDN博客 本文摘录自上面的文章 其类似于C11中的unique_ptr&#xff0c;用于管理动态分配的对象的独占所有权&#xff0c;即同一时间只能有一个QScopedPointer指向该…

Docker 安装 Nacos 教程

Nacos 是一个易于使用的平台&#xff0c;用于服务发现和配置管理。它支持服务的注册与发现&#xff0c;同时提供动态配置管理功能。本文将介绍如何使用 Docker 快速安装 Nacos&#xff0c;并提供相应的配置文件 standalone-mysql-5.7.yaml 用于设置 Nacos 与 MySQL 的集成。 参…

文本分类场景下微调BERT

How to Fine-Tune BERT for Text Classification 论文《How to Fine-Tune BERT for Text Classification?》是2019年发表的一篇论文。这篇文章做了一些实验来分析了如何在文本分类场景下微调BERT&#xff0c;是后面网上讨论如何微调BERT时经常提到的论文。 结论与思路 先来看…

Vue跨域问题、Vue配置开发环境代理服务、集成Axios发送Ajax请求、集成vue-resource发送Ajax请求

目录 1. Ajax请求服务介绍2. axios的安装3. Vue跨域问题介绍和解决方案4. 使用vue-cli配置开发环境代理服务4.1 简单配置4.2 复杂配置4.3 二次封装 5. 集成vue-resource发送Ajax请求 1. Ajax请求服务介绍 xhr: new XHLHttpRequest().open()/send()。偏向底层JQuery: 对xhr进行…

Sklearn的datasets模块与自带数据集介绍

datasets 模块 用 dir() 函数查看 datasets 模块的所有属性和函数 import sklearn.datasets as datasets# 列出 sklearn.datasets 模块中的所有属性和函数 print(dir(datasets)) datasets 模块下的数据集有三种类型&#xff1a; &#xff08;1&#xff09;load系列的经典数…

Sqoop 数据迁移

Sqoop 数据迁移 一、Sqoop 概述二、Sqoop 优势三、Sqoop 的架构与工作机制四、Sqoop Import 流程五、Sqoop Export 流程六、Sqoop 安装部署6.1 下载解压6.2 修改 Sqoop 配置文件6.3 配置 Sqoop 环境变量6.4 添加 MySQL 驱动包6.5 测试运行 Sqoop6.5.1 查看Sqoop命令语法6.5.2 测…

Vue 中 计算属性与侦听属性的使用与介绍

Vue 中 计算属性与侦听属性的使用与介绍 计算属性 - computed 计算属性是一种特殊的属性&#xff0c;它依赖于其他属性&#xff0c;并返回一个新的值。当依赖的属性发生变化时&#xff0c;计算属性会重新求值。 计算属性的语法如下&#xff1a; computed: {// 计算属性名: …

【数学建模】2024数学建模国赛经验分享

文章目录 一、关于我二、我的数模历程三、经验总结&#xff1a; 一、关于我 我的CSDN主页&#xff1a;https://gxdxyl.blog.csdn.net/ 2020年7月&#xff08;大二结束的暑假&#xff09;开始在CSDN写作&#xff1a; 阿里云博客专家&#xff1a; 接触的领域挺多的&#xff…

摩尔投票算法--169. 多数元素

169. 多数元素 普通方法-借助map计数 class Solution { public:int majorityElement(vector<int>& nums) {map<int,int> mp;for(int num :nums){mp[num];}for(auto &a :mp){if(a.second>nums.size()/2){return a.first;}}return 0;} }; 进阶&#xff…