HarmonyOS开发 - 本地持久化之实现LocalStorage实例

        用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。数据存储形式为键值对,键的类型为字符串型,值的存储数据类型包括数字型、字符型、布尔型以及这3种类型的数组类型。

说明:
本模块首批接口从API version 9开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。

首选项无法保证进程并发安全,会有文件损坏和数据丢失的风险,不支持在多进程场景下使用。

        对于前端的朋友,可能使用像uni-app、小程序或web端中的本地持久化API习惯了,也能像使用sessionStorage或localStorage一样便捷。我们也可以利用HarmonyOS中API提供的用户首选项的功能,来实现LocalStorage实例。

        上述说明了用户首选项只能存储数字型、字符型、布尔型这3种数据类型,在对Preference实例封装过程中,在基础上再增加对json对象数据存储能力,并且实现数据缓存具有时效性等功能。

一、用户首选项

1.1、导入模块

import preferences from '@ohos.data.preferences'

1.2、获取实例

        获取Preferences实例,使用Promise异步回调(当然,获取实例方法也可以使用callback异步回调)。

// context为应用的上下文
preferences.getPreferences(context, 'application').then(preference => {
    // preference 实例名称
}).catch(err => {
    // error message
})

        参数说明:

参数名类型是否必填描述
contextContext应用上下文。
namestringPreferences实例的名称。
callbackAsyncCallback<Preferences>回调函数。当获取Preferences实例成功,err为undefined,返回Preferences实例;否则err为错误对象。

1.3、Preferences实例中的方法

        这些操作通常都是异步的,因此你需要使用Promise或者async/await来处理异步逻辑。

使用callback异步回调的方法:

方法名参数返回值描述
get()key: string, defValue: ValueType, callback: AsyncCallback<ValueType>void从缓存的Preferences实例中获取键对应的值,如果值为null或者非默认值类型,返回默认数据defValue。
getAll()callback: AsyncCallback<Object>void从缓存的Preferences实例中获取所有键值数据。
put()key: string, value: ValueType, callback: AsyncCallback<void>void将数据写入缓存的Preferences实例中,可通过flushflush将Preferences实例持久化。
has()key: string, callback: AsyncCallback<boolean>void检查缓存的Preferences实例中是否包含名为给定Key的存储键值对。
delete()key: string, callback: AsyncCallback<void>void从缓存的Preferences实例中删除名为给定Key的存储键值对,可通过flush将Preferences实例持久化。
flush()callback: AsyncCallback<void>void将缓存的Preferences实例中的数据异步存储到用户首选项的持久化文件中。
clear()callback: AsyncCallback<void>void清除缓存的Preferences实例中的所有数据,可通过flush将Preferences实例持久化。

        例如获取示例,代码如下:

// 获取对应键对应的值,默认为空
this.preference.get(key, '', (err, value) => {
  if(err) {
     console.log('preference error', err)
  }
})

使用Promise异步回调的方法:

方法名参数返回值描述
get()key: string, defValue: ValueTypePromise<ValueType>从缓存的Preferences实例中获取键对应的值,如果值为null或者非默认值类型,返回默认数据defValue
getAll()/Promise<Object>从缓存的Preferences实例中获取所有键值数据。
put()key: string, value: ValueTypePromise<void>将数据写入缓存的Preferences实例中,可通过flushflush将Preferences实例持久化
has()key: stringPromise<boolean>检查缓存的Preferences实例中是否包含名为给定Key的存储键值对。
delete()key: stringPromise<void>从缓存的Preferences实例中删除名为给定Key的存储键值对,可通过flush将Preferences实例持久化。
flush()/Promise<void>将缓存的Preferences实例中的数据异步存储到用户首选项的持久化文件中。
clear()/Promise<void>清除缓存的Preferences实例中的所有数据,可通过flush将Preferences实例持久化。

        例如添加示例,代码如下:

// 添加数据
this.preference.put(key, value).then(() => {
    this.preference.flush()
}).catch(e => {
    console.log('preference error', e)
})

二、封装LocalStorage

        在封装LocalStorage类之前,首先在ets目录下创建utils文件夹,并且在该文件夹下创建LocalStorage类文件。如下图:

3304fdf3750349f880e4f4ddeeec1a56.png

2.1定义LocalStorage类

       定义LocalStorage类,需要先做好几下几步准备:

  1. 获取Preferences实例对象,需要传入UIAbility组件的应用上下文,可通过"@ohos.app.ability.common"模块获取UIAbility类型,并将其赋给context形参。
  2. 定义私有preference变量,用于接收获取到的Preferences实例,后期可以直接调用,无需重复获取。
  3. 定义存储值类型vueType,根据用户首选项描述已知,其只能为number、string和boolean三种类型。
  4. 最后将LocalStorage类实例对象导出,作为单例模式导出。
import common from '@ohos.app.ability.common'
import preferences from '@ohos.data.preferences'

// 定义存储值类型
type valueType = string | number | boolean

/**
 * 定义LocalStorage类
 */
class LocalStorage {
  private preference: preferences.Preferences    // 定义首选项实例对象
}
/**
 * 实例LocalStorage
 */
const localStorage = new LocalStorage()

/**
 * 导出localStorage单例对象
 */
export default localStorage as LocalStorage

2.2 初始化并获取Preferences实例

     类定义好后,在类体内定义initial()函数,用来初始化并获取Preferences实例,后期LocalStorage类中方法直接使用preference对象即可。在获取Prefences实例时,需要给实例命名,这里则直接使用“应用上下文”的moduleName名称,即您定义项目时取的模块名称。代码如下:

// 定义初始化函数
initial(context: common.UIAbilityContext): void {
	// 这里将UIAbility中应用上下文的moduleName作用为实例名称,即该项目的application
	preferences.getPreferences(context, context.abilityInfo.moduleName).then(preference => {
		this.preference = preference	// 将获取实例赋值给私有变量
		console.log('preference success~')
	}).catch(e => {
		console.log('preference error', e)
	})
}

        这里使用Promise异步回调获取Preferences实例,如果习惯使用async/await朋友可以将其简单修改下即可,代码如下:

async initial(context: common.UIAbilityContext): void {
	// 这里将UIAbility中应用上下文的moduleName作用为实例名称,即该项目的application
	try {
		this.preference = await preferences.getPreferences(context, context.abilityInfo.moduleName)
	} catch (e) {
		console.log('preference error', e)
	}
}

2.3 UIAbility中初始化

        当initial()函数定义好后,我们将要在什么地方调用并初始化,并且context: common.UIAbilityContext中的应用上下文在哪获取? 我们可以在项目中打开Ability文件,会发现该类是继承UIAblity,所以该类也继承了父类的context应用上下文,onCreate()函数优先于页面之前,所以将initial()放到该方法中即可,并且通过this可直接过去context,将其放到initial()方法参数中。

9cae214e285243dd894eb452a0c2b7a4.png

        如果有朋友还在疑虑类中是否存在context问题,可以打开UIAblity模块,可以看到其类中定义了context变量,如下图:

92b2127f9dd74817bc02c602074213aa.png

        ApplicationAblity继承了UIAbility,所以我们在类中可以直接调用到context应用上下文。代码如下:

import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';
import LocalStorage from '../utils/LocalStorage'

export default class ApplicationAbility extends UIAbility {
  onCreate(want, launchParam) {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
    // 初始化本地存储
    LocalStorage.initial(this.context)
  }

  // 略...

}

        当LocalStorage类中initial()函数执行后,单例对象中的preferences实例也成功创建并且初始化。

注:如果你的Ability文件是ts文件,将其后缀改为ets即可,因为ets文件无法引入ts文件。

2.4 引用LocalStorage单例

        此时,我们在首页中引入LocalStorage实例,完成对数据的增、删、改、查等操作。

        打开pages/index.ets,添加一些相关操作功能按钮,代码如下:

import localStorage from '../utils/LocalStorage'
let index = 0
@Entry
@Component
struct Index {
  @State message: string = ''
  // 重置内容
  renderMsg(message: string | number | boolean){
    this.message = message.toString()
  }

  build() {
    Row() {
      Column() {
        Row(){
          Text(this.message || '-')
            .fontSize(50)
            .fontWeight(FontWeight.Bold)
        }.width('100%').height('150vp')

        Row(){
          // 添加相关操作按钮
          Button('添加').onClick(() => {
            
          })
          Button('读取').onClick(() => {
            
          })
          Button('删除').onClick(() => {

          })
          Button('清空').onClick(() => {

          })
        }.width('100%').justifyContent(FlexAlign.Center)
      }
      .width('100%')
    }
    .height('100%').alignItems(VerticalAlign.Top)
  }
}

        页面效果如下图:

5310a0db46cc4a82b577e3e93cbbab4e.png

2.5 添加数据

        打开utils\LocalStorage.ets文件,在类中添加put()函数,增加添加数据功能;前面执行initial()方法时,Preferences实例已初始过了,所以在类中函数直接调用即可;并且当通过Preferences实例成功添加数据后,需要调用flush()函数刷新下。代码如下:

 /**
   * 定义增加函数
   * @param key
   * @param value
   */
  put(key: string, value: valueType ): void {
    this.preference.put(key, value).then(() => this.preference.flush()).catch(e => {
      console.log('testTag error', e)
    })
  }

        打开pages\index.ets文件,执行添加数据,代码如下:

// 添加相关操作按钮
Button('添加').onClick(() => {
	localStorage.put('indexValue', ++index)
	this.renderMsg('add:' + index)
	console.log('testTag', index)
})

        点击添加按钮后,数据则通过Preferences实例保存到本地,页面呈现添加数据。如下图:

9c2d74776716494c8ad72aac8f2fd1ab.png

        有些朋友可能喜欢使用async/await写法,只需简单修改下即可。

        utils/LocalStorage.ets文件中代码修改如下:

async put(key: string, value: valueType) {
	try {
		await this.preference.put(key, value)
		await this.preference.flush()
	} catch (e) {
		console.log('testTag error', e)
	}
}

        pages/index.ets文件中代码修改如下:

Button('添加').onClick(async () => {
	await localStorage.put('indexValue', ++index)
	this.renderMsg('add:' + index)
	console.log('testTag', index)
})

        此时点击添加按钮,一样可以实现数据本地化存储。

2.6 获取数据

        获取比较简单了,打开utils/LocalStorage.ets文件,在类中增加获取数据功能。这里get()函数中第二个默认值给空即可,代码如下:

/**
 * 定义获取对应key数据
 * @param key
 */
getValue(key: string): Promise<valueType> {
  return this.preference.get(key, '') as Promise<valueType>
}

        在首页获取数据事件中,添加getValue()函数,获取缓存的数据。代码如下:

Button('读取').onClick(() => {
	localStorage.getValue('indexValue').then(value => {
		this.renderMsg('get:' + value)
		console.log('testTag', value)
	}).catch(err => {
		console.log('testTag error', err)
	})
})

        将获取到的数据显示到界面中,则拿到的是上次缓存的数据。如下图:

9a32a9fee12241248e8a76861d93ceb3.png

2.7 修改数据

        修改数据,直接使用put()函数,将数据重置即可。也可以LocalStorage类中添加update()函数,判断传入的内容与上次缓存一致,则不执行Preferences实例中put()方法。

        打开utils\LocalStorage.ets文件,在类中添加update()方法, 代码如下:

/**
* 更新数据
* @param key
* @param value
*/
async update(key: string, value: valueType){
	try {
		const preValue = await this.getValue(key)
        // 当更新内容与上次不一致时,修改数据
		if(preValue != value) {
			this.put(key, value)
		}
	} catch (e) {
		console.log('testTag error', e)
	}
}

2.8 移除数据

        移除指定数据,则通过Preferences实例中的delete()函数,打开utils\LocalStorage.ets文件,代码如下:

 /**
   * 定义移除函数
   * @param key
   */
remove(key: string): void {
	this.preference.delete(key).then(() => this.preference.flush()).catch(e => {
		console.log('testTag error', e)
	})
}

        打开pages\index.ets文件,在删除事件中添加移除功能,然后再通过get()函数重新获取本地存储的indexValue内容,代码如下:

Button('删除').onClick(async () => {
    // 移除indexValue键对应的值
	localStorage.remove('indexValue')
    // 重新获取indexValue
	const value = await localStorage.getValue('indexValue')
	this.renderMsg('delete:' + value)
	console.log('testTag delete', value)        // delete:
})

        先添加数据,然后执行删除事件,页面效果如下:

0d55fe63b4f04e99a5ed42773adf903f.pngb7ad53304c3248b58152a96994c7deba.png

2.9 清空数据

        清空数据则是将当前Preferences实例中,装饰所有键-值对内容进行清空,代码如下:

/**
   * 定义清除所有数据函数
   */
clearAll(): void {
	this.preference.clear().then(() => this.preference.flush()).catch(e => {
		console.log('testTag error', e)
	})
}

三、保存JSON对象数据

        例如要保存 {"name":"Tom","age":18} 结构的JSON对象数据,则需要在put()函数中将其转换为string类型数据,再将其进行保存;在获取时候,在getValue()方法中,识别并将其转换为JSON对象模式。

3.1 判断字符串是否为JSON

        首先打开utils/utils.ets,在该文件中添加判断字符串数据是否为object对象的函数;如果你的项目中未创建该文件,创建它即可。代码如下:

/**
 * 判断字符串是否为JSON对象
 */
export const isJsonObject = (value: string) : boolean => {
  try {
    const parseStr = JSON.parse(value)
    return 'object' === typeof parseStr && null !== parseStr
  } catch (e) {
    console.log('testTag', e)
    return false
  }
}

3.2 修改put()函数

        打开utils/LocalStorage.ets文件,先修改put()函数,让其支持存储json对象数据,代码如下:

put(key: string, value: valueType | object ): void {
    // 如果数据为object类型,将其转换为字符串类型数据进行存储
	if('object' === typeof value) {
	  value = JSON.stringify(value)
	}
	this.preference.put(key, value).then(() => this.preference.flush()).catch(e => {
		console.log('testTag error', e)
	})
}

3.3 修改getValue()函数

        在修改getValue()函数时,使用async/await写法当示例,显示更为简洁,代码如下:

async getValue(key: string): Promise<valueType> {
	let value = (await this.preference.get(key, '')) as valueType
	// 判断如果为字符串类型数据,并且为JSON对象格式数据,将其转换为对象
	if('string' === typeof value && isJsonObject(value)) {
		try {
			value = JSON.parse(value)
		} catch (e) {
            value = null
			console.log('testTag error', e)
		}
	}
	// 重新通过Promise异步回调将结果返回
	return Promise.resolve(value)
}

3.4 展示json存储能力

        当上述代码完成后,在添加按钮事件位置,将之前存储number数据更改为json数据,进行存储,再来看看效果。

        打开pages/index.est文件,更新添加事件,代码如下:

Button('添加').onClick(() => {
	const testData = { name: 'Tom', age: 18 };
	localStorage.put('indexValue', testData)
	this.renderMsg('add:' + JSON.stringify(testData))
	console.log('testTag add', testData)
})

        重新获取本地存储值时,输出为Object,说明已转换成功。如下图:

fd692a9135cc4fe18ec76d6f6e095b4e.pngd45e9abb9c174eb9803f4518df043f1a.png

四、添加时效性

        有时某些数据需要在规定时间内才有效,这则需要对存储数据增加时效性的能力,即在存储数据过程中添加失效的时间,并在获取时判断是否在有效期内;这样所有保存的数据,将都需要转化为JSON对象格式的字符串进行存储。

        首先,我们将之前LocalStorage.ets文件拷贝份,在之前基础上将其改造,增加时效性能力。如下图:

4.1 定义json数据存储类型

        在修改存储方式前,需要定义json格式对象类型,在put()函数中存储前和getValue()函数中获取结果通过JSON.parse转换后的数据,都需要该类型进行约束。

// 定义json对象存储类型
type dataType = { value: valueType | object, expire: number }

4.2 修改put()函数

        当put()方法中,添加失效时间expire时,则将其合并到JSON数据中一起存储;如果无expire失效时间,则传入-1,表示此数据长久有效,除非主动清除。

        修改后代码如下:

  /**
   * 定义增加函数
   * @param key
   * @param value
   * @param expire
   */
  put(key: string, value: valueType | object, expire?: Date): void {
    // 定义存储Json格式对象
    const data = {
      value,      // 存储内容
      expire : (expire ? expire.getTime() : -1)   // 如果失效时间存在,将其转换为时间戳,否则传入-1
    }
    let dataStr: string;
    try {
      dataStr = JSON.stringify(data)    // 当数据转换成功,将其存储
    } catch (e) {
      console.log('testTag error', e)
      return
    }
    this.preference.put(key, dataStr).then(() => this.preference.flush()).catch(e => {
      console.log('testTag error', e)
    })
  }

4.3 修改getValue()函数

        在getValue()函数中,在获取数据时,除了需要将字符串数据转换为JSON格式对象外,还需判断其中expire字段,当前存储内容是否在有效期内;在有效期内则返回,不在则则返回空(null)。

        修改后代码如下:

  /**
   * 定义获取对应key数据
   * @param key
   */
  async getValue(key: string): Promise<valueType | object> {
    // 首页判断key值是否存在,不存在返回空
    if(!this.preference.has(key)) {
      return Promise.resolve(null)
    }
    let value = (await this.preference.get(key, '')) as valueType
    // 判断如果为字符串类型数据,并且为JSON对象格式数据,将其转换为对象
    if('string' === typeof value && isJsonObject(value)) {
      try {
        const data: dataType = JSON.parse(value)
        // 如果当前存储内容无时效性,或者在时效期内,都直接返回
        if(data.expire === -1 || data.expire > Date.now()) {
          return Promise.resolve(data.value)
        }
        // 如果已失效,将其信息删除
        else {
          this.preference.delete(key)
        }
      } catch (e) {
        console.log('testTag error', e)
        return Promise.resolve(null)      // 如果转换出错,返回null
      }
    } 
    // 通过Promise异步回调将结果返回(如果内容不为JSON格式对象,或者过了时效期,返回null)
    return Promise.resolve(null)
  }

  4.4 更改导入模块

        需要注意的是,我们将需要applicationAbility.ets和index.ets中的导入模块进行修改,否则引入还是之前LocalStorage.ets中类,刚非新创建的LocalStorageObj.ets。

        示例如下:

// 旧导入模块
import LocalStorage from '../utils/LocalStorage'

// 引入新模块,替换掉旧的LocalStorage
import LocalStorage from '../utils/LocalStorageObj'

  4.5 展示时效性

        以上两个函数修改完成后,我们来执行下看看结果。打开pages/index.ets,在添加事件中,增加数据缓存的时效性。

        首先传一个失效日期进去,看看获取的结果是什么。代码如下:

  Button('添加').onClick(() => {
	const testData = { name: 'Tom', age: 18 };
	const expireDate = new Date()
	// 保存一个失效10分钟的日期
	expireDate.setMinutes(expireDate.getMinutes() - 10)
	// 存储数据
	localStorage.put('indexValue', testData, expireDate)
	this.renderMsg('add:' + JSON.stringify(testData))
	console.log('testTag add', testData)
  })
  Button('读取').onClick(() => {
	localStorage.getValue('indexValue').then(value => {
	  this.renderMsg('get:' + value)
	  console.log('testTag get', value)
	}).catch(err => {
	  console.log('testTag error', err)
	})
  })

        页面效果可见,添加了json数据后,获取结果为null;这是因为在添加时,将日期设置为失效10分钟了。如下图:

        我们再将时间设置为有效后,再来看看结果。代码如下:

  Button('添加').onClick(() => {
	const testData = { name: 'Tom', age: 18 };
	const expireDate = new Date()
	// 设置为24小时后失效
	expireDate.setHours(expireDate.getHours() + 24)
	// 存储数据
	localStorage.put('indexValue', testData, expireDate)
	this.renderMsg('add:' + JSON.stringify(testData))
	console.log('testTag add', testData)
  })
  Button('读取').onClick(() => {
	localStorage.getValue('indexValue').then(value => {
	  this.renderMsg('get:' +  (null !== value ? JSON.stringify(value) : value))
	  console.log('testTag get', value)
	}).catch(err => {
	  console.log('testTag error', err)
	})
  })

        页面效果可见,在有效期内成功获取到了存储数据。如下图:

六、完整代码

       下面则是该篇所有示例完整代码。

        uitls/utils.ets文件代码如下:

/**
 * 判断字符串是否为JSON对象
 */
export const isJsonObject = (value: string) : boolean => {
  try {
    const parseStr = JSON.parse(value)
    return 'object' === typeof parseStr && null !== parseStr
  } catch (e) {
    console.log('testTag', e)
    return false
  }
}

        utils/LocalStorageObj.ets文件代码如下:

import common from '@ohos.app.ability.common'
import preferences from '@ohos.data.preferences'
import { isJsonObject } from './utils'

// 定义存储值类型
type valueType = string | number | boolean
// 定义json对象存储类型
type dataType = { value: valueType | object, expire: number }

/**
 * 定义LocalStorage类
 */
export class LocalStorage {
  private preference: preferences.Preferences // 用户首选项实例对象

  // 定义初始化函数
  initial(context: common.UIAbilityContext): void {
    // 这里将UIAbility中应用上下文的moduleName作用为实例名称,即该项目的application
    preferences.getPreferences(context, context.abilityInfo.moduleName).then(preference => {
      this.preference = preference
      console.log('testTag', 'success~')
    }).catch(e => {
      console.log('testTag error', e)
    })
  }
  /**
   * 定义增加函数
   * @param key
   * @param value
   * @param expire
   */
  put(key: string, value: valueType | object, expire?: Date): void {
    // 定义存储Json格式对象
    const data : dataType = {
      value,      // 存储内容
      expire : (expire ? expire.getTime() : -1)   // 如果失效时间存在,将其转换为时间戳,否则传入-1
    }
    let dataStr: string = '';
    try {
      dataStr = JSON.stringify(data)    // 当数据转换成功,将其存储
      console.log('testTag', dataStr)
    } catch (e) {
      console.log('testTag error', e)
      return
    }
    this.preference.put(key, dataStr).then(() => this.preference.flush()).catch(e => {
      console.log('testTag error', e)
    })
  }

  /**
   * 定义获取对应key数据
   * @param key
   */
  async getValue(key: string): Promise<valueType | object> {
    // 首页判断key值是否存在,不存在返回空
    if(!this.preference.has(key)) {
      return Promise.resolve(null)
    }
    let value = (await this.preference.get(key, '')) as valueType
    // 判断如果为字符串类型数据,并且为JSON对象格式数据,将其转换为对象
    if('string' === typeof value && isJsonObject(value)) {
      try {
        const data: dataType = JSON.parse(value)
        console.log('testTag', data.expire, Date.now(), data.expire < Date.now())
        // 如果当前存储内容无时效性,或者在时效期内,都直接返回
        if(data.expire === -1 || data.expire > Date.now()) {
          return Promise.resolve(data.value)
        }
        // 如果已失效,将其信息删除
        else {
          this.preference.delete(key)
        }
      } catch (e) {
        console.log('testTag error', e)
        return Promise.resolve(null)      // 如果转换出错,返回null
      }
    }
    // 通过Promise异步回调将结果返回(如果内容不为JSON格式对象,或者过了时效期,返回null)
    return Promise.resolve(null)
  }

  /**
   * 更新数据
   * @param key
   * @param value
   */
  async update(key: string, value: valueType){
    try {
      const preValue = await this.getValue(key)
      if(preValue != value) {
        this.put(key, value)
      }
    } catch (e) {
      console.log('testTag error', e)
    }
  }
  /**
   * 定义移除函数
   * @param key
   */
  remove(key: string): void {
    this.preference.delete(key).then(() => this.preference.flush()).catch(e => {
      console.log('testTag error', e)
    })
  }
  /**
   * 定义清除所有数据函数
   */
  clearAll(): void {
    this.preference.clear().then(() => this.preference.flush()).catch(e => {
      console.log('testTag error', e)
    })
  }
}
/**
 * 实例LocalStorage
 */
const localStorage = new LocalStorage()

/**
 * 导出localStorage单例对象
 */
export default localStorage as LocalStorage

        pages/index.ets首页代码如下:

import localStorage from '../utils/LocalStorageObj'
let index = 0
@Entry
@Component
struct Index {
  @State message: string = ''
  // 重置内容
  renderMsg(message: string | number | boolean){
    this.message = message.toString()
  }

  build() {
    Row() {
      Column() {
        Row(){
          Text(this.message || '-')
            .fontSize(30)
            .fontWeight(FontWeight.Bold)
        }.width('100%').height('150vp')

        Row(){
          // 添加相关操作按钮
          Button('添加').onClick(() => {
            const testData = { name: 'Tom', age: 18 };
            const expireDate = new Date()
            // 设置为24小时后失效
            expireDate.setHours(expireDate.getHours() + 24)
            // 存储数据
            localStorage.put('indexValue', testData, expireDate)
            this.renderMsg('add:' + JSON.stringify(testData))
            console.log('testTag add', testData)
          })
          Button('读取').onClick(() => {
            localStorage.getValue('indexValue').then(value => {
              this.renderMsg('get:' +  (null !== value ? JSON.stringify(value) : value))
              console.log('testTag get', value)
            }).catch(err => {
              console.log('testTag error', err)
            })
          })
          Button('删除').onClick(async () => {
            localStorage.remove('indexValue')
            const value = await localStorage.getValue('indexValue')
            this.renderMsg('delete:' + value)
            console.log('testTag delete', value)
          })
          Button('清空').onClick(() => {

          })
        }.width('100%').justifyContent(FlexAlign.Center)
      }
      .width('100%')
    }
    .height('100%').alignItems(VerticalAlign.Top)
  }
}

        此篇先讲到这里,希望对大家有所帮助。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

重学SpringBoot3-怎样优雅停机

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-怎样优雅停机 1. 什么是优雅停机&#xff1f;2. Spring Boot 3 优雅停机的配置3. Tomcat 和 Reactor Netty 的优雅停机机制3.1 Tomcat 优雅停机3.2 Reac…

【C++初阶】模版入门看这一篇就够了

文章目录 1. 泛型编程2. 函数模板2. 1 函数模板概念2. 2 函数模板格式2. 3 函数模板的原理2. 4 函数模板的实例化2. 5 模板参数的匹配原则2. 6 补充&#xff1a;使用调试功能观察函数调用 3. 类模板3 .1 类模板的定义格式3. 2 类模板的实例化 1. 泛型编程 在C语言中&#xff0…

numpy——数学运算

一、标量——矢量 import numpy as npa 3.14 b np.array([[9, 5], [2, 7]])print(a) print(b)# ---------- 四则运算 ---------- print(a b) # np.add print(a - b) # np.subtract print(a * b) # np.multiply print(a / b) # np.divide 二、矢量——矢量 import nump…

基于边缘计算的智能门禁系统架构设计分析

案例 阅读以下关于 Web 系统架构设计的叙述&#xff0c;回答问题1至问题3。 【说明】 某公司拟开发一套基于边缘计算的智能门禁系统&#xff0c;用于如园区、新零售、工业现场等存在来访被访业务的场景。来访者在来访前&#xff0c;可以通过线上提前预约的方式将自己的个人信息…

CAS的ABA问题

目录 什么是 CAS CAS最主要的用途&#xff0c;实现原子类 基于CAS实现自旋锁 CAS的一个典型缺陷&#xff0c;ABA问题 解决 ABA 问题的方法 什么是 CAS CAS: 全称Compare and swap&#xff0c;字⾯意思:”⽐较并交换“&#xff0c;⼀个 CAS 涉及到以下操作&#xff1a; 我…

基于yolov8的布匹缺陷检测系统,支持图像、视频和摄像实时检测【pytorch框架、python源码】

更多目标检测和图像分类识别项目可看我主页其他文章 功能演示&#xff1a; 基于yolov8的布匹缺陷检测系统&#xff0c;支持图像、视频和摄像实时检测【pytorch框架、python源码】_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于yolov8的布匹缺陷检测系统是在 PyTo…

基于SSM+小程序的童装商城管理系统(商城3)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于SSM小程序的童装商城管理系统实现了管理员及用户。 1、管理员实现了 首页、个人中心、用户管理、分类列表管理、童装商城管理、系统管理、订单管理。 2、用户实现了 注册、登录、首…

一家光伏企业终止,恐不具行业代表性,市占率仅为2.35%

海达光能终止原因如下&#xff1a;报告期内海达光能销售金额较所在行业第二名亚玛顿相差两倍以上&#xff0c;公司毛利率更是远低于行业龙头福莱特&#xff0c;恐难以说明公司行业代表性。在企业竞争上&#xff0c;公司2021年度的市场占有率约为2.35%&#xff0c;公司未来光伏玻…

丁子晴作品《指尖的爱的温度》荣获“金犊奖”全球最佳新锐奖

第33届时报金犊奖颁奖盛典于10月18日在中国成都西部智谷数字体验中心隆重举行。丁子晴的作品《指尖的爱的温度》在激烈的竞争中脱颖而出,荣获了第33届“金犊奖”全球最佳新锐奖。享有盛誉的“金犊奖”是一个全球性的奖项,以其专业严谨、创意水平高的特点,被业界誉为“青年创意的…

kaggle 数据集下载

文章目录 kaggle 数据集下载&#xff08;1&#xff09; 数据集下载&#xff08;2&#xff09; 手机号验证 kaggle 数据集下载 这两天想学习 kaggle 赛事 把深度学习相关的内容自己给过一遍&#xff0c;快忘得差不多了&#xff0c;惭愧。 参考了好多帖子&#xff0c;使用命令行…

vue2项目在发布后更新,提示用户刷新页面

1、在项目根目录创建resetVersion.js的文件&#xff0c;内容如下 &#xff08;具体路径可能会有点问题&#xff0c;但是不影响&#xff09; const path require(path); const fsExtra require(fs-extra);const runBuild async () > {try {const OUTPUT_DIR public; // …

13.音乐管理系统(基于SpringBoot + Vue)

目录 1.系统的受众说明 ​​​​​​​ 2 需求分析 2.1用例图及用例分析 2.1.1 用户用例图及用例分析 2.1.2 管理员用例图及用例分析 2.2 系统结构图和流程图 2.2.1 音乐播放器的系统流程图&#xff08;图2.2.1-1&#xff09; 2.2.2 系统功能表&#xff08;表2.2.2…

MySQL用户权限管理属于SQL语句中的DCL语句

1.用户授权 语法&#xff1a;grant 权限&#xff0c;权限&#xff0c;on 库名&#xff0c;表名 to 用户名 [identified by 密码] MySQL5的版本&#xff0c;如果这个用户事先不存在&#xff0c;这个grant命令去给用户授权的时候&#xff0c;会将用户一起创建出来&#xff0…

时间序列预测(十五)——有关Python项目框架的实例分析

#1024程序员节&#xff5c;征文# 在之前的学习中&#xff0c;已经对时间序列预测的相关内容有了大致的了解。为了进一步加深理解&#xff0c;并能够将所学知识应用于实际中&#xff0c;我决定找一个完整的Python框架来进行深入学习。经过寻找&#xff0c;我终于找到了一篇非常具…

业务流程顺畅度为何受制于数据失真

在当今数字化驱动的商业环境中&#xff0c;企业的业务流程高度依赖于数据的准确性和完整性。然而&#xff0c;数据失真问题却如同隐匿在流程中的“暗礁”&#xff0c;频繁地给企业的业务流程顺畅度带来严重挑战&#xff0c;进而影响企业的整体运营效率和竞争力。 数据失真的表…

基于hive分析Flask为后端框架echarts为前端框架的招聘网站可视化大屏项目

基于hive分析Flask为后端框架echarts为前端框架的招聘网站可视化大屏项目 1. 项目概述 项目目标是构建一个大数据分析系统&#xff0c;包含以下核心模块&#xff1a; 1、数据爬取&#xff1a;通过request请求获取猎聘网的就业数据。 2、数据存储和分析&#xff1a;使用 Hive …

GB/T 28046.1-2011 道路车辆 电气及电子设备的环境条件和试验 第1部分:一般规定(2)

写在前面 本系列文章主要讲解道路车辆电气及电子设备的环境条件和试验GB/T 28046标准的相关知识&#xff0c;希望能帮助更多的同学认识和了解GB/T 28046标准。 若有相关问题&#xff0c;欢迎评论沟通&#xff0c;共同进步。(*^▽^*) 第1部分&#xff1a;一般规定 7. 试验和要…

GetMaterialApp组件的用法

文章目录 1. 知识回顾2. 使用方法2.1 源码分析2.2 常用属性3. 示例代码4. 内容总结我们在上一章回中介绍了"Get包简介"相关的内容,本章回中将介绍GetMaterialApp组件.闲话休提,让我们一起Talk Flutter吧。 1. 知识回顾 我们在上一章回中已经介绍过GetMaterialApp组…

自适应阻抗案例分析(上)

案例分析一 Duchaine,V.,Gosselin,C.(2009).Safe,stable and intuitive control for physical human-robot interaction.In IEEE international conference on robotics and automation,2009.ICRA09(pp.3383-3388).IEEE. 主要贡献 1.利用外力反馈调整阻尼系数&#xff0c;同…

(三)行为模式:11、模板模式(Template Pattern)(C++示例)

目录 1、模板模式含义 2、模板模式的UML图学习 3、模板模式的应用场景 4、模板模式的优缺点 5、C实现的实例 1、模板模式含义 模板模式&#xff08;Template Method Pattern&#xff09;是一种行为设计模式&#xff0c;它定义了一个操作中的算法骨架&#xff0c;将某些步骤…