JavaScript常用的Hook脚本

页面最早加载代码Hook时机

  1. 在source里 用dom事件断点的script断点
  2. 然后刷新网页,就会断在第一个js标签,这时候就可以注入代码进行hook

监听 键盘 与 鼠标 事件

// 判断是否按下F12  onkeydown事件
/*
提示: 与 onkeydown 事件相关联的事件触发次序:
onkeydown
onkeypress
onkeyup
*/// F12的键码为 123,可以直接全局搜索 keyCode == 123, == 123 ,keyCode
document.onkeydown = function() {if (window.event && window.event.keyCode == 123) {// 改变键码event.keyCode = 0;event.returnValue = false;// 监听到F12被按下直接关闭窗口window.close();window.location = "about:blank";}
}
;
// 监听鼠标右键是否被按下方法 1, oncontextmenu事件
document.oncontextmenu = function () { return false; };// 监听鼠标右键是否被按下方法 2,onmousedown事件
document.onmousedown = function(evt){// button属性是2 就代表是鼠标右键 if(evt.button == 2){alert('监听到鼠标右键被按下')evt.preventDefault() // 该方法将通知 Web 浏览器不要执行与事件关联的默认动作return false;}
}// 监听用户工具栏调起开发者工具,判断浏览器的可视高度和宽度是否有改变,有改变则处理,
// 判断是否开了开发者工具不太合理。
var h = window.innerHeight, w = window.innerWidth;
window.onresize = function(){alert('改变了窗口高度')
}// hook代码
(function() {//严谨模式 检查所有错误'use strict';// hook 鼠标选择Object.defineProperty(document, 'onselectstart', {set: function(val) {console.log('Hook捕获到选中设置->', val);return val;}});// hook 鼠标右键Object.defineProperty(document,'oncontextmenu',{set:function(evt){console.log("检测到右键点击");return evt}});
})();

webpack hook 半自动扣

//在加载器后面下断点  执行下面代码
// 这里的f 替换成需要导出的函数名
window.zhiyuan = f;
window.wbpk_ = "";
window.isz = false;
f = function(r){if(window.isz){// e[r]里的e 是加载器里的call那里window.wbpk_ = window.wbpk_ + r.toString()+":"+(e[r]+"")+ ",";}return window.zhiyuan(r);
}//在你要的方法加载前下断点 执行 window.isz=true
//在你要的方法运行后代码处下断点  执行 window.wbpk_  拿到所有代码  注意后面有个逗号function o(t) {if (n[t])return n[t].exports;var i = n[t] = {i: t,l: !1,exports: {}};console.log("被调用的 >>> ", e[t].toString());//  这里进行拼接,bb变量需要在全局定义一下 // t 是模块名, e[t] 是模块对应的函数, 也就是key:value形式bb += `"${t}":${e[t].toString()},`return e[t].call(i.exports, i, i.exports, o),i.l = !0,i.exports
}
bz = o;

如果只是调用模块,不用模块里面的方法, 那么直接获取调用模块的时候所有加载过的模块,进行拼接

document下的createElement()方法的hook,查看创建了什么元素

(function() {'use strict'var _createElement = document.createElement.bind(document);document.createElement = function(elm){// 这里做判断 是否创建了script这个元素    if(elm == 'body'){debugger;}return _createElement(elm);}})();

之前我不知道我用的是 var _createElement = document.createElement 导致一直报错 Uncaught TypeError: Illegal invocation
原来是需要绑定一下对象 var _createElement = document.createElement.bind(document);

headers hook 当header中包含Authorization时,则插入断点


var code = function(){
var org = window.XMLHttpRequest.prototype.setRequestHeader;
window.XMLHttpRequest.prototype.setRequestHeader = function(key,value){if(key=='Authorization'){debugger;}return org.apply(this,arguments);
}
}
var script = document.createElement('script');
script.textContent = '(' + code + ')()';
(document.head||document.documentElement).appendChild(script);
script.parentNode.removeChild(script);

请求hook 当请求的url里包含MmEwMD时,则插入断点


var code = function(){
var open = window.XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function (method, url, async){if (url.indexOf("MmEwMD")>-1){debugger;}return open.apply(this, arguments);
};
}
var script = document.createElement('script');
script.textContent = '(' + code + ')()';
(document.head||document.documentElement).appendChild(script);
script.parentNode.removeChild(script);

docuemnt.getElementById 以及value属性的hook

// docuemnt.getElementById 以及value属性的hook,可以参考完成innerHTML的hook
document.getElementById = function(id) {var value = document.querySelector('#' + id).value;console.log('DOM操作 id: ', id)try {Object.defineProperty(document.querySelector('#'+ id), 'value', {get: function() {console.log('getting -', id, 'value -', value);return value;},set: function(val) {console.log('setting -', id, 'value -', val)value = val;}})} catch (e) {console.log('---------华丽的分割线--------')}return document.querySelector('#' + id);
}

过debugger 阿布牛逼


function Closure(injectFunction) {return function() {if (!arguments.length)return injectFunction.apply(this, arguments)arguments[arguments.length - 1] = arguments[arguments.length - 1].replace(/debugger/g, "");return injectFunction.apply(this, arguments)}
}var oldFunctionConstructor = window.Function.prototype.constructor;
window.Function.prototype.constructor = Closure(oldFunctionConstructor)
//fix native function
window.Function.prototype.constructor.toString = oldFunctionConstructor.toString.bind(oldFunctionConstructor);var oldFunction = Function;
window.Function = Closure(oldFunction)
//fix native function
window.Function.toString = oldFunction.toString.bind(oldFunction);var oldEval = eval;
window.eval = Closure(oldEval)
//fix native function
window.eval.toString = oldEval.toString.bind(oldEval);// hook GeneratorFunction
var oldGeneratorFunctionConstructor = Object.getPrototypeOf(function*() {}).constructor
var newGeneratorFunctionConstructor = Closure(oldGeneratorFunctionConstructor)
newGeneratorFunctionConstructor.toString = oldGeneratorFunctionConstructor.toString.bind(oldGeneratorFunctionConstructor);
Object.defineProperty(oldGeneratorFunctionConstructor.prototype, "constructor", {value: newGeneratorFunctionConstructor,writable: false,configurable: true
})// hook Async Function
var oldAsyncFunctionConstructor = Object.getPrototypeOf(async function() {}).constructor
var newAsyncFunctionConstructor = Closure(oldAsyncFunctionConstructor)
newAsyncFunctionConstructor.toString = oldAsyncFunctionConstructor.toString.bind(oldAsyncFunctionConstructor);
Object.defineProperty(oldAsyncFunctionConstructor.prototype, "constructor", {value: newAsyncFunctionConstructor,writable: false,configurable: true
})// hook dom
var oldSetAttribute = window.Element.prototype.setAttribute;
window.Element.prototype.setAttribute = function(name, value) {if (typeof value == "string")value = value.replace(/debugger/g, "")// 向上调用oldSetAttribute.call(this, name, value)
}
;
var oldContentWindow = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, "contentWindow").get
Object.defineProperty(window.HTMLIFrameElement.prototype, "contentWindow", {get() {var newV = oldContentWindow.call(this)if (!newV.inject) {newV.inject = true;core.call(newV, globalConfig, newV);}return newV}
})

过debugger—1 constructor 构造器构造出来的

var _constructor = constructor;
Function.prototype.constructor = function(s) {if (s == "debugger") {console.log(s);return null;}return _constructor(s);
}

过debugger—2 eval的

(function() {'use strict';var eval_ = window.eval;window.eval = function(x) {eval_(x.replace("debugger;", "  ; "));};window.eval.toString = eval_.toString;
}
)();

JSON HOOK

var my_stringify = JSON.stringify;
JSON.stringify = function (params) {//这里可以添加其他逻辑比如 debuggerconsole.log("json_stringify params:",params);return my_stringify(params);
};var my_parse = JSON.parse;
JSON.parse = function (params) {//这里可以添加其他逻辑比如 debuggerconsole.log("json_parse params:",params);return my_parse(params);
};

对象属性hook 属性自定义


(function(){// 严格模式,检查所有错误'use strict'// document 为要hook的对象 ,属性是cookieObject.defineProperty(document,'cookie',{// hook set方法也就是赋值的方法,get就是获取的方法set: function(val){// 这样就可以快速给下面这个代码行下断点,从而快速定位设置cookie的代码debugger;  // 在此处自动断下console.log('Hook捕获到set-cookie ->',val);return val;}})
})();

cookies - 1 (不是万能的 有些时候hook不到 自己插入debugger)

var cookie_cache = document.cookie;Object.defineProperty(document, 'cookie', {get: function() {console.log('Getting cookie');return cookie_cache;},set: function(val) {console.log("Seting cookie",val);var cookie = val.split(";")[0];var ncookie = cookie.split("=");var flag = false;var cache = cookie_cache.split("; ");cache = cache.map(function(a){if (a.split("=")[0] === ncookie[0]){flag = true;return cookie;}return a;})}
})

cookies - 2


var code = function(){var org = document.cookie.__lookupSetter__('cookie');document.__defineSetter__("cookie",function(cookie){if(cookie.indexOf('TSdc75a61a')>-1){debugger;}org = cookie;});document.__defineGetter__("cookie",function(){return org;});
}
var script = document.createElement('script');
script.textContent = '(' + code + ')()';
(document.head||document.documentElement).appendChild(script);
script.parentNode.removeChild(script);// 当cookie中匹配到了 TSdc75a61a, 则插入断点。

window attr


// 定义hook属性
var window_flag_1 = "_t";
var window_flag_2 = "ccc";var key_value_map = {};
var window_value = window[window_flag_1];// hook
Object.defineProperty(window, window_flag_1, {get: function(){console.log("Getting",window,window_flag_1,"=",window_value);//debuggerreturn window_value},set: function(val) {console.log("Setting",window, window_flag_1, "=",val);//debuggerwindow_value = val;key_value_map[window[window_flag_1]] = window_flag_1;set_obj_attr(window[window_flag_1],window_flag_2);},});function set_obj_attr(obj,attr){var obj_attr_value = obj[attr];Object.defineProperty(obj,attr, {get: function() {console.log("Getting", key_value_map[obj],attr, "=", obj_attr_value);//debuggerreturn obj_attr_value;},set: function(val){console.log("Setting", key_value_map[obj], attr, "=", val);//debuggerobj_attr_value = val;},});
}

eval/Function


window.__cr_eval = window.eval;
var myeval = function(src) {// src就是eval运行后 最终返回的值console.log(src);console.log("========= eval end ===========");return window.__cr_eval;
}var _myeval = myeval.bind(null);
_myeval.toString = window.__cr_eval.toString;
Object.defineProperty(window, 'eval',{value: _myeval});window._cr_fun = window.Function
var myfun = function(){var args = Array.prototype.slice.call(arguments, 0, -1).join(","), src = arguments[arguments.lenght -1];console.log(src);console.log("======== Function end =============");return window._cr_fun.apply(this, arguments)
}myfun.toString = function() {return window._cr_fun + ""} //小花招,这里防止代码里检测原生函数
Object.defineProperty(window, "Function",{value: myfun})

eval 取返回值

_eval = eval;
eval = (res)=>{res1 = res // 返回值return _eval(res)
}eval(xxxxxxxxx)

eval proxy代理 https://segmentfault.com/a/1190000025154230

// 代理eval
eval = new Proxy(eval,{// 如果代理的是函数 查看调用 就用apply属性// 第二个参数是prop 这里用不上 因为是属性,eval只是个函数 所以prop为undefind 这里设置了下划线 ——apply: (target,_,arg)=>{// target 是被代理的函数或对象名称,当前是[Function: eval]// arg是传进来的参数,返回的是个列表console.log(arg[0])}
})//  eval执行的时候就会被代理拦截
// 传入的如果是字符串 那么只会返回字符串,这里是匿名函数 直接执行 return了内容
eval((function(){return "我是包子 自己执行了"})()
)// 结果 : 我是包子 自己执行了

websocket hook

 // 1、webcoket 一般都是json数据格式传输,那么发生之前需要JSON.stringify  
var my_stringify = JSON.stringify;
JSON.stringify = function (params) {//这里可以添加其他逻辑比如 debuggerconsole.log("json_stringify params:",params);return my_stringify(params);
};var my_parse = JSON.parse;
JSON.parse = function (params) {//这里可以添加其他逻辑比如 debuggerconsole.log("json_parse params:",params);return my_parse(params);
};// 2  webScoket 绑定在windows对象,上,根据浏览器的不同,websokcet名字可能不一样 
//chrome window.WebSocket  firfox window.MozWebSocket;
window._WebSocket = window.WebSocket;// hook send
window._WebSocket.prototype.send = function (data) {console.info("Hook WebSocket", data);return this.send(data)
}Object.defineProperty(window, "WebSocket",{value: WebSocket})

hook 正则 —— 1

(function () {var _RegExp = RegExp;RegExp = function (pattern, modifiers) {console.log("Some codes are setting regexp");debugger;if (modifiers) {return _RegExp(pattern, modifiers);} else {return _RegExp(pattern);}};RegExp.toString = function () {return "function setInterval() { [native code] }"};
})();

hook 正则 2 加在sojson头部过字符串格式化检测

(function() {var _RegExp = RegExp;RegExp = function(pattern, modifiers) {if (pattern == decodeURIComponent("%5Cw%2B%20*%5C(%5C)%20*%7B%5Cw%2B%20*%5B'%7C%22%5D.%2B%5B'%7C%22%5D%3B%3F%20*%7D") || pattern == decodeURIComponent("function%20*%5C(%20*%5C)") || pattern == decodeURIComponent("%5C%2B%5C%2B%20*(%3F%3A_0x(%3F%3A%5Ba-f0-9%5D)%7B4%2C6%7D%7C(%3F%3A%5Cb%7C%5Cd)%5Ba-z0-9%5D%7B1%2C4%7D(%3F%3A%5Cb%7C%5Cd))") || pattern == decodeURIComponent("(%5C%5C%5Bx%7Cu%5D(%5Cw)%7B2%2C4%7D)%2B")) {pattern = '.*?';console.log("发现sojson检测特征,已帮您处理。")}if (modifiers) {console.log("疑似最后一个检测...已帮您处理。")console.log("已通过全部检测,请手动处理debugger后尽情调试吧!")return _RegExp(pattern, modifiers);} else {return _RegExp(pattern);}};RegExp.toString = function() {return _RegExp.toString();};
}
)();

hook canvas (定位图片生成的地方)

(function() {'use strict';let create_element = document.createElement.bind(doument);document.createElement = function (_element) {console.log("create_element:",_element);if (_element === "canvas") {debugger;}return create_element(_element);}
})();

setInterval 定时器

(function() {setInterval_ = setInterval;console.log("原函数已被重命名为setInterval_")setInterval = function() {};setInterval.toString = function() {console.log("有函数正在检测setInterval是否被hook");return setInterval_.toString();};
}
)();

setInterval 循环清除定时器

for(var i = 0; i < 9999999; i++) window.clearInterval(i)

console.log 检测例子 (不让你输出调试)

var oldConsole = ["debug", "error", "info", "log", "warn", "dir", "dirxml", "table", "trace", "group", "groupCollapsed", "groupEnd", "clear", "count", "countReset", "assert", "profile", "profileEnd", "time", "timeLog", "timeEnd", "timeStamp", "context", "memory"].map(key=>{var old = console[key];console[key] = function() {};console[key].toString = old.toString.bind(old)return old;
}
)

检测函数是否被hook例子

if (window.eval == 'native code') {console.log('发现eval函数被hook了 开始死循环');
}

模拟sleep函数,实现Date的时间增加

var saf, saf_class;
!function() {var v = console.log, n = Function, t = "prototype", e = "toString", o = n[e], i = Symbol("(".concat("", ")_", (Math.random() + "")[e](36))), c = function() {try {return "function" == typeof this && this[i] || o.call(this);} catch (n) {return v("[ERROR toString]", this + ''),"";}};function r(n, t, e) {Object.defineProperty(n, t, {enumerable: !1,configurable: !0,writable: !0,value: e});}delete n[t][e],r(n[t], e, c),r(n[t][e], i, "function toString() { [native code] }"),saf = function(n, m) {return r(n, i, `function ${m ? m : n.name || ""}() { [native code] }`),n;};
}();var v_Date = Date
var sleep_number = 0
function sleep(number) {sleep_number += number;Date = function(_Date) {var bind = Function.bind;var unbind = bind.bind(bind);function instantiate(constructor, args) {return new (unbind(constructor, null).apply(null, args));}var names = Object.getOwnPropertyNames(_Date);for (var i = 0; i < names.length; i++) {if (names[i]in Date)continue;var desc = Object.getOwnPropertyDescriptor(_Date, names[i]);Object.defineProperty(Date, names[i], desc);}function Date() {var date = instantiate(_Date, [v_Date.now() + sleep_number]);// 固定返回某一个时间点return date;}Date.prototype = _Date.prototypereturn saf(Date);}(v_Date);
}

hook Array.concat

BaseArrayConcat = Array.prototype.concathook_concat = function(a){console.log('concat-hook:', JSON.stringify(a))let result = BaseArrayConcat.apply(this, a);result.concat = hook_concat;return result
}
ce.concat = hook_concat

控制台检测

var _0x383ee0 = new Date();var _0x465be6 = 0;_0x383ee0["toString"] = function () {_0x465be6++;console.log(arguments[0])if (_0x465be6 == 2) {return "";}
};console["log"](_0x383ee0);

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

风格迁移adaIN 和iT的adaLN

文章目录 BN、LN、IN、GN的区别![](https://img-blog.csdnimg.cn/direct/d38c005616f145cba2aa1c4c2e046be0.png)图像风格迁移adaINDiT adaLN BN、LN、IN、GN的区别 BatchNorm&#xff1a;batch方向做归一化&#xff0c;算NxHxW的均值&#xff0c;对小batchsize效果不好&#x…

「Kafka」Kafka基础知识入门介绍(三)

「Kafka」Kafka基础知识入门介绍&#xff08;三&#xff09; 一、消息主题1. 创建主题 二、生产数据1. 命令行模式2. Java代码模式 三、消费数据1. 命令行模式2. Java代码模式 「Kafka」Kafka理论知识解读&#xff08;一&#xff09; 「Kafka」Kafka安装和启动&#xff08;二&a…

【kotlin】利用by关键字更加方便地实现装饰器模式

关于kotlin中的by关键字的用法&#xff0c;kotlin官方文档属性委托这一节讲得很清楚。 简单来说就是这样的&#xff0c;假设存在一个接口Component如下&#xff1a; interface Component {fun method1(): IntArrayfun method2(a: Int)fun method3(a: Int, str: String) }那么对…

React-性能优化的手段

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;React篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来React篇专栏内容:React-性能优化的手段 目录 React 性能优化的手段有哪些&#xff1f; 一、是什么 二、如何做…

【无标题】cocos与外部laya或者web交互

一.电脑&#xff1a; 电脑网页&#xff1a;cocos --->laya this.webview._impl._iframe.contentWindow.postMessage("cocosToLaya", "*");//laya //这里是浏览器环境下&#xff0c; 接收web传过来的消息//cocos window.addEventListener(&q…

【汇编语言】流程转移和子程序

【汇编语言】流程转移和子程序 文章目录 【汇编语言】流程转移和子程序前言一、“转移”综述二、操作符offset三、jmp指令jmp指令——无条件转移jmp指令&#xff1a;依据位移进行转移两种段内转移远转移&#xff1a;jmp far ptr 标号转移地址在寄存器中的jmp指令转移地址在内存…

Vue3种常用插槽的使用

插槽总结 &#xff1a; 插槽的作用&#xff1a;让父组件可以向子组件指定位置插入html结构&#xff0c;也是一种组件间通信的方式&#xff0c;适用于 父组件 > 子组件 。分类&#xff1a;默认插槽、具名插槽、作用域插槽 1、默认插槽 父组件中&#xff1a; <Category>…

鸿蒙HarmonyOS应用 - ArkUI组件

ArkUI组件 基础组件 Image 声明Image组件并设置图片源 网络权限&#xff1a;ohos.permission.INTERNET Image(scr: string | PixelMap | Resource)// 1. string&#xff1a;用于加载网络图片&#xff0c;需要申请网络权限 Image("https://xxx.png")// 2. PixelMap…

一线实战,一次底层超融合故障导致的Oracle异常恢复

背景概述 某客户数据由于底层超融合故障导致数据库产生有大量的坏块&#xff0c;最终导致数据库宕机&#xff0c;通过数据抢救&#xff0c;恢复了全部的数据。下面是详细的故障分析诊断过程&#xff0c;以及详细的解决方案描述&#xff1a; 故障现象 数据库宕机之后&#xff0c…

粤嵌—2024/4/24—删除有序数组中的重复项 ||

代码实现&#xff1a; 方法一&#xff1a;双指针 int removeDuplicates(int *nums, int numsSize) {int l 0, r 0;while (r < numsSize) {if (r > 1 && nums[r] nums[l - 1] && nums[r] nums[l - 2]) {r;} else {nums[l] nums[r];l;r;}}return l; }…

ONES 功能上新|ONES Wiki 新功能一览

支持在 ONES Wiki 页面中使用分栏进行横向排版&#xff0c;丰富排版方式&#xff0c;帮助用户以更丰富的版式展示内容。 应用场景&#xff1a; 页面的布局对内容的阅读有很大的影响。当页面中有图文混排的需求时&#xff0c;可以通过分栏来组织页面结构&#xff0c;以更清晰、更…

Docker容器概念介绍与基本管理

前言 在软件开发和部署环境中&#xff0c;使用 Docker 等容器技术可以帮助团队实现快速、一致、可靠的应用程序部署&#xff0c;提高开发效率和应用程序的可移植性。 目录 一、虚拟化产品介绍 1. 云服务模型 1.1 IaaS 1.2 PaaS 1.3 SaaS 1.4 DaaS 2. 产品介绍 2.1 虚…

mysql全量备份及数据恢复实践

前置 myql&#xff1a;8.0.34 percona-xtrabackup&#xff1a;percona-xtrabackup-8.0.34-29 一、全量备份脚本 #!/bin/bash #删除历史 find /data/backups -mtime 10 -exec rm -rf {} \; #下载备份工具 #wget https://file.zjwlyy.cn/percona-xtrabackup-8.0.34-22.tar…

14 JavaScript学习:条件语句

JavaScript条件语句 JavaScript中的条件语句主要用于根据条件执行不同的代码块。以下是对JavaScript条件语句概念的详细解释和分类&#xff1a; if语句&#xff1a; 单个if语句&#xff1a;最简单的条件语句&#xff0c;根据条件判断是否执行特定的代码块。if…else语句&#x…

【机器学习与实现】机器学习概述

目录 一、机器学习的基本概念和方法&#xff08;一&#xff09;基本概念&#xff08;二&#xff09;机器学习的一般过程举例&#xff08;三&#xff09;样本和参数估计 二、机器学习的步骤总结&#xff08;一&#xff09;机器学习的主要步骤&#xff08;二&#xff09;样本及样…

C++并发编程

基本介绍 线程 C98标准没有直接提供原生的多线程支持 在C98中&#xff0c;并没有像后来的C11标准中那样的<thread>库或其他直接的多线程工具 然而&#xff0c;这并不意味着在C98中无法实现多线程。开发者通常会使用平台特定的API&#xff08;如Windows的线程API或POSI…

el-tree 或 el-tree-select 根据条件,控制节点的显示隐藏

目录 情况概述方案&#xff1a;render-content&#xff08;树节点的内容区的渲染 Function&#xff09;代码如下 情况概述 要求el-tree-select 中的节点 根据节点状态来进行显示隐藏&#xff08;状态字段为status&#xff0c;显示&#xff1a;‘1’&#xff0c;隐藏&#xff1…

vmware workstation使用心得

1 启动虚拟机windows系统立即蓝屏 windows未开启虚拟化 vmware workstation与windows系统不兼容 下载兼容版本的vmware workstationwindow10若最新&#xff0c;升级vmware workstation 到15.5.7 排除不兼容的情况&#xff0c;windows系统自身虚拟化配置未开启&#xff1a; VMw…

vue3中的ref、isRef、shallowRef、triggerRef和customRef

1.ref 接受一个参数值并返回一个响应式且可改变的 ref 对象。 ref 对象拥有一个指向内部值的单一属性 .value property &#xff0c;指向内部值。 例&#xff1a;此时&#xff0c;页面上的 str1 也跟着变化 <template><div><button click"handleClick&quo…

如何在 windows 下运行 linux 程序

安装WSL和相关的Linux发行版可以按照以下步骤进行&#xff1a; 启用WSL功能&#xff1a; 打开“控制面板” -> “程序” -> “程序和功能” -> “启用或关闭Windows功能”。在弹出的窗口中&#xff0c;勾选“适用于Linux的Windows子系统”&#xff0c;然后点击“确定”…