告别繁琐的 try-catch:JavaScript 安全赋值运算符 (?= ) 来了!

你是否厌倦了代码中难以阅读和维护的冗长 try-catch 代码块?全新的 ECMAScript 安全赋值运算符 (?= ) 将彻底改变游戏规则!这一突破性的特性简化了错误处理,让你的代码更简洁、更高效。让我们深入了解 ?= 运算符如何彻底改变你的编码体验!

提案:https://github.com/arthurfiorette/proposal-safe-assignment-operator

简化代码,轻松处理错误

告别嵌套的 try-catch 混乱

问题: 传统的 try-catch 代码块会导致代码深度嵌套,难以理解和调试。

解决方案: 使用 ?= 运算符,你可以将函数结果转换为一个元组,更优雅地处理错误。如果出现错误,你将得到 [error, null] ,如果一切正常,你将得到 [null, result] 。你的代码将会感谢你!

使用 ?= 之前:

async function fetchData() {
  try {
    const response = await fetch("https://api.example.com/data");
    try {
      const data = await response.json();
    &nb
<template> <view class="container"> <uni-nav-bar background-color="#f8f8f8" @clickLeft="handleBack" left-icon="left" :border="false" :title="title" /> <!-- 批量编辑工具栏 --> <view class="batch-edit-bar" v-if="purchaseList.length > 0"> <picker class="batch-picker" @change="batchFieldChange" :range="batchFields" range-key="name"> <view class="picker"> 批量修改:{{batchFields[batchFieldIndex].name}} </view> </picker> <input class="batch-input" v-model="batchValue" :placeholder="`输入${batchFields[batchFieldIndex].name}`" /> <button class="batch-btn" @click="applyBatchEdit">应用</button> </view> <!-- 采购列表 --> <view class="purchase-list"> <view v-for="(item, index) in purchaseList" :key="index" class="purchase-item"> <view class="purchase-content"> <view class="attr-item"> <text class="attr-label">采购编码:</text> <input class="attr-input" v-model="item.serial" placeholder="输入采购编码" /> </view> <view class="attr-item"> <text class="attr-label">采购时间:</text> <view class="attr-input" @click="showDatePicker(index)"> {{ item.date || '选择采购时间' }} </view> </view> <view class="attr-item"> <text class="attr-label">采购备注:</text> <input class="attr-input" v-model="item.remark" placeholder="输入采购备注" /> </view> </view> </view> </view> <!-- 日期选择弹窗 --> <uni-popup ref="popupdate" type="bottom"> <popupDate @changeDate="handleDateConfirm" @close="closeDatePopup" :date="currentDate"></popupDate> </uni-popup> <!-- 保存按钮 --> <view class="footer" v-if="purchaseList.length > 0"> <button class="save-btn" @click="saveToLocal"> 保存修改 ({{ purchaseList.length }}) </button> </view> <view v-else class="empty-tip"> <image src="https://cdn-icons-png.flaticon.com/512/4076/4076478.png" class="empty-img" /> <text>暂无采购数据</text> </view> </view> </template> <script> import popupDate from '../../../components/popup-date/popup-date.vue' export default { components: { popupDate }, data() { return { title: '批量编辑采购信息', purchaseList: [], batchFields: [ { name: '备注', field: 'remark' }, { name: '编码', field: 'serial' } ], batchFieldIndex: 0, batchValue: '', currentDate: {}, currentEditIndex: -1 } }, onLoad() { let purchaseList = this.$getStorageSync('purchaseList') if (purchaseList) { this.purchaseList = purchaseList } }, methods: { showDatePicker(index) { this.currentEditIndex = index this.currentDate = new Date(this.purchaseList[index].date ) this.$refs.popupdate.open() }, handleDateConfirm(date) { if (this.currentEditIndex >= 0) { this.$set(this.purchaseList, this.currentEditIndex, { ...this.purchaseList[this.currentEditIndex], date: this.$formatDate3(date) }) } }, closeDatePopup() { this.$refs.popupdate.close() this.currentEditIndex = -1 }, batchFieldChange(e) { this.batchFieldIndex = e.detail.value this.batchValue = '' }, applyBatchEdit() { if (!this.batchValue) { uni.showToast({ title: '请输入要设置的值', icon: 'none' }) return } const field = this.batchFields[this.batchFieldIndex].field this.purchaseList = this.purchaseList.map(item => ({ ...item, [field]: this.batchValue })) uni.showToast({ title: '批量修改成功', icon: 'success' }) }, saveToLocal() { try { this.$setStorageSync('purchaseList', this.purchaseList) uni.showToast({ title: '保存成功', icon: 'success' }) setTimeout(() => { uni.navigateBack() }, 1500) } catch (e) { uni.showToast({ title: '保存失败', icon: 'none' }) console.error('本地存储失败:', e) } }, handleBack() { uni.navigateBack() } } } </script> <style scoped> /* 保持原有样式不变 */ .container { display: flex; flex-direction: column; height: 100vh; background-color: #f5f5f5; } .batch-edit-bar { display: flex; align-items: center; padding: 15rpx 20rpx; background-color: #fff; border-bottom: 1rpx solid #eee; } .batch-picker { flex: 2; height: 70rpx; line-height: 70rpx; padding: 0 20rpx; border-radius: 8rpx; margin-right: 15rpx; font-size: 26rpx; background-color: #f5f5f5; } .batch-input { flex: 3; height: 70rpx; padding: 0 20rpx; border-radius: 8rpx; margin-right: 15rpx; font-size: 26rpx; background-color: #f5f5f5; } .batch-btn { flex: 1; height: 70rpx; line-height: 70rpx; background-color: #007aff; color: white; border-radius: 8rpx; font-size: 26rpx; } .purchase-list { flex: 1; overflow-y: auto; padding: 20rpx; } .purchase-item { background-color: #fff; border-radius: 12rpx; margin-bottom: 20rpx; padding: 20rpx; box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); } .attr-item { margin-bottom: 20rpx; } .attr-label { font-size: 26rpx; color: #666; display: block; margin-bottom: 8rpx; } .attr-input { width: 100%; height: 70rpx; border: 1rpx solid #eee; border-radius: 6rpx; padding: 0 15rpx; font-size: 26rpx; background-color: #f9f9f9; box-sizing: border-box; line-height: 70rpx; } .empty-tip { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; } .empty-img { width: 200rpx; height: 200rpx; margin-bottom: 30rpx; opacity: 0.6; } .footer { padding: 20rpx; background: #fff; border-top: 1rpx solid #eee; } .save-btn { background: #007aff; color: white; border-radius: 50rpx; height: 80rpx; line-height: 80rpx; font-size: 32rpx; } </style>
08-08
以下App.vue登入後,在其中的子組件post時發生csrf token missing: <div class="app-container"> <!-- 认证状态界面 --> <div v-if="!isAuthenticated" class="auth-container"> <div class="auth-switcher"> <button @click="authView = 'login'" :class="{ active: authView === 'login' }">登入</button> <button @click="authView = 'register'" :class="{ active: authView === 'register' }">註冊</button> </div> <component :is=“authView” @login-success=“handleLoginSuccess” @register-success=“authView = ‘login’” class=“auth-component” /> </div> <!-- 主应用界面 --> <div v-else> <!-- 顶部固定菜单 - 新增注销按钮 --> <div class="fixed-menu"> <!-- 医仙按钮 --> <button class="external-link"> 醫仙 </button> <!-- 动态组件切换按钮 --> <button @click="setCurrentView('mreditor')" :class="{ active: currentView === 'mreditor' }"> 醫案編輯 </button> <button @click="setCurrentView('mrassociator')" :class="{ active: currentView === 'mrassociator' }"> 醫案關聯 </button> <!-- 新增医案查询按钮 --> <button @click="setCurrentView('mrquery')" :class="{ active: currentView === 'mrquery' }"> 醫案查詢 </button> <!-- 醫案匯入器按钮 --> <button @click="setCurrentView('mrcrud')" :class="{ active: currentView === 'mrcrud' }"> 醫案匯入 </button> <button @click="setCurrentView('dncrud')" :class="{ active: currentView === 'dncrud' }"> 病態匯入 </button> <!-- 新增效驗匯入按钮 --> <button @click="setCurrentView('encrud')" :class="{ active: currentView === 'encrud' }"> 效驗匯入 </button> <!-- 新增論治匯入按钮 --> <button @click="setCurrentView('tncrud')" :class="{ active: currentView === 'tncrud' }"> 論治匯入 </button> <!-- 新增辨證匯入按钮 --> <button @click="setCurrentView('diancrud')" :class="{ active: currentView === 'diancrud' }"> 辨證匯入 </button> <!-- 人物匯入按钮 --> <button @click="setCurrentView('fncrud')" :class="{ active: currentView === 'fncrud' }"> 人物匯入 </button> <!-- 方劑匯入按钮 --> <button @click="setCurrentView('pncrud')" :class="{ active: currentView === 'pncrud' }"> 方劑匯入 </button> <button @click="setCurrentView('mncrud')" :class="{ active: currentView === 'mncrud' }"> 本草匯入 </button> <button @click="setCurrentView('sncrud')" :class="{ active: currentView === 'sncrud' }"> 典籍匯入 </button> <!-- 新增注销按钮 --> <button @click="handleLogout" class="logout-btn">登出</button> </div> <!-- 动态组件展示区域 --> <div class="content-wrapper"> <keep-alive :include="cachedComponents"> <component :is="currentViewComponent" :key="currentView" /> </keep-alive> </div> </div> </div> </template> <script> // 导入主应用组件 import mrassociator from "./components/mrassociator.vue"; import mrquery from "./components/mrquery.vue"; // 新增医案查询组件 import mreditor from "./components/mreditor.vue"; import dncrud from "./components/dncrud.vue"; import encrud from "./components/encrud.vue"; import tncrud from "./components/tncrud.vue"; import mncrud from "./components/mncrud.vue"; import mrcrud from "./components/mrcrud.vue"; import sncrud from "./components/sncrud.vue"; import pncrud from "./components/pncrud.vue"; import fncrud from "./components/fncrud.vue"; import diancrud from "./components/diancrud.vue"; // 导入认证组件 import Register from './legalization/register.vue'; import Login from './legalization/login.vue'; export default { components: { // 主应用组件 mrassociator, mrquery, // 注册医案查询组件 mreditor, dncrud, encrud, tncrud, mncrud, mrcrud, sncrud, pncrud, fncrud, diancrud, // 认证组件 register: Register, login: Login }, data() { return { // 主应用状态 currentView: "mrassociator", // 添加mrquery到缓存列表 cachedComponents: [ "mrassociator", "mrquery", "mreditor", "dncrud", "encrud", "tncrud", "mncrud", "mrcrud", "sncrud", "pncrud", "fncrud", "diancrud" ], // 认证状态 isAuthenticated: false, authView: 'login' // 'login' 或 'register' }; }, computed: { currentViewComponent() { return this.currentView; } }, methods: { // 主应用视图切换方法 setCurrentView(viewName) { if (this.currentViewComponent && this.currentViewComponent.beforeLeave) { this.currentViewComponent.beforeLeave(); } this.currentView = viewName; localStorage.setItem("lastActiveView", viewName); }, // 恢复最后查看的视图 restoreLastView() { const lastView = localStorage.getItem("lastActiveView"); if (lastView && this.cachedComponents.includes(lastView)) { this.currentView = lastView; } }, // 认证相关方法 handleLoginSuccess() { this.isAuthenticated = true; this.restoreLastView(); }, // 注销处理方法 async handleLogout() { try { // 从localStorage获取Token const authToken = localStorage.getItem('authToken'); // 获取CSRF token的辅助函数 const getCookie = (name) => { const cookieValue = document.cookie.match( `(^|;)\\s*${name}\\s*=\\s*([^;]+)` ); return cookieValue ? cookieValue.pop() : ''; }; const csrfToken = getCookie('csrftoken'); // 发送注销请求 const response = await fetch("api/logout/", { method: "POST", headers: { "X-CSRFToken": csrfToken, "Authorization": `Token ${authToken}` }, credentials: "include" }); if (response.ok) { // 清除认证状态 localStorage.removeItem("authToken"); this.isAuthenticated = false; this.authView = 'login'; console.log("用戶已成功登出"); } else { console.error(`登出失敗: ${response.status}`); } } catch (error) { console.error(`登出錯誤: ${error.message}`); } } }, mounted() { // 检查本地存储中是否有认证token this.isAuthenticated = !!localStorage.getItem('authToken'); // 如果已认证,恢复最后查看的视图 if (this.isAuthenticated) { this.restoreLastView(); } } }; </script> <style scoped> .app-container { display: flex; flex-direction: column; min-height: 100vh; } .fixed-menu { position: fixed; top: 0; left: 0; width: 100%; background: #2c3e50; padding: 10px; display: flex; gap: 10px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); z-index: 100; flex-wrap: wrap; /* 允许按钮换行 */ } .fixed-menu button { padding: 8px 16px; border: none; border-radius: 4px; background: #3498db; color: white; cursor: pointer; transition: background 0.3s; white-space: nowrap; /* 防止按钮文字换行 */ } .fixed-menu button:hover { background: #2980b9; } .fixed-menu button.active { background: #e74c3c; font-weight: bold; } .fixed-menu button.external-link { background: #0094ff; cursor: default; } .fixed-menu button.external-link:hover { background: #0094ff; } .content-wrapper { flex: 1; margin-top: 60px; padding: 20px; background: #f5f5f5; height: calc(100vh - 80px); overflow-y: auto; } /* 新增认证界面样式 */ .auth-container { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; background: #f5f7fa; } .auth-switcher { margin-bottom: 20px; display: flex; gap: 15px; } .auth-switcher button { padding: 12px 24px; border: none; border-radius: 6px; background: #e0e7ff; cursor: pointer; transition: all 0.3s; font-size: 16px; min-width: 100px; } .auth-switcher button.active { background: #6366f1; color: white; font-weight: bold; box-shadow: 0 4px 6px -1px rgba(99, 102, 241, 0.3); } .auth-component { width: 100%; max-width: 450px; padding: 30px; background: white; border-radius: 12px; box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); } /* 注销按钮样式 */ .logout-btn { margin-left: auto; background: #ef4444 !important; margin-right: 15px; } .logout-btn:hover { background: #dc2626 !important; } </style>
09-29
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夕阳_醉了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值