Vue 场景(Solutions for Vue)
针对 Vue(Composition API)业务的复杂方案,结合 nex-lib 组件化使用,提供更少代码与更高可维护性。
搜索框(防抖 + 超时 + 重试)
js
import { debounce, Http } from 'nex-lib'
const search = debounce(async (q) => {
const data = await Http.get('/api/search', { query: { q }, timeoutMs: 3000, retry: { times: 3, delay: 200 } })
// render(data)
}, 300)ts
import { ref } from 'vue'
import { debounce, Http } from 'nex-lib'
const q = ref('')
const search = debounce(async (kw: string) => {
const data = await Http.get('/api/search', {
query: { q: kw },
timeoutMs: 3000,
retry: { times: 3, delay: 200 },
})
// 更新列表数据
}, 300)
function onInput(e: Event) { q.value = (e.target as HTMLInputElement).value; search(q.value) }节日倒计时(TTL + 校时)
js
import { nextAt, nowSeconds, formatDuration, Http, StorageUtils } from 'nex-lib'
// 目标:三天后 18:00(本地时区);刷新页面保持倒计时(TTL 缓存)
const cached = StorageUtils.getWithTTL('holiday_target')
const computedTarget = nextAt(3, 18)
const target = typeof cached === 'number' ? cached : computedTarget
StorageUtils.setWithTTL('holiday_target', target, 24*60*60*1000)
setInterval(async () => {
// 校时:优先服务端时间,失败回退本地时间
const now = await Http.get('/api/time', { timeoutMs: 3000, retry: 2 })
.then(r => r.now).catch(() => nowSeconds())
// 格式化显示,避免出现负数
document.querySelector('#countdown').textContent = formatDuration(Math.max(0, (target - now) * 1000))
}, 1000)ts
import { ref, onMounted, onUnmounted } from 'vue'
import { nextAt, nowSeconds, formatDuration, Http, StorageUtils } from 'nex-lib'
const remain = ref('00:00:00')
// 目标:三天后 18:00(本地时区);刷新页面保持倒计时(TTL 缓存)
const cached = StorageUtils.getWithTTL('holiday_target')
const computedTarget = nextAt(3, 18)
const target = typeof cached === 'number' ? cached : computedTarget
StorageUtils.setWithTTL('holiday_target', target, 24*60*60*1000)
let timer: any
async function tick() {
// 校时:优先服务端时间,失败回退本地时间
const now = await Http.get<{ now: number }>('/api/time', { timeoutMs: 3000, retry: 2 })
.then(r => r.now).catch(() => nowSeconds())
// 格式化显示,避免出现负数
remain.value = formatDuration(Math.max(0, (target - now) * 1000))
}
onMounted(() => { timer = setInterval(tick, 1000) })
onUnmounted(() => { clearInterval(timer) })渠道参数(同源 + 持久化 + 分享)
js
import { createWURL, StorageUtils, browserUtils } from 'nex-lib'
const w = createWURL(location.href)
const merged = w.replaceParams({ ...w.parseQueryParams(), utm_source: 'web', sku })
if (!w.isSameOrigin(merged)) throw new Error('unsafe')
StorageUtils.setWithTTL('utm', merged, 7*24*60*60*1000)
await browserUtils.copyToClipboard(merged)ts
import { createWURL, StorageUtils, browserUtils } from 'nex-lib'
const w = createWURL(location.href)
const merged = w.replaceParams({ ...w.parseQueryParams(), utm_source: 'web', sku })
if (!w.isSameOrigin(merged)) throw new Error('unsafe')
StorageUtils.setWithTTL('utm', merged, 7*24*60*60*1000)
await browserUtils.copyToClipboard(merged)主题色应用(CSS 变量)
js
import { ColorUtils } from 'nex-lib'
const brand = '#ffcc00'
document.documentElement.style.setProperty('--btn-bg', ColorUtils.darken(brand, .1))
document.documentElement.style.setProperty('--block-bg', ColorUtils.lighten(brand, .15))
document.documentElement.style.setProperty('--text', ColorUtils.contrastColor(ColorUtils.darken(brand, .1)))ts
import { ColorUtils } from 'nex-lib'
const brand = '#ffcc00'
const btnBg = ColorUtils.darken(brand, .1)
const blockBg = ColorUtils.lighten(brand, .15)
const textColor = ColorUtils.contrastColor(btnBg)
document.documentElement.style.setProperty('--btn-bg', btnBg)
document.documentElement.style.setProperty('--block-bg', blockBg)
document.documentElement.style.setProperty('--text', textColor)国际化(IP/语言/时区)
I18n Highlights
- 自动 Locale 与语言/国家英文名
- 本地时区与 UTC 偏移
- 一行获取公共 IP(带超时与重试)
- 示例展示统一 JSON 输出
js
import { browserUtils, TimeUtils } from 'nex-lib'
const locale = browserUtils.getPreferredLocale()
const li = browserUtils.getLocaleInfo(locale)
const timezone = TimeUtils.getTimezone()
const offsetMin = TimeUtils.getTimezoneOffsetMinutes()
const ip = await browserUtils.getPublicIP()
const info = { ip, locale: li.code, language: li.language.name, country: li.country.name || '', timezone, offsetMin, languages: browserUtils.getLanguage() }
// 渲染到页面:<pre id="i18n">...</pre>
document.querySelector('#i18n').textContent = JSON.stringify(info, null, 2)ts
import { ref, onMounted } from 'vue'
import { browserUtils, TimeUtils } from 'nex-lib'
type Info = { ip: string; locale: string; language: string; country: string; timezone: string; offsetMin: number; languages: string }
const info = ref<Info>({ ip: '-', locale: '', language: '', country: '', timezone: '', offsetMin: 0, languages: '' })
onMounted(async () => {
const locale = browserUtils.getPreferredLocale()
const li = browserUtils.getLocaleInfo(locale)
const timezone = TimeUtils.getTimezone()
const offsetMin = TimeUtils.getTimezoneOffsetMinutes()
const ip = await browserUtils.getPublicIP()
info.value = { ip, locale: li.code, language: li.language.name, country: li.country.name || '', timezone, offsetMin, languages: browserUtils.getLanguage() }
})