侧边栏壁纸
“学习日记” 共(42)篇
  • JAVA 的多线程编程 java的多线程编程下图展示了 java 线程的生命周期当我们只调用 run()方法时,线程并不会被启动,只会执行 run();只有当调用 start()方法时,线程才会被启动,并且调用 run()方法;我们常用的 public static void main(String[] args) main 其实是 java 中的主线程。java创建线程的三种方式通过实现 Runnable 接口 创建一个线程,最简单的方法是创建一个实现 Runnable 接口的类。为了实现 Runnable,一个类只需要执行一个方法调用 run(),声明如下:public void run()你可以重写该方法,重要的是理解的 run() 可以调用其他方法,使用其他类,并声明变量,就像主线程一样。在创建一个实现 Runnable 接口的类之后,你可以在类中实例化一个线程对象。Thread 定义了几个构造方法,下面的这个是我们经常使用的: 教程参考菜鸟教程 Thread(Runnable threadOb,String threadName);这里,threadOb 是一个实现 Runnable 接口的类的实例,并且 threadName 指定新线程的名字。新线程创建之后,你调用它的 start() 方法它才会运行。 通过继承 Thread 类本身创建一个线程的第二种方法是创建一个新的类,该类继承 Thread 类,然后创建一个该类的实例。继承类必须重写 run() 方法,该方法是新线程的入口点。它也必须调用 start() 方法才能执行。该方法尽管被列为一种多线程实现方式,但是本质上也是实现了 Runnable 接口的一个实例。class ThreadDemo extends Thread { private Thread t; private String threadName; ThreadDemo( String name) { threadName = name; System.out.println("Creating " + threadName ); } public void run() { System.out.println("Running " + threadName ); try { for(int i = 4; i > 0; i--) { System.out.println("Thread: " + threadName + ", " + i); // 让线程睡眠一会 Thread.sleep(50); } }catch (InterruptedException e) { System.out.println("Thread " + threadName + " interrupted."); } System.out.println("Thread " + threadName + " exiting."); } public void start () { System.out.println("Starting " + threadName ); if (t == null) { t = new Thread (this, threadName); t.start (); } } } public class TestThread { public static void main(String args[]) { ThreadDemo T1 = new ThreadDemo( "Thread-1"); T1.start(); ThreadDemo T2 = new ThreadDemo( "Thread-2"); T2.start(); } }下表列出了Thread类的一些重要方法:序号方法描述1.使该线程开始执行;Java 虚拟机调用该线程的 run 方法public void start();2.如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回public void run();3.改变线程名称,使之与参数 name 相同。public final void setName(String name);4.更改线程的优先级。public final void setPriority(int priority);5.将该线程标记为守护线程或用户线程。public final void setDaemon(boolean on);6.等待该线程终止的时间最长为 millis 毫秒。public final void join(long millisec);7.中断线程。public void interrupt();8.测试线程是否处于活动状态。public final boolean isAlive();上述方法是被 Thread 对象调用的,下面表格的方法是 Thread 类的静态方法。序号方法描述1.暂停当前正在执行的线程对象,并执行其他线程。public static void yield()2.在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。public static void sleep(long millisec)3.当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。public static boolean holdsLock(Object x)4.返回对当前正在执行的线程对象的引用。public static Thread currentThread()5.将当前线程的堆栈跟踪打印至标准错误流。public static void dumpStack()方法演示// 文件名 : DisplayMessage.java // 通过实现 Runnable 接口创建线程 public class DisplayMessage implements Runnable { private String message; public DisplayMessage(String message) { this.message = message; } public void run() { while(true) { System.out.println(message); } } }// 文件名 : GuessANumber.java // 通过继承 Thread 类创建线程 public class GuessANumber extends Thread { private int number; public GuessANumber(int number) { this.number = number; } public void run() { int counter = 0; int guess = 0; do { guess = (int) (Math.random() * 100 + 1); System.out.println(this.getName() + " guesses " + guess); counter++; } while(guess != number); System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**"); } }// 文件名 : ThreadClassDemo.java public class ThreadClassDemo { public static void main(String [] args) { Runnable hello = new DisplayMessage("Hello"); Thread thread1 = new Thread(hello); thread1.setDaemon(true); thread1.setName("hello"); System.out.println("Starting hello thread..."); thread1.start(); Runnable bye = new DisplayMessage("Goodbye"); Thread thread2 = new Thread(bye); thread2.setPriority(Thread.MIN_PRIORITY); thread2.setDaemon(true); System.out.println("Starting goodbye thread..."); thread2.start(); System.out.println("Starting thread3..."); Thread thread3 = new GuessANumber(27); thread3.start(); try { thread3.join(); }catch(InterruptedException e) { System.out.println("Thread interrupted."); } System.out.println("Starting thread4..."); Thread thread4 = new GuessANumber(75); thread4.start(); System.out.println("main() is ending..."); } }通过 Callable 和 Future 创建线程创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。public class CallableThreadTest implements Callable<Integer> { public static void main(String[] args) { CallableThreadTest ctt = new CallableThreadTest(); FutureTask<Integer> ft = new FutureTask<>(ctt); for(int i = 0;i < 100;i++) { System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i); if(i==20) { new Thread(ft,"有返回值的线程").start(); } } try { System.out.println("子线程的返回值:"+ft.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } @Override public Integer call() throws Exception { int i = 0; for(;i<100;i++) { System.out.println(Thread.currentThread().getName()+" "+i); } return i; } }
    • 1年前
    • 278
    • 0
    • 1
  • 软件测试省赛功能测试答案
    软件测试省赛功能测试答案 import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.interactions.Actions; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.By; import org.openqa.selenium.Alert; @SuppressWarnings("unused") public class Example { // Mooctest Selenium Example // <!> Check if selenium-standalone.jar is added to build path. public static void test(WebDriver driver) { // TODO Test script // eg:driver.get("https://www.baidu.com/") // eg:driver.findElement(By.id("wd")); try { driver.get("https://www.ifeng.com/"); driver.manage().window().maximize(); Thread.sleep(20000); driver.findElement(By.linkText("资讯")).click();//1咨询 Thread.sleep(1500); driver.switchTo().window(driver.getWindowHandles().toArray()[1].toString()); Thread.sleep(1500); driver.findElement(By.linkText("新时代")).click();//2新时代 Thread.sleep(1500); driver.switchTo().window(driver.getWindowHandles().toArray()[2].toString()); Thread.sleep(1500); driver.findElement(By.linkText("+ 更多新闻")).click();//3更多新闻 Thread.sleep(1500); driver.findElement(By.className("index_go_to_top_A0C-y")).click();//4置顶 Thread.sleep(1500); driver.findElement(By.linkText("凤凰资讯")).click();//5选择凤凰资讯 Thread.sleep(1500); driver.switchTo().window(driver.getWindowHandles().toArray()[3].toString()); Thread.sleep(1500); // driver.findElement(By.className("index_checked_2L1JS")).click();//6站内框 // Thread.sleep(1500); // driver.findElement(By.linkText("站内")).click();//7站内 // Thread.sleep(2500); driver.findElement(By.xpath("/html/body/div[1]/div[3]/div[3]/div[2]/input")).sendKeys("科技");//6搜索框 Thread.sleep(1500); driver.findElement(By.className("index_btn_S-5T7")).click();//7点击 Thread.sleep(1500); driver.switchTo().window(driver.getWindowHandles().toArray()[4].toString()); Thread.sleep(1500); // driver.switchTo().window(driver.getWindowHandles().toArray()[2].toString());//速度过题 // Thread.sleep(1500); driver.findElement(By.xpath("/html/body/div/div[2]/div[3]/div[2]/ul/li[1]/div/h2/a")).click();//8 2023慕尼黑国际车展 Thread.sleep(1500); driver.switchTo().window(driver.getWindowHandles().toArray()[5].toString()); Thread.sleep(1500); // driver.switchTo().window(driver.getWindowHandles().toArray()[3].toString());//速度过题 // Thread.sleep(1500); driver.findElement(By.className("index_item_box_t--L6")).click();//9 现场直击 Thread.sleep(1500); driver.findElement(By.linkText("2023慕尼黑车展:大众全新Tiguan亮相,预计2024年上市")).click();//10 2023慕尼黑车展:大众全新Tiguan亮相,预计2024年上市 Thread.sleep(1500); driver.switchTo().window(driver.getWindowHandles().toArray()[6].toString()); Thread.sleep(1500); // driver.switchTo().window(driver.getWindowHandles().toArray()[4].toString());//速度过题 // Thread.sleep(1500); driver.findElement(By.className("text-2sXTFgZW")).click();//11 人参与 Thread.sleep(1500); driver.switchTo().window(driver.getWindowHandles().toArray()[7].toString()); Thread.sleep(1500); // driver.switchTo().window(driver.getWindowHandles().toArray()[5].toString());//速度过题 // Thread.sleep(1500); driver.findElement(By.xpath("/html/body/div/div[3]/div[2]/div[1]/div[2]/div[5]/div[2]/div[1]/div/div[2]/span[1]/a[2]")).click();//12 最新处 回复 Thread.sleep(1500); driver.findElement(By.className("w-submitBtn")).click();//13 发表 Thread.sleep(2500); Alert b = driver.switchTo().alert(); b.accept(); Thread.sleep(2500); driver.findElement(By.className("w-commentArea")).sendKeys("优秀");//14输入 Thread.sleep(1500); driver.findElement(By.className("icon-faceTrigArr")).click();//15表情 Thread.sleep(2500); driver.findElement(By.xpath("/html/body/div/div[3]/div[2]/div[1]/div[2]/div[5]/div[2]/div[1]/div/div[3]/div[1]/form/div[2]/div/div/div[2]/div/div[2]/ul/li[20]/img")).click();//18拜拜表情 Thread.sleep(1500); // driver.findElement(By.linkText("取消")).click();//15取消 // Thread.sleep(1500); driver.findElement(By.id("js_toTop")).click();//16 置顶 Thread.sleep(1500); driver.findElement(By.linkText("凤凰网首页")).click();//17凤凰网首页 Thread.sleep(1500); driver.switchTo().window(driver.getWindowHandles().toArray()[8].toString()); Thread.sleep(1500); driver.findElement(By.linkText("视频")).click();//18 视频 Thread.sleep(1500); driver.switchTo().window(driver.getWindowHandles().toArray()[9].toString()); Thread.sleep(1500); driver.findElement(By.className("index_more_T32Gd")).click();//19更多 Thread.sleep(1500); driver.findElement(By.linkText("美食")).click();//20 美食 Thread.sleep(1500); driver.switchTo().window(driver.getWindowHandles().toArray()[10].toString()); Thread.sleep(1500); driver.findElement(By.xpath("/html/body/div/div[4]/div[1]/div[2]/a/div/img")).click();//21 第二个视频 Thread.sleep(1500); driver.switchTo().window(driver.getWindowHandles().toArray()[11].toString()); Thread.sleep(1500); driver.findElement(By.className("index_vSelect_1csYP")).click();//22更多 Thread.sleep(3000); // WebElement e = driver.findElement(By.className("index_vSelect_1csYP"));//频道 // Actions c = new Actions(driver); // c.moveToElement(e).perform(); // Thread.sleep(2500); driver.findElement(By.linkText("凤凰首页")).click();//23 凤凰首页 Thread.sleep(1500); driver.switchTo().window(driver.getWindowHandles().toArray()[12].toString()); Thread.sleep(1500); driver.findElement(By.className("index_login_name_2x_UU-Kq")).click();//24 名字 Thread.sleep(1500); driver.findElement(By.linkText("进入个人中心")).click();//25 个人中心 Thread.sleep(1500); driver.switchTo().window(driver.getWindowHandles().toArray()[13].toString()); Thread.sleep(1500); driver.findElement(By.linkText("注销")).click();//26注销 Thread.sleep(1500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { // Run main function to test your script. WebDriver driver = new ChromeDriver(); try { test(driver); } catch(Exception e) { e.printStackTrace(); } finally { driver.quit(); } } }import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.interactions.Actions; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.By; import java.util.List; import org.openqa.selenium.Alert; @SuppressWarnings("unused") public class Example { // Mooctest Selenium Example // <!> Check if selenium-standalone.jar is added to build path. public static void test(WebDriver driver) { // TODO Test script // eg:driver.get("https://www.baidu.com/") // eg:driver.findElement(By.id("wd")); try { driver.get("http://121.43.149.85/"); driver.manage().window().maximize(); Thread.sleep(10000); driver.findElement(By.id("username")).clear(); Thread.sleep(2000); driver.findElement(By.id("password")).clear(); Thread.sleep(2000); driver.findElement(By.id("username")).sendKeys("sunkang@mooctest.com");//1账号 Thread.sleep(2000); driver.findElement(By.id("password")).sendKeys("123456");//2密码 Thread.sleep(2000); driver.findElement(By.id("loginbutton")).click();//3登录 Thread.sleep(10000); driver.findElement(By.xpath("/html/body/div[1]/div/div[1]/div[2]/div[1]/div/ul/div[2]/li/div")).click();//4商品管理 Thread.sleep(5000); driver.findElement(By.xpath("/html/body/div[1]/div/div[1]/div[2]/div[1]/div/ul/div[2]/li/ul/div[1]/a/li")).click();//5商品分类 Thread.sleep(5000); driver.findElement(By.xpath("/html/body/div/div/div[2]/section/div[1]/div[1]/div[2]/span/button[1]/span")).click();//6新增 Thread.sleep(5000); driver.findElement(By.xpath("/html/body/div[2]/div/div[2]/form/div[1]/div/div/input")).sendKeys("测试");//7商品名称 Thread.sleep(5000); driver.findElement(By.xpath("/html/body/div[2]/div/div[2]/form/div[2]/div/div/div[1]/i")).click();//8 加好 Thread.sleep(2000); driver.findElement(By.xpath("/html/body/div[3]/div/div[2]/section/main/div/div[2]/div/div[1]/div/div[3]/div/div/div[2]/label/span[1]/span")).click();//9选择第三个 Thread.sleep(2000); driver.findElement(By.xpath("/html/body/div[3]/div/div[3]/span/button[2]")).click();//10确定 Thread.sleep(2000); driver.findElement(By.xpath("/html/body/div[2]/div/div[2]/form/div[3]/div/div/label[2]/span[2]")).click();//11隐藏 Thread.sleep(2000); driver.findElement(By.xpath("/html/body/div[2]/div/div[3]/div/button[2]")).click();//12确定 Thread.sleep(2000); driver.findElement(By.xpath("/html/body/div[1]/div/div[2]/section/div[1]/div[1]/div[1]/div/input")).sendKeys("测试");//13 搜索测试 Thread.sleep(2000); driver.findElement(By.xpath("/html/body/div[1]/div/div[2]/section/div[1]/div[1]/div[1]/span/button[1]")).click();//14搜索确定 Thread.sleep(2000); driver.findElement(By.xpath("/html/body/div[1]/div/div[2]/section/div[1]/div[2]/div[4]/div[2]/table/tbody/tr/td[5]/div/div/span/span/button")).click();//15删除 Thread.sleep(5000); driver.findElement(By.xpath("/html/body/div[4]/div[1]/button[2]")).click();//16确定删除 Thread.sleep(5000); driver.findElement(By.xpath("/html/body/div/div/div[1]/div[2]/div[1]/div/ul/div[4]/li/div")).click();//17选择订单管理 Thread.sleep(2000); driver.findElement(By.xpath("/html/body/div/div/div[1]/div[2]/div[1]/div/ul/div[4]/li/ul/div[3]/a/li/span")).click();//18物流快递 Thread.sleep(2000); driver.findElement(By.xpath("/html/body/div/div/div[2]/section/div[1]/div[1]/div/button/span")).click();//19物流快递新增 Thread.sleep(2000); List<WebElement> inputs = driver.findElements(By.className("el-input__inner")); inputs.get(2).sendKeys("Test01"); Thread.sleep(2000); inputs = driver.findElements(By.className("el-input__inner")); inputs.get(3).sendKeys("Test01"); Thread.sleep(3000); driver.findElement(By.xpath("/html/body/div[2]/div/div[3]/div/button[2]")).click();; Thread.sleep(3000); // driver.findElement(By.xpath("/html/body/div[4]/div/div[2]/form/div[1]/div/div/input")).sendKeys("test01");//19物流快递新增 // Thread.sleep(2000); // driver.findElement(By.xpath("/html/body/div[4]/div/div[2]/form/div[2]/div/div/input")).sendKeys("test01");//19物流快递新增 // Thread.sleep(2000); // driver.findElement(By.xpath("/html/body/div[4]/div/div[3]/div/button[2]")).click();//19物流快递新增 // Thread.sleep(2000); // driver.findElement(By.xpath("/html/body/div[1]/div/div[2]/section/div[1]/div[2]/div[3]/table/tbody/tr[1]/td[4]/div/span/span/button")).click();//20物流快递删除 Thread.sleep(2000); driver.findElement(By.xpath("/html/body/div[3]/div[1]/button[2]")).click();//20物流快递删除 Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { // Run main function to test your script. WebDriver driver = new ChromeDriver(); try { test(driver); } catch(Exception e) { e.printStackTrace(); } finally { driver.quit(); } } }
    • 1年前
    • 392
    • 0
    • 0
  • Vue 3 和 Element Plus 实现商品管理系统 - 表单校验部分解析 介绍在本博客中,我们将深入探讨如何使用 Vue 3 和 Element Plus 构建一个简单的商品管理系统,并重点关注表单校验的部分。这个系统允许用户添加、编辑和删除商品,以及对商品进行搜索。技术栈在这个项目中,我们使用了以下技术:Vue 3:一个流行的 JavaScript 框架,用于构建用户界面。Element Plus:一套基于 Vue 3 的组件库,用于创建漂亮的用户界面。Axios:一个用于处理 HTTP 请求的 JavaScript 库。表单校验在这个项目中,我们使用 Element Plus 的表单组件来实现商品信息的输入和校验。以下是与表单校验相关的关键知识点和注意事项:1. 引入 Element Plus在项目中首先要确保已正确引入 Element Plus,以便使用它的表单组件和验证规则。import { ref, reactive } from "vue"; import { ElForm, ElFormItem, ElInput, ElSelect, ElOption } from "element-plus";2. 创建表单数据和校验规则我们使用 ref 来创建表单数据,并使用 ref 来创建校验规则。校验规则通常包括字段的必填性、数据类型和自定义校验方法。const insertForm = ref({ sellerID: "", name: "", description: "", price: "", category: "", condition: "", quantity: "", // status: "", }); const insertFormRules = ref({ name: [ { required: true, message: "商品名称不能为空", trigger: "blur", }, ], description: [ { required: true, message: "商品描述不能为空", trigger: "blur", }, ], price: [ { required: true, validator: validatePrice, trigger: "blur", }, ], category: [ { required: true, message: "请选择商品类别", trigger: "change", }, ], condition: [ { required: true, message: "请选择商品成色", trigger: "change", }, ], quantity: [ { required: true, message: "商品数量不能为空", trigger: "blur", }, { type: "number", message: "商品数量必须为数字值", }, ], });3. 表单校验在提交表单之前,我们使用 el-form 的 validate 方法来进行表单校验。校验成功后才允许提交数据。<el-dialog v-model="dialogInsertFormVisible" title="上架商品"> <el-form ref="insertFormRulesRef" :model="insertForm" :rules="insertFormRules" > <el-form-item label="商品名称" label-width="80px" prop="name"> <el-input v-model="insertForm.name" autocomplete="off" /> </el-form-item> <el-form-item label="描述" label-width="80px" prop="description"> <el-input v-model="insertForm.description" autocomplete="off" /> </el-form-item> <el-form-item label="价格" label-width="80px" prop="price"> <el-input v-model="insertForm.price" autocomplete="off" /> </el-form-item> <el-form-item label="类别" label-width="80px" prop="category"> <el-select v-model="insertForm.category" placeholder="请选择类别"> <el-option label="服饰鞋帽" value="服饰鞋帽" /> <el-option label="家居用品" value="家居用品" /> <el-option label="电子数码" value="电子数码" /> <el-option label="美妆护肤" value="美妆护肤" /> <el-option label="食品生鲜" value="食品生鲜" /> <el-option label="图书音像" value="图书音像" /> <el-option label="儿童玩具" value="儿童玩具" /> <el-option label="运动户外" value="运动户外" /> <el-option label="汽车用品" value="汽车用品" /> <el-option label="医疗保健" value="医疗保健" /> <el-option label="工艺礼品" value="工艺礼品" /> <el-option label="虚拟物品" value="虚拟物品" /> </el-select> </el-form-item> <el-form-item label="成色" label-width="80px" prop="condition"> <el-select v-model="insertForm.condition" placeholder="请选择成色"> <el-option label="全新" value="全新" /> <el-option label="99新" value="99新" /> <el-option label="95新" value="95新" /> <el-option label="9成新" value="9成新" /> <el-option label="8成新" value="8成新" /> <el-option label="6成新" value="6成新" /> </el-select> </el-form-item> <el-form-item label="数量" label-width="80px" prop="quantity"> <el-input v-model.number="insertForm.quantity" autocomplete="off" /> </el-form-item> </el-form> <template #footer> <span class="dialog-footer"> <el-button @click="dialogInsertFormVisible = false">取消</el-button> <el-button type="primary" @click="submitItemInform(insertFormRulesRef)" > 提交 </el-button> <el-button type="primary" @click="test"> 提交 </el-button> </span> </template> </el-dialog>ref[0].validate(async (valid) => { if (valid) { // 表单校验通过,可以提交数据 try { const res = await axios.post( "http://localhost:8080/userInsertItem", insertForm.value ); // 处理提交结果... } catch (err) { // 处理请求错误... } } else { // 表单校验不通过,不执行提交操作 console.log("表单校验不通过!"); } });4. 自定义校验规则在校验规则中,我们可以使用自定义校验方法来实现特定的验证逻辑。例如,我们可以编写一个验证价格是否为数字的自定义校验方法:const validatePrice = (rule, value, callback) => { if (!value) return callback(new Error("请输入金额")); if (!Number(value)) return callback(new Error("请输入数字值")); let reg = /((^[1-9]\d*)|^0)(\.\d{0,2}){0,1}$/; if (!reg.test(value)) return callback(new Error("请输入正确价格")); callback(); };然后,在校验规则中应用该自定义校验方法:price: [ { required: true, validator: validatePrice, trigger: "blur", }, ],5. 提示用户在校验不通过时,使用 Element Plus 的消息提示功能来通知用户错误信息:if (!valid) { // 表单校验不通过,显示错误消息 this.$message.error("表单校验不通过,请检查输入!"); }6. 表单重置在提交成功后,你可以选择重置表单,以便用户继续添加新的商品信息:ref[0].resetFields(); // 重置表单总结在这篇博客中,我们深入探讨了如何使用 Vue 3 和 Element Plus 构建商品管理系统的表单校验部分。我们学习了如何创建表单数据、定义校验规则、进行表单校验以及处理校验结果。此外,我们还介绍了如何使用自定义校验方法和提示用户错误信息的方法。这个项目只是一个简单的示例,你可以根据实际需求扩展和定制更多功能,以满足你的项目要求。希望这篇博客对你有所帮助,如果有任何问题或建议,请随时留言。
    • 1年前
    • 643
    • 1
    • 1
  • Vue 3 中 Watch 侦听器的深入讲解:Immediate 和 Deep 参数 Vue 3 的组合式 API 为我们提供了强大的响应式编程能力,其中 watch 侦听器是一个关键的部分。它允许我们对响应式数据进行更细粒度的观察。在使用 watch 时,我们经常会用到两个非常有用的配置参数:immediate 和 deep。Watch 侦听器的基本用法在介绍 immediate 和 deep 之前,让我们先回顾一下 watch 侦听器的基本用法:import { ref, watch } from 'vue'; export default { setup() { const count = ref(0); watch(count, (newValue, oldValue) => { console.log(`计数器的值从 ${oldValue} 变为 ${newValue}`); }); // ...其他逻辑 } }Immediate 参数immediate 参数用于控制 watch 侦听器是否在初次执行时立即触发回调函数。不使用 Immediate默认情况下,watch 侦听器在创建时不会立即执行回调函数,它只会在侦听的响应式引用发生变化时被触发。使用 Immediate如果我们想要在 watch 创建的同时立即执行一次回调,可以设置 immediate: true。watch(count, (newValue, oldValue) => { // 即使 count 没有变化,这里也会立即执行,并且 oldValue 为 undefined console.log(`计数器的值从 ${oldValue} 变为 ${newValue}`); }, { immediate: true });在开发中,immediate 参数特别有用,例如,当我们需要基于当前的响应式状态立即运行副作用时。Deep 参数deep 参数用于指定 watch 侦听器是否需要深度观察对象内部值的变化。不使用 Deep默认情况下,如果我们的响应式数据是一个对象,watch 只能检测到对象的第一层属性的变化。使用 Deep如果我们需要侦听一个对象内部深层属性的变化,我们可以设置 deep: true。const userProfile = ref({ name: '张三', preferences: { theme: 'dark' } }); watch(userProfile, (newValue, oldValue) => { // 当 userProfile 内部的任何属性改变时,这里都会执行 console.log('用户配置发生变化'); }, { deep: true });在实际应用中,使用 deep 参数可以帮助我们捕捉到嵌套对象的变化,但同时也要注意它可能会引起的性能问题,因为它会监听所有内部属性的变化。总结在 Vue 3 中,watch 侦听器通过 immediate 和 deep 参数提供了高度灵活的配置选项,使我们能够根据具体的场景灵活地处理数据变化。immediate 参数在需要立即反应当前状态时非常有用,而 deep 参数允许我们深入到响应式对象的内部,监视其嵌套属性的变动。恰当地使用这些特性,可以大大增强我们应用的响应能力和用户体验。
    • 1年前
    • 684
    • 0
    • 2
  • 使用Spring Boot实现在线聊天功能 在线聊天功能对于实时通讯的应用程序是必不可少的。在本文中,我们将详细探讨如何使用Spring Boot来实现一个简单的在线聊天接口。技术栈Spring Boot: 用于创建MVC架构的Web应用程序。WebSocket: 提供客户端和服务端之间的双向通信。第一步:创建Spring Boot项目首先,使用Spring Initializr (start.spring.io)创建一个新的Spring Boot项目,添加spring-boot-starter-websocket作为依赖。第二步:配置WebSocket在Spring Boot中配置WebSocket,需要创建一个配置类,如下:import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.*; @EnableWebSocketMessageBroker @Configuration public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { // 注册一个WebSocket端点,客户端将使用它进行连接 registry.addEndpoint("/chat").withSockJS(); } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { // 配置一个简单的消息代理,它将用于将消息从服务器路由到客户端 registry.enableSimpleBroker("/topic"); registry.setApplicationDestinationPrefixes("/app"); } }这里,/chat是连接端点,客户端通过这个URL来建立WebSocket连接。/topic用于定义目的地前缀,客户端将订阅这个路径以接收消息。第三步:创建消息模型创建一个简单的消息模型来承载聊天数据。public class ChatMessage { private String from; private String text; // 构造函数、Getter和Setter }第四步:创建消息处理器定义一个控制器来处理发送到特定路径的消息。import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.stereotype.Controller; @Controller public class ChatController { @MessageMapping("/sendMessage") @SendTo("/topic/public") public ChatMessage sendMessage(ChatMessage chatMessage) { return chatMessage; } }这里的sendMessage方法会处理发送到/app/sendMessage的消息,并将其路由到/topic/public,所有订阅了这个路径的客户端都会收到消息。第五步:前端实现在前端,你可以使用JavaScript和HTML来实现客户端逻辑。<!DOCTYPE html> <html> <head> <title>Chat Room</title> <script src="https://cdn.jsdelivr.net/npm/sockjs-client/dist/sockjs.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/stomp-websocket/lib/stomp.min.js"></script> </head> <body> <div> <input type="text" id="user" placeholder="Enter your name"/> <input type="text" id="messageInput" placeholder="Type a message..."/> <button onclick="sendMessage()">Send</button> </div> <ul id="messageArea"> </ul> <script type="text/javascript"> var stompClient = null; var user = document.querySelector('#user'); var messageInput = document.querySelector('#messageInput'); var messageArea = document.querySelector('#messageArea'); function connect() { var socket = new SockJS('/chat'); stompClient = Stomp.over(socket); stompClient.connect({}, onConnected, onError); } function onConnected() { stompClient.subscribe('/topic/public', onMessageReceived); } function onError(error) { // 处理连接错误 } function sendMessage() { var messageContent = messageInput.value.trim(); if(messageContent && stompClient) { var chatMessage = { from: user.value, text: messageContent }; stompClient.send("/app/sendMessage", {}, JSON.stringify(chatMessage)); messageInput.value = ''; } } function onMessageReceived(payload) { var message = JSON.parse(payload.body); var messageElement = document.createElement('li'); messageElement.innerText = message.from + ': ' + message.text; messageArea.appendChild(messageElement); } connect(); </script> </body> </html>第六步:运行和测试启动Spring Boot应用程序,并在浏览器中打开HTML页面,尝试发送和接收消息。总结以上步骤展示了如何使用Spring Boot和WebSocket实现在线聊天功能。这只是基础实现,生产环境中你还需要考虑安全、错误处理和消息历史等功能。
    • 1年前
    • 418
    • 2
    • 2
  • Vue 3中的选项式API与组合式API对比详解 Vue 3不仅带来了性能的提升,还引入了一个新的组合式API,旨在解决选项式API在复杂组件中遇到的问题。下面,我们将通过具体示例,对比和分析两种API的使用差异,并探讨组合式API的设计理念。数据声明选项式API在选项式API中,我们通过data来声明组件的响应式状态。<script> export default { data() { return { count: 0 }; } }; </script>选项式API通过一个集中的data函数返回所有状态,便于快速查找和管理。组合式API组合式API利用ref或reactive来声明响应式状态。<script setup> import { ref } from 'vue'; const count = ref(0); </script>在组合式API中,<script setup>提供了一种更简洁的方式来定义组件的逻辑。通过ref或reactive,我们可以将状态的声明更紧密地关联到它们被使用的地方,这使得逻辑更加模块化和可重用。方法选项式API选项式API中,方法被定义在methods选项内。<script> export default { methods: { increment() { this.count++; } } }; </script>这种方式使得方法能够通过this访问组件的状态和其他方法,所有方法都被组织在一起。组合式API在组合式API中,方法被定义为局部函数。<script setup> const increment = () => { count.value++; }; </script>使用组合式API时,方法可以直接定义在<script setup>中,更接近原生JavaScript的函数定义,这样做提高了函数的可测试性和可重用性。通过减少对this的依赖,方法的独立性得到加强,也便于与其他逻辑片段共享。计算属性选项式API选项式API中,计算属性被定义在computed选项里。<script> export default { data() { return { firstName: 'John', lastName: 'Doe' }; }, computed: { fullName() { return `${this.firstName} ${this.lastName}`; } } }; </script>通过定义在computed中,Vue为每个计算属性创建了一个响应式的getter,使得它们在依赖变更时自动更新。组合式API在组合式API中,计算属性通过computed函数创建。<script setup> import { ref, computed } from 'vue'; const firstName = ref('John'); const lastName = ref('Doe'); const fullName = computed(() => `${firstName.value} ${lastName.value}`); </script>这里,computed函数使得定义计算属性更加直观,也更容易控制依赖。组合式API允许开发者在逻辑相关的代码附近声明计算属性,提高了代码的可读性。侦听器选项式API选项式API中使用watch选项。<script> export default { watch: { count(newValue) { console.log(`count变化了,现在是${newValue}`); } } }; </script>watch选项允许我们定义一个侦听器,当指定的数据变化时执行回调。组合式API组合式API中使用watch函数。<script setup> import { watch } from 'vue'; watch(count, (newValue) => { console.log(`count变化了,现在是${newValue}`); }); </script>与选项式API不同,watch函数在组合式API中被定义为顶级的API,这使得它更易于从组件逻辑中分离和重用。watch函数的使用提供了更多控制,并且由于是基于函数的,它可以很容易地组合其他响应式状态和逻辑。生命周期钩子选项式API选项式API直接将生命周期钩子作为组件选项。<script> export default { mounted() { console.log('组件挂载完成'); } }; </script>生命周期钩子作为选项,易于识别,但是在处理多个钩子时,代码会分散到不同位置。组合式API组合式API中使用特定的钩子函数。<script setup> import { onMounted } from 'vue'; onMounted(() => { console.log('组件挂载完成'); }); </script>在组合式API中,生命周期钩子被作为函数导入,这样不仅代码更加一致,也更容易将相关的生命周期钩子和逻辑组织在一起,有助于提升代码的清晰度和模块性。
    • 1年前
    • 442
    • 0
    • 0
  • 深入解析ES6:从var到let/const,再到箭头函数 随着ECMAScript 6(ES6)的到来,JavaScript语言引入了许多改革性的新特性,这些新特性不仅使得代码更加简洁,还解决了一些长期存在的问题。在本文中,我们将详细探讨其中的两个显著特性:let/const声明和箭头函数,以及它们是如何改善JavaScript编程的。let和const:新时代的变量声明ES5中仅有var在ES5之前,我们声明变量的唯一方法是使用var。这种方式存在变量提升等问题,容易导致代码中出现错误。var price = 49.99; var price = 29.99; // 重复声明,不会报错 console.log(price); // 输出:29.99ES6带来的变革:let和constES6引入了let和const来优化变量声明。let允许你声明一个作用域受限的变量,而不像var那样经常造成意外的全局变量。let price = 49.99; price = 29.99; // 正常运作 console.log(price); // 输出:29.99const更进一步,它允许你声明一个值不可变的常量。const RATE = 0.1; RATE = 0.2; // TypeError: Assignment to constant variable.这种声明方式确保变量值的不变性,避免了程序中可能出现的错误。箭头函数与this的新维度JavaScript中的this关键字是一个复杂且容易引起混淆的概念。它的值通常取决于函数调用的上下文,这经常导致意外的结果。ES5的this困扰function Team() { this.members = ['John', 'Jane']; this.addMember = function(name) { this.members.push(name); }; this.showMembers = function() { this.members.forEach(function(member) { console.log(this.members); // this指向错误 }); }; } var team = new Team(); team.addMember('Smith'); team.showMembers(); // TypeError or unexpected behaviorES6箭头函数的清晰thisES6的箭头函数提供了一种清晰和简洁的方式来维护this的上下文。function Team() { this.members = ['John', 'Jane']; this.addMember = function(name) { this.members.push(name); }; this.showMembers = () => { this.members.forEach((member) => { console.log(this.members); // this现在指向Team对象 }); }; } var team = new Team(); team.addMember('Smith'); team.showMembers(); // 正确显示成员列表箭头函数没有自己的this,它会捕获其所在上下文的this值,使得在回调函数和方法中的this行为变得可预测和一致。总结let和const的引入,以及箭头函数中的this的改进,是ES6中对JavaScript进行现代化改进的重要步骤。这些新特性不仅使得代码更加可靠和易于维护,也让开发者能够写出更加清晰和简洁的代码。随着ES6特性的广泛采用,JavaScript的强大和易用性正在持续提升,对开发者来说,掌握这些新特性是非常重要的。
    • 1年前
    • 484
    • 0
    • 1
  • 深入浅出:SQL Server数据库设计的三范式 在数据库设计领域,范式(Normalization)是一组关于表结构设计的规则,主要用来避免数据冗余和维护数据的完整性。SQL Server数据库也不例外,它广泛应用三个基本的范式规则来设计数据库。下面,我们将详细介绍这三个范式,并通过实例来帮助大家更好地理解。一、第一范式(1NF):原子性列第一范式是设计数据库的基础,它要求表中的所有列都要有原子性,即列的值不可再分。这意味着数据库表的每一列都应该是不可再分的最小数据单位。例子说明:不符合1NF的订单表:订单编号客户姓名订单商品1张三电脑, 手机2李四电视符合1NF的订单表:订单编号客户姓名商品名称1张三电脑1张三手机2李四电视二、第二范式(2NF):完全函数依赖在满足第一范式的基础上,第二范式要求表中的所有非主属性必须完全依赖于主键。如果存在复合主键,那么非主键的列必须依赖于所有的主键列,而不是部分。例子说明:不符合2NF的订单表(已符合1NF):订单编号商品编号客户姓名商品名称商品价格1001张三电脑50001002张三手机30002003李四电视2500分解后符合2NF的订单商品表和客户表:订单商品表:订单编号商品编号商品名称商品价格1001电脑50001002手机30002003电视2500客户表:订单编号客户姓名1张三2李四三、第三范式(3NF):消除传递依赖第三范式要求在第二范式的基础上,表中的非主键列不能依赖于其他非主键列,即不存在传递依赖。例子说明:不符合3NF的订单客户表(已符合2NF):订单编号客户编号客户姓名客户积分等级11001张三VIP21002李四普通分解后符合3NF的订单表和客户表:订单表:订单编号客户编号1100121002客户信息表:客户编号客户姓名客户积分等级1001张三VIP1002李四普通总结:通过对表结构进行规范化设计,我们可以有效地减少数据冗余,避免数据更新异常,以及提升数据的一致性。在实际的数据库设计过程中,数据库设计师应当根据实际需求,权衡范式的应用与数据库性能之间的关系,做出合理的设计选择。
    • 1年前
    • 502
    • 0
    • 2
  • SpringBoot中获取yml文件数据信息 在Spring Boot中,获取YAML(.yml或.yaml)配置文件中的数据是一个简单而直接的过程。Spring Boot使用了一个非常强大的库Spring Framework,它提供了@Value注解和ConfigurationProperties绑定来实现这个功能。首先,假设我们有一个application.yml文件,内容如下:myapp: name: My Spring Boot App description: This is a sample Spring Boot application. metadata: version: 1.0.0 author: Jane Doe在Spring Boot应用程序中获取这些配置的步骤如下:1. 使用@Value注解@Value注解可以直接在字段上使用,来获取配置文件中的相应值。import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class MyAppProperties { @Value("${myapp.name}") private String name; @Value("${myapp.description}") private String description; // 获取嵌套属性 @Value("${myapp.metadata.version}") private String version; // ... getters and setters }2. 使用ConfigurationProperties绑定另一种方式是创建一个配置属性类,使用@ConfigurationProperties注解,它会自动映射配置文件中的属性到同名的Java字段。import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; @Configuration @ConfigurationProperties(prefix = "myapp") public class MyAppConfig { private String name; private String description; private Metadata metadata; public static class Metadata { private String version; private String author; // ... getters and setters } // ... getters and setters }然后,你可以在需要的地方注入MyAppConfig类的实例来使用这些配置值。3. @PropertySource与YAML需要注意的是,如果你想要使用@PropertySource注解来指定配置文件的路径,这个注解并不支持YAML格式的文件,它仅仅支持.properties文件。所以,在使用YAML的时候,这个注解通常是不需要的,因为Spring Boot会自动加载application.yml文件。4. 激活特定的配置文件Spring Boot允许你根据环境激活特定的配置文件,比如application-dev.yml或application-prod.yml。你可以通过设置spring.profiles.active属性来激活特定的配置文件。spring: profiles: active: dev或者在运行应用程序时通过命令行参数激活:java -jar myapp.jar --spring.profiles.active=prod这样,Spring Boot会加载相应的application-{profile}.yml文件,并覆盖默认的配置值。结语通过以上步骤,你可以轻松地在Spring Boot应用程序中读取和使用YAML配置文件中的数据。这个功能强大而灵活,非常适合现代应用程序的配置管理需求。记得在实际的编码过程中,你还需要添加相关的依赖项,如spring-boot-configuration-processor,来享受更完善的功能,例如配置属性的自动补全和文档生成。
    • 1年前
    • 422
    • 0
    • 2
  • 人生倒计时
    舔狗日记