【Vue.js设计与实现】第一篇:框架设计概览-阅读笔记(完结)

从高层设计的角度去探讨框架需要关注的问题。

参考:速读《Vue.js 设计与实现》 - 掘金 (juejin.cn)

系列目录:

标题博客
第一篇:框架设计概览【Vue.js设计与实现】第一篇:框架设计概览-阅读笔记
第二篇:响应系统【Vue.js设计与实现】第二篇:响应系统-阅读笔记
第三篇:渲染器【Vue.js设计与实现】第三篇:渲染器-阅读笔记
第四篇:组件化【Vue.js设计与实现】第四篇:组件化-阅读笔记
第五篇:编译器【Vue.js设计与实现】第五篇:编译器-阅读笔记
第六篇:服务端渲染【Vue.js设计与实现】第六篇:服务端渲染-阅读笔记

第一篇:框架设计概览

  • 第 1 章 权衡的艺术
  • 第 2 章 框架设计的核心要素
  • 第 3 章 Vue.js 3 的设计思路

文章目录

    • 第一章 权衡的艺术
      • 1.1 命令式和声明式
      • 1.2 性能与可维护性的权衡
      • 1.3 运行时和编译时
    • 第二章:框架设计的核心要素
      • 2.1 `__DEV__`:在开发环境中为用户提供友好的警告信息的同时,不会增加生产环境代码的体积
      • 2.2 ` /*#__PURE__*/`与Tree Shaking
      • 2.3 框架应该输出怎样的构建产物
      • 2.4 错误处理
      • 2.5 良好的TypeScript类型支持
      • 总结
    • 第三章:Vue.js 3 的设计思路
      • 3.1 声明式地描述UI
      • 3.2 初识渲染器
      • 3.3 组件的本质
      • 3.4 模板的工作原理
      • 3.5 Vue.js 是各个模块组成的有机整体
      • 总结

第一章 权衡的艺术

框架的设计,本身就是一种权衡的艺术。

1.1 命令式和声明式

命令式关注过程,声明式关注结果

命令式:

const div=document.querySelector('#app') //获取div
div.innerText='hello world' //设置文本内容
div.addElementListener('click',()=>{alert('OK')}) //绑定事件

声明式:

<div @click="()=>alert('OK')">Hello world</div>

vue的内部是命令式的,而我们使用vue的时候是声明式的。

即,vue封装了命令式的过程,对外暴露出了声明式的结果

1.2 性能与可维护性的权衡

从性能的角度上看,命令式的性能>声明式的性能。

原因:

命令式的代码通过原生的JS实现,声明式的代码要实现同样功能时,还要再调用相同的命令式代码。
声明式代码的更新性能消耗 = 找出差异的性能消耗+直接修改的性能消耗.
所谓的虚拟 DOM,就是为了最小化找出差异这一步的性能消耗而出现的。

显然命令式的性能更高。此时vue还要对外暴露出声明式的接口,原因是声明式的可维护性,远大于命令式的可维护性

而且,vue在性能优化之下,它并不会比纯命令式的性能差太多。

在前端领域,用JS修改HTML的方式主要有3种:原生JS,innerHTML,虚拟DOM。

心智负担:虚拟DOM < innerHTML< 原生JS
性能:innerHTML < 虚拟DOM < 原生JS
可维护性:原生JS < innerHTML < 虚拟DOM

虚拟DOM的性能并不是最高的,但是vue依然选择虚拟DOM来进行渲染层的构架。

这也是性能与可维护性的权衡。

1.3 运行时和编译时

都是框架设计的一种方式,可单独出现,也可组合使用。

  • 运行时:runtime

利用render函数,把虚拟DOM转化为真实DOM的方式。

  • 编译时:compiler

把template模板中的内容,转化为真实DOM。
注意,存在编译过程,可以分析用户提供的内容。同时,没有运行时理论上性能会更好。

  • 运行时+编译时

过程分两步:

  1. 先把template模板转化成render函数,即编译时
  2. 再利用render函数,把虚拟DOM转化为真实DOM,即运行时

两者的结合,可以:

  • 在编译时:分析用户提供的内容
  • 在运行时,提供足够的灵活性

这也是vue的主要实现方式。

第二章:框架设计的核心要素

2.1 __DEV__:在开发环境中为用户提供友好的警告信息的同时,不会增加生产环境代码的体积

有一个常量__DEV__,存在于所有的console.warn中:

if (__DEV__ && !res) {warn(`Failed to mount app: mount target selector "${container}"returned null.`);
}

在开发环境中__DEV__永远为true,在生产环境中__DEV__永远为false。永远不会执行的代码成为dead code,不会出现在最终产物中。

2.2 /*#__PURE__*/与Tree Shaking

Tree Shaking:消除那些永远不会被执行的代码。

想要实现Tree Shaking,模块必须是ESM。

Tree Shaking的关键点:副作用。如果一个函数调用会产生副作用,那么就不能将其移除。副作用就是,当调用函数的时候会对外部产生影响,例如修改了全局变量。
举个例子:

如果 obj 对象是一个通过 Proxy 创建的代理对象,那么当我们读取对象属性时,就会触发代理对象的 get 夹子(trap),在 get 夹子中是可能产生副作用的,例如我们在 get 夹子中修改了某个全局变量。而到底会不会产生副作用,只有代码真正运行的时候才能知道,JavaScript 本身是动态语言,因此想要静态地分析哪些代码是 dead code 很有难度

静态地分析 JavaScript 代码很困难,所以像 rollup.js 这类工具都会提供一个机制,让我们能明确地告诉 rollup.js:这段代码没有副作用,可以移除。

import {foo} from './utils'
/*#__PURE__*/ foo()

注释代码/*#__PURE__*/,就是告诉rollup.js,对于foo的调用不会产生副作用,可以Tree-Shaking。

实际上,通常产生副作用的代码都是模块内函数的顶级调用。

foo() // 顶级调用function bar(){foo() // 函数内调用
}

可以看到,对于顶级调用来说,是可能产生副作用的;对于函数内调用来说,只要函数 bar 没有调用,那么 foo 函数的调用自然不会产生副作用
因此,在 Vue.js 3 的源码中,基本都是在一些顶级调用的函数上使用 /*#__PURE__*/ 注释。此注释也可以应用在语句上。

2.3 框架应该输出怎样的构建产物

用户能够使用 <script> 标签直接引入 ESM 格式的资源,如:

<script type="module" src="/path/to/vue.esm-browser.js">
</script>

ESM格式的资源中,文件会有一个-browser字样。其实对于ESM格式的资源来说,Vue.js还会输出一个vue.esm-bundler.js 文件。这样做的原因是:在寻找资源时,如果package.json中存在module字段,那么会优先使用module字段指向的资源来代替main字段指向的资源。

如Vue.js源码中的packages/vue/package.json

{"main":"index.js","module":"dist/vue.runtime.esm-bundler.js"
}

带有 -bundler 字样的 ESM 资源是给 rollup.js 或 webpack 等打包工具使用的,而带有 -browser 字样的 ESM 资源是直接给 <script type="module"> 使用的

当我们构建提供给打包工具的ESM格式的资源时,不能直接把__DEV__设置为true或false,而是:process.env.NODE_ENV!=='production'。即,当前环境不是生产环境.

如源码:

if (__DEV__)

在带有 -bundler 字样的资源中会变为:

if ((process.env.NODE_ENV !== 'production'))

2.4 错误处理

举个例子,一个模块导出一个方法,参数是一个回调函数:

import utils from 'utils.js'utils.foo(()=>{
//...
})

如果用户在执行回调函数时出错了,怎么办?有两个方法:让用户自己处理、vue代替用户统一处理错误。

用户自己处理:

utils.foo(()=>{try{//...}catch(e){//...}
})

会增加用户的负担。不建议。

vue代替用户统一处理错误

将错误处理程序封装为一个函数,如:callWithErrorHanding

export default {foo(fn) {callWithErrorHanding(fn);},bar(fn) {callWithErrorHanding(fn);},
};function callWithErrorHanding(fn) {try {fn && fn();} catch (e) {console.log(e);}
}

这样做的好处:为用户提供统一的错误处理接口

提供 registerErrorHandler函数,用户可以使用它注册错误处理程序,在callWithErrorHanding捕获错误后,把错误传给用户注册的错误处理程序:

export default {foo(fn) {callWithErrorHanding(fn);},// 用户可以调用该函数注册统一的错误处理函数registerErrorHandler(fn) {handleError = fn;},
};function callWithErrorHanding(fn) {try {fn && fn();} catch (e) {// 将捕获到的错误传递给用户的错误处理程序handleError(e);}
}

这时错误处理的能力完全由用户控制,用户既可以选择忽略错误,也可以调用上报程序将错误上报给监控系统。

在Vue.js中,我们也可以注册统一的错误处理函数:

import App from "App.vue";
const app = createApp(app);
app.config.errorHandler = () => {// 错误处理程序
};

2.5 良好的TypeScript类型支持

使用TS 编写代码与对TS 类型支持友好是两件事。

举个例子:

function foo(val: any) {return val;
}const res = foo('str')

当我们把鼠标指针悬浮到 res 常量上时,可以看到其类型是 any,而不是string——返回值类型丢失

在这里插入图片描述
为了达到理想状态,需要做出一些修改:

function foo<T extends any>(val: T):T {return val;
}

在这里插入图片描述
由此可见,框架想要做到完善的类型支持,需要付出相当大的努力。

总结

开发体验是衡量一个框架的重要指标之一。大多数情况下“框架”要比开发者更清楚问题出在哪里,因此在框架层面抛出有意义的警告信息是非常必要的。

我们通过预定义 __DEV__ 常量,从而实现仅在开发环境中打印警告信息—— Tree-Shaking 机制。这使得线上代码不会因为警告信息而体积过大。

Tree-Shaking是一种排除 dead code 的机制。一些工具能够识别/*#__PURE__*/ 注释,可以利用此注释来辅助构建工具进行 Tree-Shaking。

对于框架的输出产物,不同类型的产物是为了满足不同的需求。为了让用户能够通过 <script> 标签直接引用并使用,我们需要输出 IIFE 格式的资源,即立即调用的函数表达式。为了让用户能够通过 <script type="module"> 引用并使用,我们需要输出ESM 格式的资源。

需要注意的是,ESM 格式的资源有两种:用于浏览器的 esm-browser.js 和用于打包工具的 esm-bundler.js。它们的区别在于对预定义常量 __DEV__ 的处理,前者直接将 __DEV__ 常量替换为字面量 true 或 false,后者则将 __DEV__ 常量替换为process.env.NODE_ENV !== 'production' 语句。

有时出于灵活性和兼容性的考虑,对于同样的任务,框架提供了两种解决方案,如组合式API/选项式API。不被使用的方案会被 Tree-Shaking 机制排除。

框架的错误处理做得好坏直接决定了用户应用程序的健壮性,同时还决定了用户开发应用时处理错误的心智负担。框架需要为用户提供统一的错误处理接口,这样用户可以通过注册自定义的错误处理函数来处理全部的框架异常。

第三章:Vue.js 3 的设计思路

3.1 声明式地描述UI

Vue.js 3是一个声明式的UI框架。前端页面涉及的东西:

  • DOM 元素:例如是 div 标签还是 a 标签。
  • 属性:如 a 标签的 href 属性,再如 id、class 等通用属性。
  • 事件:如 click、keydown 等。
  • 元素的层级结构:DOM 树的层级结构,既有子节点,又有父节点。

Vue是如何声明式地描述上述内容的:

使用模板来声明式地描述UI

  • 使用与 HTML 标签一致的方式来描述 DOM 元素、属性和层级结构
  • 使用 :v-bind 来描述动态绑定的属性
  • 使用 @v-on 来描述事件

使用JavaScript 对象来声明式地描述UI

const title = {// 标签名称tag: "h1",// 标签属性props: {onClick: handler,},// 子节点children: [{ tag: "span" }],
};

对应到Vue模板:

<h1 onClick="handler"><span></span>
</h1>

使用 JavaScript 对象描述UI比模板描述更加灵活。如:表示一个标题,根据标题级别的不同,会分别采用 h1~h6 这几个标签,JS对象描述:

let level = 3;
const title = {tag: `h${level}`,
};

用模板描述,只能穷举:

<h1 v-if="level === 1"></h1>
<h2 v-else-if="level === 2"></h2>
<h3 v-else-if="level === 3"></h3>
<h4 v-else-if="level === 4"></h4>
<h5 v-else-if="level === 5"></h5>
<h6 v-else-if="level === 6"></h6>

使用 JavaScript 对象来描述 UI的方式,就是虚拟DOM

Vue.js组件中手写的渲染函数就是使用虚拟 DOM 来描述 UI 的,如:

import { h } from "vue";
export default {render() {return h("h1", { onClick: handlder }); // 虚拟DOM},
};

这里的h函数返回值是一个对象,其作用是让我们编写虚拟DOM更加轻松。也可以这样写:

export default {render() {return {tag: "h1",props: { onClick: handler },};},
};

h 函数,一个辅助创建虚拟 DOM 的工具函数

3.2 初识渲染器

渲染器的工作原理其实很简单,是使用一些我们熟悉的 DOM 操作 API 来完成渲染工作。

虚拟 DOM:用 JavaScript对象来描述真实的 DOM 结构。
渲染器:把虚拟 DOM 渲染为真实 DOM。

在这里插入图片描述
举个例子,有虚拟DOM如下:

const vnode = {tag: "div",props: {onClick: () => alert("hello"),},children: "click me",
};
  • tag:标签名称
  • props:一个对象,描述属性、事件等
  • children:标签的子节点

实际上,你完全可以自己设计虚拟 DOM 的结构,例如可以使用tagName 代替 tag,因为它本身就是一个 JavaScript 对象,并没有特殊含义

分析渲染器 renderer 的实现思路

  • 创建元素:vnode.tag 作为标签名
  • 为元素添加属性和事件:遍历 vnode.props 对象,如果 keyon 字符开头,说明它是一个事件
  • 处理 children:若children 是一个数组,就递归地调用renderer 继续渲染

具体代码如下:

渲染器:

  • vnode:虚拟DOM节点
  • container:真实 DOM 元素,作为挂载点,渲染器会把虚拟 DOM 渲染到该挂载点下
function renderer(vnode, container) {// 使用 vnode.tag 作为标签名称创建 DOM 元素const el = document.createElement(vnode.tag);// 遍历 vnode.props,将属性、事件添加到 DOM 元素for (const key in vnode.props) {//  on开头的事件名if (/^on/.test(key)) {el.addEventListener(key.substring(2).toLowerCase(),vnode.props[key]);}}// 处理childrenif (typeof vnode.children === "string") {// 文本子节点el.appendChild(document.createTextNode(vnode.children));} else if (Array.isArray(vnode.children)) {// 递归地调用 renderer 函数渲染子节点,使用当前元素 el 作为挂载点vnode.children.forEach((child) => renderer(child, el));}// 将元素添加到挂载点下container.appendChild(el);
}

如:

<div id="div"></div>
const div = document.getElementById("div");
renderer(vnode, div);

打开浏览器,会发现渲染出了click me,点击它:

在这里插入图片描述

上述仅仅只是创建节点,渲染器的精髓在更新节点:渲染器只更新修改的部分,而不是重新渲染全部节点。

3.3 组件的本质

虚拟 DOM 除了能够描述真实 DOM 之外,还能够描述组件组件就是一 组 DOM 元素的封装

定义一个函数来代表组件,而函数的返回值就代表组件要渲染的内容
这里tag用来描述组件函数

const MyComponent = function () {return {tag: "div",props: {onClick: () => alert("hello"),},children: "click me",};
};const vnode = {tag: MyComponent,
};

总体代码:

function renderer(vnode, container) {// 渲染标签/组件if (typeof vnode.tag === "string") {mountElement(vnode, container);} else if (typeof vnode.tag === "function") {mountComponent(vnode, container);}
}function mountElement(vnode, container) {// 使用 vnode.tag 作为标签名称创建 DOM 元素const el = document.createElement(vnode.tag);// 遍历 vnode.props,将属性、事件添加到 DOM 元素for (const key in vnode.props) {//  on开头的事件名if (/^on/.test(key)) {el.addEventListener(key.substring(2).toLowerCase(),vnode.props[key]);}}// 处理childrenif (typeof vnode.children === "string") {// 文本子节点el.appendChild(document.createTextNode(vnode.children));} else if (Array.isArray(vnode.children)) {// 递归地调用 renderer 函数渲染子节点,使用当前元素 el 作为挂载点vnode.children.forEach((child) => renderer(child, el));}// 将元素添加到挂载点下container.appendChild(el);
}function mountComponent(vnode, container) {// 获取虚拟DOMconst subtree = vnode.tag();renderer(subtree, container);
}

实际上,组件不一定得是函数,也可以是对象。对象里有一个render函数,返回一个虚拟DOM。

const MyComponent = {render() {return {tag: "div",props: {onClick: () => alert("hello"),},children: "click me",};},
};const vnode = {tag: MyComponent,
};function mountComponentObj(vnode, container) {const subtree=vnode.tag.render()renderer(subtree, container);}function renderer(vnode, container) {// 渲染标签/组件if (typeof vnode.tag === "string") {mountElement(vnode, container);} else if (typeof vnode.tag === "function") {mountComponent(vnode, container);} else if (typeof vnode.tag === "object") {mountComponentObj(vnode, container);}
}

3.4 模板的工作原理

这里只需要清楚编译器的作用及角色,具体之后会着重讲。

编译器:将模板编译为渲染函数

一个 .vue 文件就是一个组件,以.vue文件举例:

<template><div @click="handler">click me</div>
</template><script>
export default {};
</script>

其中 <template> 里的内容就是模板内容,编译器会把模板内容编译成渲染函数并添加到 <script> 标签块的组件对象上,所以最终在浏览器里运行的代码为:

export default {render() {return h("div", { onClick: handler }, "click me");},
};

对于组件来说,它要渲染的内容都是渲染函数产生的,然后渲染器再把渲染函数返回的虚拟 DOM 渲染为真实 DOM。这就是Vue.js 渲染页面的流程。

3.5 Vue.js 是各个模块组成的有机整体

编译器和渲染器可以配合工作。假设有模板如下:

<template><div id="foo" :class="cls"></div>
</template>

这里cls是一个变量,通过v-bind动态绑定到class上。渲染器的作用之一就是寻找并且只更新变化的内容。在这里,编译器能识别出哪些是静态属性,哪些是动态属性,在生成代码的时候完全可以附带这些信息,它生成的虚拟DOM如下:

render() {return {tag: "div",props: {id: "foo",class: cls,},patchFlags: 1, // 假设数字 1 代表 class 是动态的};
},

这样,渲染器看到patchFlags: 1就知道class是动态的了。

编译器和渲染器之间是存在信息交流的,它们互相配合使得性能进一步提升,而它们之间交流的媒介就是虚拟 DOM 对象

总结

Vue.js 是一个声明式的框架。声明式的好处在于,它直接描述结果,用户不需要关注过程。描述UI:

  • 采用模板的方式(直观)
  • 支持虚拟DOM的方式(灵活)

渲染器作用:把虚拟 DOM 对象渲染为真实 DOM 元素。工作原理是:递归地遍历虚拟 DOM 对象,并调用原生 DOM API 来完成真实 DOM 的创建。精髓:后续的更新,它会通过 Diff 算法找出变更点,并且只会更新需要更新的内容。

组件的本质:一组虚拟 DOM 元素的封装。它可以是一个返回虚拟 DOM 的函数,也可以是一个对象,但这个对象下必须要有一个函数用来产出组件要渲染的虚拟 DOM

编译器把Vue.js 的模板编译为渲染函数。 编译器、渲染器都是 Vue.js 的核心组成部分,它们共同构成一个有机的整体,不同模块之间互相配合,进一步提升框架性能。

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

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

相关文章

Vue-51、Vue技术github案例(发送ajax)

1、在index引入bootstrap.csss (注意第三方css库最好在indxe里面引入) 2、List.vue源码 <template><div class"row"><div v-show"users.length" class"card" v-for"p in users" :key"p.login"><a :hr…

Qt/C++音视频开发65-切换声卡/选择音频输出设备/播放到不同的声音设备/声卡下拉框

一、前言 近期收到一个用户需求&#xff0c;要求音视频组件能够切换声卡&#xff0c;首先要在vlc上实现&#xff0c;于是马不停蹄的研究起来&#xff0c;马上查阅对应vlc有没有自带的api接口&#xff0c;查看接口前&#xff0c;先打开vlc播放器&#xff0c;看下能不能切换&…

一条select在mysql中的执行过程

查询缓存&#xff1a; 一个select语句&#xff0c;会先到查询缓存中看看&#xff0c;若是以前执行过&#xff0c;直接将查询结果返回给客户端&#xff0c;若是查询缓存没有命中&#xff0c;就需要执行后面的计划 分析器 如果没有命中查询缓存&#xff0c;就要开始分析器了&am…

第八篇:node模版引擎Handlebars及他的高级用法(动态参数)

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! ​ 目录 &#x1f4d8; 引言&#xff1a; &#x1f…

【VSCode 光标返回上一位置】

默认按键 Windows: Alt ← ;或者 鼠标侧键 Linux: Ctrl Alt - ;貌似数字键盘的减号没效果 Mac: Ctrl - 自定义修改方法&#xff1a; VSCode左下角 “管理 / Manage” “键盘快捷方式 / KeyBoard Shortcuts” 搜索 “前进 / Go Forward 或 后退 / Go Back” 双击需…

HarmonyOS4.0系统性深入开发33相对布局(RelativeContainer)

相对布局&#xff08;RelativeContainer&#xff09; 概述 RelativeContainer为采用相对布局的容器&#xff0c;支持容器内部的子元素设置相对位置关系。子元素支持指定兄弟元素作为锚点&#xff0c;也支持指定父容器作为锚点&#xff0c;基于锚点做相对位置布局。下图是一个…

【python基础】爬虫练习

不知道大家有没有通过豆瓣网寻找一些排名较高的电影&#xff0c;反正小编基本上是顺着排名一点点找电影看。 本文将详细介绍如何使用Python爬虫抓取豆瓣网电影评论用户的观影习惯数据&#xff0c;并进行简单的数据处理和分析。 目录 一、配置环境1.1、 安装Python1.2、 安装Re…

『C++成长记』string使用指南

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;C &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、string类介绍 二、string类的常用接口说明 &#x1f4d2;2.1string类对象的常…

Sg7050ccn晶体振荡器spxo规格书

SG7050CCN是一款CMOS输出石英晶体振荡器&#xff0c;小体积尺寸7.0x5.0mm,四脚贴片&#xff0c;额定频率2.5MHz ~ 50MHz&#xff0c;电源电压4.5V至5.5V,工作温度范围B : -20 C to 70 C / G : -40 C to 85 C C&#xff0c;具有小体积轻薄型&#xff0c;低抖动&#xff0c;低功耗…

Unity打包Android,jar文件无法解析的问题

Unity打包Android&#xff0c;jar无法解析的问题 介绍解决方案总结 介绍 最近在接入语音的SDK时&#xff0c;发现的这个问题. 当我默认导入这个插件的时候&#xff0c;插件内部的文件夹&#xff08;我下面话红框的文件夹&#xff09;名字原本为GCloudVoice&#xff0c;这时候我…

vit细粒度图像分类(八)SIM-Trans学习笔记

1.摘要 细粒度视觉分类(FGVC)旨在从相似的从属类别中识别物体&#xff0c;这对人类准确的自动识别需求具有挑战性和实用性。大多数FGVC方法侧重于判别区域挖掘的注意机制研究&#xff0c;而忽略了它们之间的相互依赖关系和组成的整体对象结构&#xff0c;而这些对模型的判别信…

解锁文档处理的全新维度:ONLYOFFICE 文档开发者版

前言 相信大家对于 ONLYOFFICE 这款办公软件可能已经有所耳闻&#xff0c;最近因工作需要&#xff0c;我在众多办公协作工具中选择了 ONLYOFFICE&#xff0c;原因主要是它开源经济实惠&#xff0c;可以部署在自己的服务器上并且能够轻松集成到我们的平台中。在数字化信息时代&…

torch与cuda\cudnn和torchvision的对应

以上图片来源于这篇博客 于是&#xff0c;我需要手动下载0.9.0torchvision 直接在网站https://pypi.tuna.tsinghua.edu.cn/simple/后面加上torchvision&#xff0c;就不用ctrlF搜torchvision了&#xff0c;即进入下面这个网站&#xff0c;找到对应版本的包下载安装即可 https…

Github设置clone慢的解决方案

Github设置代理clone依然慢的解决方案 1、前提&#xff1a; 注意&#xff1a; 必须要有科学上网&#xff01;必须要有科学上网&#xff01;必须要有科学上网&#xff01;重要的事情说三遍&#xff1b; 2、http/https方案&#xff08;git clone时使用http&#xff09;&#x…

MirrorLayer可以正常触摸屏幕原理分析

背景&#xff1a; 上次blog分享了给学员朋友们布置的作业&#xff0c;今天来进行简单的揭秘。 问题&#xff1a; 在多屏互动时候有一个屏幕的画面是一个MirrorLayer&#xff0c;另一个屏幕画面是真实的&#xff0c;即2个屏幕上有一个是MirrorLayer&#xff0c;这个时候疑问就…

【C++基础入门】四、程序流程结构(水仙花数、乘法口诀、七和七的倍数、随机数猜数字)

四、程序流程结构 C/C支持最基本的三种程序运行结构&#xff1a;顺序结构、选择结构、循环结构 顺序结构&#xff1a;程序按顺序执行&#xff0c;不发生跳转选择结构&#xff1a;依据条件是否满足&#xff0c;有选择的执行相应功能循环结构&#xff1a;依据条件是否满足&…

Virtual DOM的实现原理

Virtual DOM的实现原理 课程目标 了解什么是虚拟DOM,以及虚拟DOM的作用Snabbdom的基本使用&#xff08;Vue内部的虚拟Dom是改造了开源库Snabbdom&#xff09;Snabbdom的源码解析 在面试的时候经常会问到虚拟DOM是怎么工作的&#xff0c;通过查看Snabbdom源码&#xff0c;可以…

机器学习---半监督学习(基于分岐的方法)

1. 基于分歧的方法 与生成式方法、半监督SVM、图半监督学习等基于单学习器利用未标记数据不同&#xff0c;基于分歧的方 法(disagreement--based methods)使用多学习器&#xff0c;而学习器之间的“分歧”(disagreement)对未标记 数据的利用至关重要。 1.2 协同训练 “协同…

实验一 古典密码算法的设计与实现

✅作者简介&#xff1a;CSDN内容合伙人、信息安全专业在校大学生&#x1f3c6; &#x1f525;系列专栏 &#xff1a;简单外包单 &#x1f4c3;新人博主 &#xff1a;欢迎点赞收藏关注&#xff0c;会回访&#xff01; &#x1f4ac;舞台再大&#xff0c;你不上台&#xff0c;永远…

猫什么时候发腮?猫咪发腮指南!这些生骨肉冻干发腮效果好

猫什么时候发腮是许多猫主人非常关心的问题。在猫咪的成长过程中&#xff0c;发腮是一项重要的体征&#xff0c;也是猫咪成熟的标志。主人需要在适龄的年龄段加强营养补给&#xff0c;可以让让猫咪拥有可爱的肉嘟嘟脸型&#xff0c;不要错失最佳发腮期。那么&#xff0c;什么时…