Vue2 API-源码解析
目录
Vue.extend(option)
delimiters
functional
Vue.component(id, Function | Object)
Vue.directive( id, [definition] )
Vue.filter( id, function)
Vue.nextTick()
Vue.set()
Vue.delete(target, index/key)
Vue.compile(template)
Vue.observable(object)
provide/inject
extends、mixins
Vue.extend(option)
作用:返回一个vue子组件的构造函数
参数:创建vue实例的参数
<template><div id="home"><div>home</div></div>
</template><script>
import Vue from 'vue'export default {name: "",mounted() {// 创建子组件构造函数 VueComponentvar VueComponentFun = Vue.extend({template: "<p>{{firstName}} {{lastName}} aka {{alias}}</p>",data: function () {return {firstName: "Walter",lastName: "White",alias: "Heisenberg",};},});// 挂载到一个元素上。new VueComponentFun().$mount("#home");},
};
</script>
home节点被替换掉,渲染结果如下
源码:
- 调用Vue.prototype._init()进行数据初始化 - created周期
- 继承原型上的extend、mixin、use、component, directive, filter
Vue.extend = function (extendOptions) {extendOptions = extendOptions || {};var Super = this;......//创建子类(子组件构造函数),先调用父类的方法进行初始化var Sub = function VueComponent(options) {this._init(options);};//创建子类的原型对象Sub.prototype = Object.create(Super.prototype);Sub.prototype.constructor = Sub;Sub.options = mergeOptions(Super.options, extendOptions);Sub['super'] = Super;//对props属性做set、get拦截if (Sub.options.props) {initProps(Sub);}if (Sub.options.computed) {initComputed(Sub);}Sub.extend = Super.extend;Sub.mixin = Super.mixin;Sub.use = Super.use;//赋值生命周期ASSET_TYPES.forEach(function (type) {Sub[type] = Super[type];});......return Sub;};
delimiters
作用:分隔符,定义 模板字符串的变量标识
上面的代码可改为
template: "<p @click='add'>{firstName} {lastName} aka {num}</p>",
delimiters: ['{', '}'],
functional
作用:函数组件,没有响应式数据,也没有实例(this),使用functional:true生命,使用render生成Dom
优点: 1.使用render
函数返回虚拟节点使它们渲染的代价更小;2.不需要实例化
Vue.component('custom-component', Vue.extend({functional: true,props: {params: String,},render: function (h, context) {let props = context.propsreturn h('div', { class: 'custom' }, [h('div', {domProps: {innerText: props.params,},class: ['custom_button'],on: {click: () => {console.log("click")},},})])}
}))
Vue.component(id, Function | Object)
作用:注册或获取全局组件
有下面两种方法生成组件
// 注册组件,传入一个扩展过的构造器
Vue.component('my-component', Vue.extend({ /* ... */ }))// 注册组件,传入一个选项对象 (自动调用 Vue.extend)
Vue.component('my-component', { /* ... */ })
Vue.directive( id, [definition] )
作用:添加自定义指令,可以在绑定的元素未插入到Dom内时,判断Dom是否存在
参数:指令名称、函数
回调参数:
el:指令绑定到的元素
binding:指令接收的参数
- value:指令绑定的对象
- oldValue:指令绑定的对象修改之前的值
- expression:字符串形式的指令表达式
Vue.directive('focus', {// 指令与元素成功绑定时调用bind: function (el, binding) {},// 当被绑定的元素插入到 DOM 中时……inserted: function (el, binding) {},// 命令所绑定的dom及数据有变化时,update: function (el, binding) {},// 指令所在组件的 VNode 及其子 VNode 全部更新后调用componentUpdated: function (el, binding) {},// 指令与元素解绑时调用unbind: function (el, binding) {}
})<div v-focus="{name:a}"></div>
Vue.filter( id, function)
作用:过滤器
参数:被处理的值、接受的其他传参(多个)
//main.js
Vue.filter('capitalize', function (value, a) {// 被处理的值、参数...if (!value) return "";return value + a;
})//home.vue
<template><div id="home"><!-- 大括号绑定,页面显示11 --><div>{{ a | capitalize(10) }}</div> <!-- v-bind绑定 --><A :pd="b | capitalize"></A></div>
</template><script>
import A from "./A.vue";
export default {name: "",components: {A,},data() {return {a: 1,};},
};
</script>
源码:['component', 'directive', 'filter'] 直接返回回调函数
function initAssetRegisters(Vue) {ASSET_TYPES.forEach(function (type) {// @ts-expect-error function is not exact same typeVue[type] = function (id, definition) {if (!definition) {return this.options[type + 's'][id];}else {......if (type === 'component' && isPlainObject(definition)) {// @ts-expect-errordefinition.name = definition.name || id;// this.options._base = Vue构造函数,就是直接调用Vue.extenddefinition = this.options._base.extend(definition);}if (type === 'directive' && isFunction(definition)) {definition = { bind: definition, update: definition };}// 给当前实例this添加{id: definition}this.options[type + 's'][id] = definition;return definition;}};});
}
Vue.nextTick()
作用:在 DOM 更新循环结束之后执行延迟回调,vue修改数据时不能及时反映到页面上,需要一轮事务处理,才能获取到页面上修改过的值
参数:callback()、this
用法:
// 修改数据
vm.msg = 'Hello'
// DOM 还没有更新
Vue.nextTick(function () {// DOM 更新了
})// 作为一个 Promise 使用 (2.1.0 起新增,详见接下来的提示)
Vue.nextTick().then(function () {// DOM 更新了})
注:使用第二种方法时,需要在支持Promise的环境中(IE不支持)
源码:
如果浏览器支持Promise,则使用Promise.resolve()返回一个Promise 对象(微任务),因为微任务会等待主线程的同步任务执行完毕,再执行微任务队列。微任务队列就是下列callbacks数组,这里面会放入回调函数,如果一次同步任务中有多个nextTick,则callback中会有多个回调函数。这些回调函数会在then()回调中执行。
var p_1 = Promise.resolve();timerFunc = function () {p_1.then(flushCallbacks);if (isIOS)setTimeout(noop);};
function nextTick(cb, ctx) {// cb: 回调函数 ctx: this指向var _resolve;// 回调函数放入数组,如果短时间内多次调用nextTick,则数组中有多个回调函数callbacks.push(function () {if (cb) {try {cb.call(ctx);}catch (e) {handleError(e, ctx, 'nextTick');}}else if (_resolve) {_resolve(ctx);}});// 执行回调函数; if (!pending) {pending = true;timerFunc();}// 支持.then写法if (!cb && typeof Promise !== 'undefined') {return new Promise(function (resolve) {_resolve = resolve;});}
}var timerFunc;
// 实现nextTick用了两种方法,Promise.then or MutationObserver
if (typeof Promise !== 'undefined' && isNative(Promise)) {// 浏览器支持Promise的情况,使用Promise// 创建微任务,微任务会等到主线程的代码执行完毕,再执行,所以p_1.then实现了nextTick的功能var p_1 = Promise.resolve();timerFunc = function () {p_1.then(flushCallbacks);if (isIOS)setTimeout(noop);};isUsingMicroTask = true;
}
else if (!isIE &&typeof MutationObserver !== 'undefined' &&(isNative(MutationObserver) ||// PhantomJS and iOS 7.xMutationObserver.toString() === '[object MutationObserverConstructor]')) {// Use MutationObserver where native Promise is not available,// e.g. PhantomJS, iOS7, Android 4.4var counter_1 = 1;var observer = new MutationObserver(flushCallbacks);var textNode_1 = document.createTextNode(String(counter_1));observer.observe(textNode_1, {characterData: true});timerFunc = function () {counter_1 = (counter_1 + 1) % 2;textNode_1.data = String(counter_1);};isUsingMicroTask = true;
}
else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {timerFunc = function () {setImmediate(flushCallbacks);};
}
else {timerFunc = function () {setTimeout(flushCallbacks, 0);};
}
Vue.set()
作用:向具有响应式的对象中添加property,且新的property具有响应式。在创建的过程中对该属性进行依赖收集
<template><div id="home"><div id="customComA">{{ obj }}</div><div id="customComB">{{ arr }}</div></div>
</template><script>
import Vue from "vue";export default {name: "",data() {return {obj: { name: "zz" },arr: [1, 2]};},mounted() {// 无效this.obj.age = 15this.arr[1] = 10// Vue.setVue.set(this.obj, 'age', 15)Vue.set(this.obj, 1, 10)},
};
</script>
源码实现:
- 对Array对象进行修改,对元素进行响应式化
- 对Object类型进行赋值,对赋值的新元素进行响应式
function set(target, key, val) {// obj|arr , key|indexif (process.env.NODE_ENV !== 'production' && (isUndef(target) || isPrimitive(target))) {warn$2("Cannot set reactive property on undefined, null, or primitive value: ".concat(target));}if (isReadonly(target)) {process.env.NODE_ENV !== 'production' && warn$2("Set operation on key \"".concat(key, "\" failed: target is readonly."));return;}var ob = target.__ob__;// 对Array对象进行修改,对元素进行响应式化if (isArray(target) && isValidArrayIndex(key)) {target.length = Math.max(target.length, key);target.splice(key, 1, val);// when mocking for SSR, array methods are not hijackedif (ob && !ob.shallow && ob.mock) {observe(val, false, true);}return val;}// object修改属性值if (key in target && !(key in Object.prototype)) {console.log("obj")target[key] = val;return val;}if (target._isVue || (ob && ob.vmCount)) {process.env.NODE_ENV !== 'production' &&warn$2('Avoid adding reactive properties to a Vue instance or its root $data ' +'at runtime - declare it upfront in the data option.');return val;}// 对空值的赋值不做其他处理if (!ob) {target[key] = val;return val;}// object对象新增属性值defineReactive(ob.value, key, val, undefined, ob.shallow, ob.mock);if (process.env.NODE_ENV !== 'production') {ob.dep.notify({type: "add" /* TriggerOpTypes.ADD */,target: target,key: key,newValue: val,oldValue: undefined});}else {ob.dep.notify();}return val;
}
Vue.delete(target, index/key)
作用:删除属性,并触发有关dom的改变
对于Array,使用 target.splice(key, 1);
对于Object,使用 delete target[key]; 并触发监听器ob.dep.notify()
源码:
function del(target, key) {if (process.env.NODE_ENV !== 'production' && (isUndef(target) || isPrimitive(target))) {warn$2("Cannot delete reactive property on undefined, null, or primitive value: ".concat(target));}// 数组类型的直接操作if (isArray(target) && isValidArrayIndex(key)) {target.splice(key, 1);return;}var ob = target.__ob__;......if (!hasOwn(target, key)) {return;}// Object类型delete target[key];if (!ob) {return;}// 通知删除的该元素关联的依赖if (process.env.NODE_ENV !== 'production') {ob.dep.notify({type: "delete" /* TriggerOpTypes.DELETE */,target: target,key: key});}else {ob.dep.notify();}
}
Vue.compile(template)
作用:将一个模板字符串编译成 render 函数(VNode)
//把解析的模板渲染,挂载到.home节点上
let res = Vue.compile("<div class='wrapper'>{{ msg }}</div>");
new Vue({data: {msg: "hello",},render: res.render,// staticRenderFns: res.staticRenderFns,
}).$mount(".home");
源码:
var _a = createCompiler(baseOptions)
执行createCompiler函数,即createCompilerCreator函数,即执行 createCompiler函数,同时对baseCompile、compile函数进行缓存;
Vue.compile = compileToFunctions;
Vue.compile("<div class='wrapper'>{{ msg }}</div>");
1.执行createCompileToFunctionFn(compile),即compileToFunctions函数,判断缓存中是否有该模板的编译结果,如果有,取出返回;没有则执行compile函数,之后把获取到的编译结果res.render转化为函数形式,编译结果res写入缓存
2.执行compile函数,先执行baseCompile编译模板template,再在编译结果compiled上添加errors、tips(与options有关,没传入,暂不考虑)
3.执行baseCompile函数,把template模板字符串处理成ast(树状数据结构,未注入真实数据),调用generate把ast转化为render渲染函数字符串形式,返回{ast, render,staticRenderFns)
部分简略源码:
function createCompileToFunctionFn(compile) {// compile, 一开始就被缓存的参数var cache = Object.create(null); //对解析的模板进行缓存return function compileToFunctions(template, options, vm) {/*** template: <div class='wrapper'><div>{{ msg }}</div></div>* option未传*/console.log("1----compileToFunctions")......var key = template;if (cache[key]) {return cache[key];}// compilevar compiled = compile(template, options);......var res = {};var fnGenErrors = [];res.render = createFunction(compiled.render, fnGenErrors);res.staticRenderFns = compiled.staticRenderFns.map(function (code) {return createFunction(code, fnGenErrors);});......return (cache[key] = res);};
}function createCompilerCreator(baseCompile) {// baseCompile, 一开始就被缓存的参数return function createCompiler(baseOptions) {// 先调用createCompiler,什么都不执行,只返回return {compile, compileToFunctions}function compile(template, options) {console.log("2----compile")var finalOptions = Object.create(baseOptions);......var compiled = baseCompile(template.trim(), finalOptions);if (process.env.NODE_ENV !== 'production') {detectErrors(compiled.ast, warn);}compiled.errors = errors;compiled.tips = tips;return compiled;}return {compile: compile,compileToFunctions: createCompileToFunctionFn(compile)};};
}var createCompiler = createCompilerCreator(function baseCompile(template, options) {console.log("3----baseCompile")var ast = parse(template.trim(), options); // 把template字符串处理成树状数据结构,未注入真实数据var code = generate(ast, options); // code: {render, staticRenderFns}return {ast: ast,render: code.render, // render函数staticRenderFns: code.staticRenderFns};
});
//入口
var _a = createCompiler(baseOptions), compileToFunctions = _a.compileToFunctions;
Vue.compile = compileToFunctions;
Vue.observable(object)
作用:让一个对象可响应,返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器
1.使用计算属性
<template><div id="home"><!-- 点击可改变obj、arr, 并能响应页面 --><div @click="changeObj">obj: {{ obj }}</div><div @click="changeArr">arr: {{ arr }}</div></div>
</template><script>
import Vue from "vue";
// 需要使用computed做中介,供页面使用
const state = Vue.observable({ arr: [1,2,3], obj: {name: 'haha', age: 10} });
console.log("state: ", state); //响应式数据export default {name: "",data() {return {};},computed: {obj(){return state.obj},arr(){return state.arr}},methods: {changeObj(){state.obj.age++},changeArr(){state.arr.push(10)},pushRouter(){this.$router.push("/b")}},
};
</script>
2.把该步骤单独封装为文件,使用计算属性,可作为跨组件状态存储器使用
// store.js
import Vue from "vue";
const state = Vue.observable({ arr: [1,2,3], obj: {name: 'haha', age: 10} });
export default state//A页面
<template><div>arr: {{ arr }}</div>
</template><script>
import state from "@/store/replaceStore.js";
export default {computed: {arr() {return state.arr;},},
};
</script>
源码:
function initGlobalAPI(Vue) {......Vue.observable = function (obj) {observe(obj);return obj;};......
}function observe(value, shallow, ssrMockReactivity) {// 已经有响应式对象,直接retuen响应式对象if (value && hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {return value.__ob__;}......return new Observer(value, shallow, ssrMockReactivity);
}function Observer(value, shallow, mock) {if (shallow === void 0) { shallow = false; }if (mock === void 0) { mock = false; }this.value = value;this.shallow = shallow;this.mock = mock;this.dep = mock ? mockDep : new Dep();this.vmCount = 0;// 给value添加属性{__ob__: Observer对象}, 表示该数据value已经被响应式def(value, '__ob__', this);if (isArray(value)) {if (!mock) {if (hasProto) {value.__proto__ = arrayMethods;}else {// 对数组进行拦截 for (var i = 0, l = arrayKeys.length; i < l; i++) {var key = arrayKeys[i];def(value, key, arrayMethods[key]);}}}if (!shallow) {this.observeArray(value);}}else {// 对每个属性进行响应式 var keys = Object.keys(value);for (var i = 0; i < keys.length; i++) {var key = keys[i];defineReactive(value, key, NO_INIITIAL_VALUE, undefined, shallow, mock);}}
}
provide/inject
作用:祖孙组件通信
provide:返回一个对象,或者一个返回对象的函数
inject:注入的字段名,可重命名
//返回一个对象
//父组件
provide: {msg: 'haha', //--非响应式name: this.name, //--简单类型,非响应式arr: this.arr, //--Array、Object类型,响应式
},
//子组件
inject: ["name"],//返回一个函数
//父组件
provide: {name: () => this.subName, //--简单类型,响应式
},
//子组件
<template><div>name: {{ name() }}</div>
</template>
inject: ["name"],
v-once
模板只渲染一次,不会根据数据的变化而重新渲染模板
<transition>
vue内部自定义组件,可以设置过渡效果
参数:
- name,会根据name自动生成六个类名,表示不同的过渡阶段
extends、mixins
作用:扩展另一个组件,与minxins原理类似
extends会比mixins先执行。执行顺序:extends > mixins > 组件
<template><div id="home"><div>firstName: {{ firstName }}</div></div>
</template><script>
export default {name: "",extends: {data: function () {return {firstName: "Walter"};},mounted() {// console.log("mounted: ", this);},},
};
</script>
源码:使用深搜的方法,把extends、mixin的对象扁平化到组件一级
function mergeOptions(parent, child, vm) {if (process.env.NODE_ENV !== 'production') {checkComponents(child);}if (isFunction(child)) {// @ts-expect-errorchild = child.options;}normalizeProps(child, vm);normalizeInject(child, vm);normalizeDirectives$1(child);if (!child._base) {if (child.extends) {parent = mergeOptions(parent, child.extends, vm);}if (child.mixins) {for (var i = 0, l = child.mixins.length; i < l; i++) {parent = mergeOptions(parent, child.mixins[i], vm);}}}var options = {};var key;// 映射parent的key到一级for (key in parent) {mergeField(key);}// 映射child的key(parent没有的)到一级for (key in child) {if (!hasOwn(parent, key)) {mergeField(key);}}function mergeField(key) {var strat = strats[key] || defaultStrat;options[key] = strat(parent[key], child[key], vm, key);}// 把扁平化的数据返回return options;
}
相关文章:

Vue2 API-源码解析
目录 Vue.extend(option) delimiters functional Vue.component(id, Function | Object) Vue.directive( id, [definition] ) Vue.filter( id, function) Vue.nextTick() Vue.set() Vue.delete(target, index/key) Vue.compile(template) Vue.observable(object) …...

FastViT: A Fast Hybrid Vision Transformer using Structural Reparameterization
FastViT: A Fast Hybrid Vision Transformer using Structural Reparameterization 论文地址:https://arxiv.org/pdf/2303.14189.pdf 概述 本文提出了一种通用的 CNN 和 Transformer 混合的视觉基础模型 移动设备和 ImageNet 数据集上的精度相同的前提下…...

C/C++文档阅读笔记-A Simple Makefile Tutorial解析
Makefile文件可以使得程序编译变得简单。本博文并不是很系统的讲解makefile,本博文的目标是让读者快速编写自己的makefile文件并能应用到中小项目中。 简单实例 举个例子有下面3个文件,分别是hellomake.c,hellofunc.c,hellomake.…...

GraphSAGE的基础理论
文章目录GraphSAGE原理(理解用)GraphSAGE工作流程GraphSAGE的实用基础理论(编代码用)1. GraphSAGE的底层实现(pytorch)PyG中NeighorSampler实现节点维度的mini-batch GraphSAGE样例PyG中的SAGEConv实现2. …...

Windows 安装 GDAL C++库
Windows 安装 GDAL C库1. 方法1:下载配置网友编译的GDAL版本1.1 下载1.2 配置1.3 测试1.4 缺点2. 方法2:自己编译3. 参考1. 方法1:下载配置网友编译的GDAL版本 1.1 下载 CSDN: GDAL,geos联合编译的库,版本为1.8.0&am…...

二叉树基础概念
1.二叉树种类 1.1 满二叉树 满二叉树:如果一棵二叉树只有度为 0 0 0 的结点和度为 2 2 2 的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。 如图所示: 这棵二叉树为满二叉树,也可以说深度为 k k k&…...

【MySQL】(1)数据库基础,库与表的增删查改,数据库的备份与还原
文章目录服务器,数据库,表关系MySQL 数据存储逻辑SQL 分类存储引擎库的操作查看数据库创建数据库查看创建语句删除数据库选择(切换)数据库查看当前选择的数据库修改数据库字符集和排序规则表的操作创建表查询表查询表结构插入数据…...

Python基础-01 变量
注释 注释的分类 在Python中,支持单行及多行注释 单行注释 使用#对代码进行说明,#右边的所有内容就是注释的内容,起辅助说明作用 # #右边的都是注释,解析器会忽略 print(hello world) #在控制台里打印一段话多行注释 多行注释中,允许换行,使用三个单引号开始,三个单引号结…...

springcloud2.1.0整合seata1.5.2+nacos2.10(附源码)
springcloud2.1.0整合seata1.5.2nacos2.10(附源码) 1.创建springboot2.2.2springcloud2.1.0的maven父子工程如下,不过多描述: 搭建过程中也出现很多问题,主要包括: 1.seataServer.properties配置文件的组…...

map原理
map源码结构体: type hmap struct {count int // 元素的个数B uint8 // buckets 数组的长度就是 2^B 个overflow uint16 // 溢出桶的数量buckets unsafe.Pointer // 2^B个桶对应的数组指针oldbuckets unsafe.Pointer // 发生扩容时࿰…...

[Ext JS]3.6 Ext JS 表格(Grid)概览
Grid, 翻译过来是网格, 也就是表格。 Grid 的基本构成 面板 :Ext.grid.Panel表格视图 :Ext.view.Table。 不直接使用, 通过面板的viewConfig配置项进行配置。比如可以用来配置表格中行是否跳色显示列: Ext.grid.column.Column。 表格中的列定义store , 表格的数据示例代码…...

关于使用云渲染的五大优势
在不影响质量或性能的情况下节省时间、金钱和资源,对于需要在通常较短且严格的期限内创建高质量 3D 内容的专业人士来说,云渲染都是最好的选择!云渲染作为数字媒体生产的最新趋势,与传统的渲染农场和机器相比具有许多优势…...

CSS基础样式
1.高度和宽度 .c1{height:300px;width:500px; } 注意事项: 宽度,支持百分比 行内标签:默认无效 块级标签:默认有效(右侧区域就算是空白,也不给占用) 2.块级和行内标签 css样式:标签…...

第03章_流程控制语句
第03章_流程控制语句 讲师:尚硅谷-宋红康(江湖人称:康师傅) 官网:http://www.atguigu.com 本章专题与脉络 流程控制语句是用来控制程序中各语句执行顺序的语句,可以把语句组合成能完成一定功能的小逻辑模…...

配电网电压调节及通信联系研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

stegano(图片隐写、摩斯密码)
附件是PDF,我们在选择内容时发现光标溢出了文本 说明这里还存在一些我们看不到的内容 直接CtrlA全选,CtrlC复制后新建一个纯文本文件 将复制的东西粘贴过去 粘贴后发现果然多出来了一些东西,提取出来 BABA BBB BA BBA ABA AB B AAB ABAA A…...

wsl安装torch_geometric
在官网选择需要的版本 选择安装途径,选择runfile 执行第一行,会下载一个文件到目录下 需要降低C的版本,否则 执行sudo sh cuda_11.1.0_455.23.05_linux.run,会出现 查看对应的文件,会有 可以加上override参数之后,…...

ASP.NET Core - 依赖注入(二)
2,NET Core 依赖注入的基本用法 话接上篇,这一章介绍 .NET Core 框架自带的轻量级 Ioc 容器下服务使用的一些知识点,大家可以先看看上一篇文章 [ASP.NET Core - 依赖注入(一)] 2.3 服务解析 通过 IServiceCollection 注册了服务之后…...

Scala之集合(1)
目录 集合介绍: 不可变集合继承图:编辑 可变集合继承图 数组: 不可变数组: 样例代码: 遍历集合的方法: 1.for循环 2.迭代器 3.转换成List列表: 4.使用foreach()函数&a…...

公网使用SSH远程登录macOS服务器【内网穿透】
文章目录前言1. macOS打开远程登录2. 局域网内测试ssh远程3. 公网ssh远程连接macOS3.1 macOS安装配置cpolar3.2 获取ssh隧道公网地址3.3 测试公网ssh远程连接macOS4. 配置公网固定TCP地址4.1 保留一个固定TCP端口地址4.2 配置固定TCP端口地址5. 使用固定TCP端口地址ssh远程前言…...

PVE相关的各种一键脚本(一键安装PVE)(一键开设KVM虚拟化的NAT服务器-自带内外网端口转发)
PVE 原始仓库:https://github.com/spiritLHLS/pve 前言 建议debian在使用前尽量使用最新的系统 非debian11可使用 debian一键升级 来升级系统 当然不使用最新的debian系统也没问题,只不过得不到官方支持 请确保使用前机器可以重装系统,…...

CSDN目录博客(zhaoshuangjian)
总目录 一、Java1.1 高并发1.2 多线程1.3 集合1.4 I/O1.5 异常1.6 事务1.7 锁机制1.8 JVM 二、数据库2.1 mysql2.1.1 mysql索引2.1.1 mysql锁2.1.1 mysql事务2.1.1 2.2 oracle2.3 postgresql2.4 达梦2.5 人大金仓kingbase 三、设计模式四、中间件4.1 缓存中间件-redis4.2 缓存中…...

uniapp人脸识别解决方案
APP端: 因为APP端无法使用uni的camera组件,最开始考虑使用内嵌webview的方式,通过原生dom调用video渲染画面然后通过canvas截图。但是此方案兼容性在ios几乎为0,如果app只考虑安卓端的话可以采用此方案。后面又想用live-pusher组件…...

hashlib模块
欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起探讨和分享Linux C/C/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。 hashlib模块专栏:《python从入门到实战》 哈希算法,也叫摘要算法。 加密&…...

NC65合并报表如何取消上报并退回以及注意事项和相关问题总结
NC65合并报表如何取消上报并退回? 在【企业绩效管理】-【合并报表】-【合并】-【合并执行】节点中,点击〖数据中心〗按钮,在弹出的〖合并报表数据中心〗界面中,点击〖报送管理〗-〖合并方案请求退回〗,然后到【合并综…...

28岁,终于从字节退休了...
大厂一直是每个程序员都向往职业目标,大厂意味着薪资高、福利好、倍有面儿,而且发展空间也大。甚至有人调侃不想进大厂的程序员不是好程序员。 而在网上,也有各个网友分享自己在大厂的经历,在某平台还有一个近2600万浏览的话题&a…...

数据的表示和存储——
目录 浮点数的编码表示 浮点数类型 编辑 浮点数的表示 (1)浮点数(Float Point)的表示范围 (2)规格化数形式 (3)IEEE 754标准 其他形式的机器数表示 个人总结 浮点数的编码表…...

springboot零基础到项目实战
推荐教程: springboot零基础到项目实战 SpringBoot这门技术课程所包含的技术点其实并不是很多,但是围绕着SpringBoot的周边知识,也就是SpringBoot整合其他技术,这样的知识量很大,例如SpringBoot整合MyBatis等等。因此…...

自媒体都在用的5个素材网站,视频、音效、图片全部免费下载~
推荐几个自媒体必备的素材库,免费可商用,建议收藏! 1、菜鸟图库 视频素材下载_mp4视频大全 - 菜鸟图库 国内超大的素材库,在这里你可以找到设计、办公、图片、视频、音频等各种素材。视频素材就有上千个,全部都很高清…...

开放式耳机新巅峰!南卡OE Pro兼备澎湃音质、舒适佩戴、创新设计
众所周知,当初苹果带来TWS耳机新时代以后,后面有许多的蓝牙耳机相继跟随和模仿,但NANK南卡却独辟蹊径,将在近日重磅推出首款0压无感全开放无线耳机——南卡OE Pro,走向开放式TWS耳机的新时代。 31度黄金倾斜受力面&…...