Vue子组件每次加载都执行Mounted周期函数

Vue框架为了提高性能,对组件进行了缓存,所以第二次加载相同的子组件的时候,是不会创建新的dom,而是使用缓存。

但是有时候我们需要每次加载子组件都执行一次Mounted函数来做一些数据的初始化或者请求等等,所以这是个麻烦事了。

vue组件通过key来识别组件是否缓存,那么我们可以在组件上面添加一个key值,每次请求的时候都不一样,这样就可以解决上面的问题(不考虑性能问题)。

父组件

<template>
 <div>
   <Com :key="randdomKey"></Com>
 </div>
</template>

在每次要加载显示Com子组件的时候,就改变randdomKey的值即可。

WIN32创建模态和非模态弹窗框

第一步,在资源文件中创建弹出框资源。

新建两个弹出框资源,菜单分别为IDD_DIALOG1 和IDD_DIALOG2

二、建立一个顶级菜单,方便操作。

ID分别设置为 ID_MODEL(模态) 和 ID_NO_MODEL(非模态)

三、在.cpp中的窗口处理函数的添加WM_COMMAND的后续处理函数

    .......
    case WM_COMMAND: {
        OnCommand(hwnd, wParam);
        break;
   .......
void OnCommand(HWND hwnd,  WPARAM wParam) {
   //点击不同菜单之后做不同的处理。
    switch (LOWORD(wParam))
    {
    case ID_MODEL:{
        DialogBoxA( g_hInstance, (char*)IDD_DIALOG1, hwnd, DlgProc); //创建模态
       break;
    }
    case ID_NO_MODEL: {
       //创建非模态
       HWND hNoModelWnd =  CreateDialog(g_hInstance, (char*)IDD_DIALOG2, hwnd, DlgNoModeProc);
       //显示窗口(模态不需要)
       ShowWindow(hNoModelWnd, SW_SHOW);
       break;
    }
    default:
        break;
    }

}
INT_PTR  DlgProc(HWND hwnd, UINT msgId, WPARAM wParam, LPARAM lParam) {
  
    switch (msgId)
    {
    case WM_SYSCOMMAND:
    {
        //点击关闭弹出窗口的关闭按钮
        if (wParam == SC_CLOSE) {
            EndDialog(hwnd, TRUE); //模态必须要用EndDialog,因为这个函数既可以销毁窗口又能解除阻塞
        }
        break;
    }
    default:
        break;
    }

    return FALSE;
}

//非模态
INT_PTR  DlgNoModeProc(HWND hwnd, UINT msgId, WPARAM wParam, LPARAM lParam) {
    switch (msgId)
    {
    case WM_SYSCOMMAND:
    {
        //点击关闭弹出窗口的关闭按钮
        if (wParam == SC_CLOSE) {
            DestroyWindow(hwnd); //只要销毁窗口,不需要解除窗口,所以不需要用EndDialog
        }
        break;
    }
    default:
        break;
    }

    return FALSE;
}

当我们创建主窗口的时候系统会发送WM_CREATE消息,但是创建弹窗窗口的时候发送的不是WM_CREATE而是发送的WM_INITDIALOG,这是弹窗窗口和其他窗口不同之处,其他的和普通窗口处理没有差异。

WIN32中添加上下文菜单

我们经常看到在界面上点击右键出现一个菜单,这个菜单就是上下文菜单,有些人也把它叫做“右键菜单”。

在win32中我们创建菜单只能创建顶部菜单,那么这种上下文菜单是如何实现的呢,其实就是拿这个顶部菜单的一部分,也就是这个顶部菜单的子菜单。

单我们点击鼠标右键的时候窗口会有产生两个消息分别是“WM_RBUTTONUP” 和 “WM_CONTEXTMENU”。在这里我们用WM_CONTEXTMENU,因为WM_CONTEXTMENU消息附带了以屏幕为准的鼠标的x,y坐标。

WM_CONTEXTMENU中定义一个处理函数OnContentMenu,接收两个参数(HWND hwnd, LPARAM lParam)

VOID OnContentMenu(HWND hwnd, LPARAM lParam) {
	HMENU MainhMenu = LoadMenu(g_hInstance, (char*)IDR_MENU1); //加载顶部菜单
	HMENU hMenu = GetSubMenu(MainhMenu, 0);//获取顶部菜单的子菜单,0是索引。
        //TrackPopupMenu是创建一个上下文菜单。可以参考msdn文档。
	TrackPopupMenu(hMenu, TPM_LEFTALIGN|TPM_TOPALIGN, LOWORD(lParam), HIWORD(lParam),0,hwnd,NULL);
}

这样就可以在鼠标点击的时候弹出菜单了。点击菜单项之后去 WM_COMMAND 消息处理。

WIN32挂载顶部菜单的三种方式

第一种:在注册窗口类

给lpszMenuName属性赋值: winClass.lpszMenuName = (char*)IDR_MENU1

第二种:在创建窗口的时候赋值参数

在执行CreateWindow的时候,传递菜单句柄。

HMENU hMenu = LoadMenu(hInstance, (char*)IDR_MENU1);
HWND hWnd = CreateWindow(CLASS_NAME, "JishugeApp", WS_OVERLAPPEDWINDOW, 100, 100, 800, 500, NULL, hMenu, hInstance, NULL);

第三种:在WM_CREATE消息挂载菜单

定义一个全局进程句柄:HINSTANCE g_hInstance = NULL;

在入口函数给全局句柄赋值 : g_hInstance = hInstance;

在WM_CREATE消息的时候调用自定义函数OnCreate

VOID OnCreate(HWND hwnd) {
	HMENU hMenu = LoadMenu(g_hInstance, (char*)IDR_MENU1);
	SetMenu(hwnd, hMenu);
}

win32窗口常见的消息

WM_CREATE

  • 触发时机:在窗口创建成功但是还没有被显示时
  • 附带信息
    • wParam:0;
    • lParam:为CREATESTRUCT类似的指针,通过这个指针可以获取CreateWindow的全部参数信息
  • 使用场景:一般用于初始化窗口的参数、资源、创建子窗口等等。

WM_DESTROY

  • 触发时机:窗口被销毁的时候。
  • 附带信息
    • wParam:0;
    • lParam:0;
  • 使用场景:一般用于窗口销毁前的处理工作,比如资源、内存等等。

WM_SYSCOMMAND

  • 触发时机:点击窗口的最大化、最小化、关闭等。
  • 附带信息
    • wParam:具体点击的位置,例如关闭SC_CLOSE等;
    • lParam:鼠标光标的位置。
      • LOWORD(lParam); x 坐标
      • HIWORD(lParam); y 坐标
  • 使用场景:一般用于窗口关闭的时候,提示用户处理

WM_QUIT

  • 触发时机:程序员主动触发
  • 附带信息
    • wParam:PostQuitMessage 函数传递的参数;
    • lParam:0;
  • 使用场景:一般用于结束消息循环,当GetMessage收到该消息后,会返回FALSE,来结束while循环,退出循环消息。

WM_SIZE

  • 触发时机:在窗口的大小发生变化的时候
  • 附带信息
    • wParam:窗口大小变化的原因(一般不重要);
    • lParam:窗口变化后的大小;
      • LOWORD(lParam):变化后的宽度
      • HIWORD(lParam):变化后的高度
  • 使用场景:一般用于窗口大小变化后,调整窗口各个部分的布局。

在Win32窗口开发中通过控制台输出方式

一、定义一个全局变量,HANDLE类型

HANDLE g_outPut = 0;

二、启动console输出

在入口函数执行函数 AllocConsole();

三、在入口函数内获取标志输出句柄并赋值给全局变量

g_outPut = GetStdHandle(STD_OUTPUT_HANDLE);

四、输出内容到控制台

WriteConsole(g_outPut , "哈哈",strlen("哈哈"),NULL,NULL);

关于Linux编程中的execl参数简单说明。

    int execl(const char *pathname, const char *arg, ... /* (char  *) NULL */);
    int execlp(const char *file, const char *arg, ... /* (char  *) NULL */);

以上是从man手册中复制下来的。以下我们进行分析。

execl

比如我们使用execl执行ls进程,那么可以写成这样:

execlp("/bin/ls","ls","-al","/temp",NULL);

第一个参数就是ls命令的绝对路径,

第二个参数就是进程的名称,这个名字可以填写也可以不填写,甚至可以随便填写,你填写什么,到时候在ps中就可以显示这个进程的名称,比如你填写成LinuxC123,那么在ps中可以查看到如下

USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
jishuge     5794  0.0  0.0   2772  1280 pts/0    S    10:32   0:00 LinuxC123

剩余的参数就是命令的选项。

最后一个参数一定是NULL。文档规定的:

The const char *arg and subsequent ellipses can be thought of as arg0, arg1, …, argn. To‐
gether they describe a list of one or more pointers to null-terminated strings that represent
the argument list available to the executed program. The first argument, by convention, should
point to the filename associated with the file being executed. The list of arguments must be
terminated by a null pointer, and, since these are variadic functions, this pointer must be
cast (char *) NULL.

在SpringBoot中响应出xml格式数据。

我们在用SpringBoot开发的时候,经常响应的数据一般都是json格式或者字符串。

当时偶尔我们也要响应xml格式,比如在更新chrome 插件的时候,如下xml格式:

<?xml version='1.0' encoding='UTF-8'?>
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
  <app appid='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'>
    <updatecheck codebase='https://myhost.com/mytestextension/mte_v2.crx' version='2.0' />
  </app>
</gupdate>

那么如何处理呢?手动拼接、替换?其实我们不用这么麻烦,可以用到一个开源库,一下是它的maven

     <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
        </dependency>

然后创建三个类:

public class UpdateCheck {

    private String codebase;
    private String version;

    @JsonProperty("codebase")
    public String getCodebase() {
        return codebase;
    }

    public void setCodebase(String codebase) {
        this.codebase = codebase;
    }

    @JsonProperty("version")
    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }
}
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;

@JacksonXmlRootElement(localName = "gupdate", namespace = "http://www.google.com/update2/response")
public class GUpdate {

    private App app;

    @JsonProperty("app")
    public App getApp() {
        return app;
    }

    public void setApp(App app) {
        this.app = app;
    }
}
public class App {

    private String appid;
    private UpdateCheck updateCheck;

    @JsonProperty("appid")
    public String getAppid() {
        return appid;
    }

    public void setAppid(String appid) {
        this.appid = appid;
    }

    @JsonProperty("updatecheck")
    public UpdateCheck getUpdateCheck() {
        return updateCheck;
    }

    public void setUpdateCheck(UpdateCheck updateCheck) {
        this.updateCheck = updateCheck;
    }
}

然后再Controller中定义使用。

   
    @GetMapping(value = "/xml", produces = MediaType.APPLICATION_XML_VALUE)
    public String  getXmlData() throws JsonProcessingException {
        XmlMapper xmlMapper = new XmlMapper();
        GUpdate gUpdate = new GUpdate();
        App app = new App();
        app.setAppid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
        UpdateCheck updateCheck = new UpdateCheck();
        updateCheck.setCodebase("https://myhost.com/mytestextension/mte_v2.crx");
        updateCheck.setVersion("2.0");
        app.setUpdateCheck(updateCheck);
        gUpdate.setApp(app);
        return xmlMapper.writeValueAsString(gUpdate);
    }

以上controller响应需要用到的库。

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;

响应结果

关于最近更新某个网站的wss协议逆向的一些想法。

由于某网站(我们这里简称M)的wss协议的一些验证参数升级,导致原来的身份验证失效,从而获取不到最新的消息,于是重新研究了下算法。

具体过程就是不断的用Chrome 浏览器面板来做断点调试,过程就不说了,但是总结了一些想法。

如果在浏览器的JavaScript中找不到加密和解密算法,那么算法一定是在服务器验证的。

如果发现在浏览器可以实现,但是模拟却实现不了,内容参数一样,那么95%以上是头部参数的原因。

如果碰见了问题就一定要记录下来,否则时间久了,就忘记了。下次接着继续踩同样的坑。(画重点)

SpringBoot项目中普通类使用Bean。

我们在SpringBoot中使用Bean的方式是@Autowired 方式自动注入的,但是在某些普通类中使用Bean的话,就不能使用这种方式,比如以下场景:

在使用 import org.java_websocket.client.WebSocketClient; 作为客户端去链接WebSocket服务器的时候,必须使用有参构造,而不能自动注入的情况下,我们要在WebSocketClient中使用自动注入过的redis、mysql等,使用 @Autowired 获取的对象是null,那么只能直接从SpringBoot的Bean对象管理器中拿取所需要的Bean对象。

操作步骤:

一、定义一个获取Bean的工具类。

定义工具类 SpringUtil (名字自定义) 实现 org.springframework.context.ApplicationContextAware 这个接口,代码如下:

@Component
public class SpringUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringUtil.applicationContext = applicationContext;
    }

    public static <T> T getBean(Class<T> beanClass) {
        return applicationContext.getBean(beanClass);
    }

    public static <T> T getBean(String beanName, Class<T> beanClass) {
        return applicationContext.getBean(beanName, beanClass);
    }
}

记住,这个类一定要添加 @Component 组件注解

二、使用demo

在一个普通类中使用,比如我们想获取RabbitMq的自动注入对象。

RabbitTemplate rabbitTemplate= SpringUtil.getBean(RabbitTemplate.class);
rabbitTemplate.convertAndSend("test_message","test"+new Date());
原理:就是SpringBoot的Bean都交由ApplicationContext applicationContext 来管理,我们只是间接的从 ApplicationContext applicationContext 这个对象中获取,更多的可以参考Spring的原理。