【Vue.js设计与实现】阅读笔记(持续更新)

news2024/11/17 19:49:55

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

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

文章目录

    • 第一章 权衡的艺术
      • 命令式和声明式
      • 性能与可维护性的权衡
      • 运行时和编译时
    • 第二章:框架设计的核心要素
      • `__DEV__`:在开发环境中为用户提供友好的警告信息的同时,不会增加生产环境代码的体积
      • ` /*#__PURE__*/`与Tree Shaking
      • 框架应该输出怎样的构建产物
      • 错误处理
      • 良好的TypeScript类型支持
      • 总结
    • 第三章:Vue.js 3 的设计思路
      • 声明式地描述UI
      • 初识渲染器
    • 第四章:响应系统的作用与实现
      • 响应式数据
      • 调度系统
      • 计算属性
      • 懒惰执行
      • watch的实现原理
      • 过期的副作用
    • 第七章:渲染器的设计
    • 第八章:挂载与更新
      • DOM节点操作
      • 属性节点操作
      • 事件

第一章 权衡的艺术

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

命令式和声明式

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

命令式:

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封装了命令式的过程,对外暴露出了声明式的结果

性能与可维护性的权衡

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

原因:

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

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

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

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

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

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

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

运行时和编译时

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

  • 运行时:runtime

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

  • 编译时:compiler

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

  • 运行时+编译时

过程分两步:

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

两者的结合,可以:

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

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

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

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

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

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

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

/*#__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__*/ 注释。此注释也可以应用在语句上。

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

用户能够使用 <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'))

错误处理

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

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 = () => {
	// 错误处理程序
};

良好的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 的设计思路

声明式地描述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 的工具函数

初识渲染器


在Vue中,UI形式主要分为两种:

  • 声明式的模板描述:本质上是template模板。它会被编译器编译,得到渲染函数render
<div :id="Id" :class="{Class:true}" @click="onClick">
  • 命令式的render函数
import {h} from 'vue'

export default {
	render(){
		return h('h1',{onClick:handler}) //虚拟DOM
	}
}

注意,渲染器和渲染函数,不一样

渲染器是:

  • 函数createRenderer的返回值,是一个对象,被叫做renderer
  • renderer对象中有一个方法叫render,即渲染函数

即,渲染函数是渲染器的返回对象的一个方法

render渲染函数接受两个参数:VNodecontainer

VNode即虚拟DOM,本质上是一个JS对象。
container是一个容器,即被挂载的位置。
render的作用:把VNode挂载到container上。

同时,Vue以组件代表最小颗粒度,vue内部的渲染,本质上是大量的组件渲染
而组件本质上是一组DOM的集合,因此,vue渲染一个组件就是在渲染一组DOM。
即,以组件为介质,来完成针对一组组DOM的渲染

第四章:响应系统的作用与实现

响应式数据

先了解两个概念:副作用函数和响应式数据。

副作用函数:会产生副作用的函数。

如:

let val=1

function effect(){
	val=2
}

这个函数改变了全局变量val,产生了副作用,因此是副作用函数。

而如果val这个数据的变化改变了视图的变化,那么val就称作响应式数据

想要实现响应式数据,需要依赖两个行为:

  • getter:数据读取
  • setter:数据修改

vue2中这两个行为通过Object.defineProperty实现
vue3中这两个行为通过Proxy实现。

响应式数据的实现原理

Vue在组件和实例初始化的时候,会将data里的数据进行数据劫持,即Object.defineProperty对数据进行处理。被劫持后的数据会有两个属性:gettersetter

使用数据时触发getter,修改数据时触发setter,同时也出发了底层的watcher监听,通知DOM修改刷新。 这就实现了响应式数据。

Vue中的数据变页面一定变吗?

不一定。数据变页面也变是因为数据有getter和setter两个属性。如果没有则不会变。

vue中的响应式是什么?
怎么理解响应式原理?_vue响应式属性_J.P_P的博客-CSDN博客
Vue 核心之数据劫持 - Jaye8584 - 博客园 (cnblogs.com)

调度系统

调度系统,指的是响应性的可调度性

可调度性,指的是,当数据更新的动作,触发副作用函数重新执行时,有能力决定:副作用函数effect执行的时机、次数以及方式。

想要实现一个调度系统,需要依赖:异步Promise和队列jobQueue。需要:基于Set构建出一个基本的队列数组jobQueue,利用Promise的异步特性,来控制执行的顺序。

计算属性

计算属性的本质:一个属性值,当依赖响应式数据发生变化时,重新计算。

计算属性的实现:依赖于调度系统。

懒惰执行

watch监听器:观测一个响应式数据,当数据发生变化时,通知并执行对应的回调函数。

这意味着,watch很多时候不需要立即执行。因此,需要懒惰执行进行控制。

懒惰执行的实现:比调度系统简单。本质上是一个boolean型的值,被添加到effet函数中,用来控制副作用的执行。

if(!lazy){
	//执行副作用函数
}

watch的实现原理

基于调度系统和懒惰执行。

过期的副作用

我们可以在watch完成异步操作。但是,大量的异步操作可能导致竞态问题

竞态问题:在描述一个系统或进程的输出,依赖于不受控制的事件出现顺序或者出现时机。

如:

let findData

watch(obj,async()=>{
	const res=await fetch('/path/to/request')
	findData=res
})

这段代码是异步操作。若obj改变了两次,就会发送两次网络请求。我们无法知道findData最后保存的值是哪一次网络请求的。这种问题就是竞态问题

而如果想要解决这问题,那么就需要使用到 watch 回调函数的第三个参数 onInvalidate,它本身也是一个回调函数。并且该回调函数(onInvalidate)会在副作用下一次重新执行前调用,可以用来清除无效的副作用,例如等待中的异步请求
而onInvalidate 的实现原理也非常简单,只需要 在副作用函数(effct)重新执行前,先触发 onInvalidate 即可。

好抽象,没太懂。

第七章:渲染器的设计

渲染器和渲染函数不是一个东西。

  • 渲染器是createRenderer的返回值,是一个对象
  • 渲染函数是渲染器对象中的render方法

在vue 3.2.37 源码中,createRenderer函数具体是通过baseCreateRenderer进行的。总体可以分为两部分:

  1. 在浏览器端渲染时,利用DOM API完成DOM操作:如渲染DOM用createElement,删除DOM使用removeChild
  2. 渲染器不能和宿主环境(浏览器)强耦合:vue中有浏览器渲染和服务端渲染。若与浏览器强耦合就不好实现服务端渲染了。

vnode

一个普通的JS对象,代表了渲染的内容。 对象中通过type表示渲染的DOM。如type===div表示div标签。type===Fragment表示文档片段。

第八章:挂载与更新

对于渲染器,它所作的最核心的事情是:对节点进行挂载、更新。

第八章分为两部分来讲解这件事:

  1. DOM节点操作
  2. 属性节点操作

DOM节点操作

分为3部分:

  • 挂载:节点的初次渲染。如用createElement新建一个节点。
  • 更新:当响应性数据发生变化时,可能会涉及到DOM的更新。本质上属于属性的更新。
  • 卸载:旧节点不再需要了,就删除旧节点。如通过parentEl.removeChild进行。

属性节点操作

属性可以分为两类:

  1. 属性,如:class,id,value…
  2. 事件,如:click、input…

先了解非事件的属性部分。
想了解vue对属性的处理,需要先了解浏览器中的属性分类。
浏览器中,DOM属性被分为两类:

  1. HTML Attributes:直接定义在HTML上的属性
  2. DOM Properties:拿到DOM对象后定义的属性。

对于HTML Attributes,它只能在html中操作。而想要在JS中操作DOM,需要通过DOM Properties来实现。因为JS本身特性的问题,会导致某些DOM Properties的设置存在特殊性,如classtypevalue。为了保证DOM Properties的成功设置,我们需要知道不同属性的DOM Properties定义方式。

  1. el.setAttribute('属性名','属性值')
  2. . 属性赋值el.属性名=属性值el[属性名]=属性值

在这里插入图片描述
在这里插入图片描述

事件

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

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

相关文章

【AD9361 LVDS 时序图 补充】

ADI 官方图 ​2T2R LVDS 整理补充完整 特别注意调整frame

Visual Studio 2022 C++ 生成dll或so文件在windows或linux下用C#调用

背景 开发中我们基本使用windows系统比较快捷&#xff0c;但是部署的时候我们又希望使用linux比较便宜&#xff0c;硬件产商还仅提供了c sdk&#xff01;苦了我们做二次开发的码农。 方案 需要确认一件事&#xff0c;目前c这门语言不是跨平台的 第一个问题【C生成dll在window…

Unity3d Cinemachine篇(三)— FreeLook

文章目录 前言一、使用FreeLook制造第三人称跟随效果1. 创建一个游戏物体2. 创建FreeLook相机4. 完成 前言 上一期我们简单的使用了Dolly CamerawithTrack相机&#xff0c;这次我们来使用一下FreeLook 一、使用FreeLook制造第三人称跟随效果 1. 创建一个游戏物体 游戏物体比较…

美国将限制中国,使用Azure、AWS等云,训练AI大模型

1月29日&#xff0c;美国商务部在Federal Register&#xff08;联邦公报&#xff09;正式公布了&#xff0c;《采取额外措施应对与重大恶意网络行为相关的国家紧急状态》提案。 该提案明确要求美国IaaS&#xff08;云服务&#xff09;厂商在提供云服务时&#xff0c;要验证外国…

【Linux】fork()函数

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

详细讲解Java中的Properties类

目录 前言1. 基本知识2. 代码示例3. Demo 前言 使用Properties出现中文乱码可看我这篇文章&#xff1a;properties出现中文乱码解决方法&#xff08;万能&#xff09; 1. 基本知识 Properties 类是 Java 中用于处理配置文件的工具类&#xff0c;它继承自 Hashtable 类&#…

防火墙到防火墙的高可用知识汇总

目录​​​​​​​ 防火墙 防火墙的分类&#xff1a; 防火墙的发展史 传统防火墙&#xff08;包过滤防火墙&#xff09;—— 一个严格的规则表 传统防火墙&#xff08;应用代理防火墙&#xff09;—— 每个应用添加代理 传统防火墙&#xff08;状态检测防火墙&#xff09…

去中心化世界的奇迹:深度解析Web3

随着科技的飞速发展&#xff0c;我们正逐渐进入一个新的数字时代&#xff0c;而Web3技术正是这个时代的奇迹之一。本文将深入解析Web3&#xff0c;揭示它在构建去中心化世界方面的深远影响以及给我们带来的可能性。 什么是Web3&#xff1f; Web3是互联网的第三个时代&#xff…

借助gpt生成ppt:文心一言(chatgpt)、chatppt

提供一种简单的基于gpt快速生成ppt的方式。前置条件&#xff1a; 文心一言chatpptwps/office ppt Step1: 下载chatppt插件 https://chat-ppt.com/invitelinke?share_code47949695&channelchat-ppt.com 注册地址 下载完成后&#xff0c;安装即可&#xff0c;安装完成后…

k8s 进阶实战笔记 | 应用的蓝绿、金丝雀发布笔记

文章目录 应用的蓝绿、金丝雀发布笔记应用升级策略停机升级滚动更新蓝绿发布金丝雀发布 应用的蓝绿、金丝雀发布笔记 应用升级策略 Deployment.spec.strategy 设置 Recreate&#xff1a;同时删除所有副本&#xff0c;停机升级策略 不存在新老版本共存 存在某个时间段服务不可…

Armv8-M的TrustZone技术之测试目标指令

为了允许软件确定内存位置的安全属性,使用了TT指令(Test Target)。 Test Target(TT)查询内存位置的安全状态和访问权限。 Test Target Unprivileged(TTT)查询内存位置的安全状态和访问权限,以进行对该位置的非特权访问。 Test Target Alternate Domain(TTA)和Test…

血细胞分类项目

血细胞分类项目 数据集&#xff1a;血细胞分类数据集数据处理 dataset.py网络 net.py训练 train.py拿训练集的几张图进行预测 数据集&#xff1a;血细胞分类数据集 https://aistudio.baidu.com/datasetdetail/10278 数据处理 dataset.py from torchvision import transfor…

Mysql使用命令行备份数据

目录 前言1. 基本知识2. 常用参数3. 拓展 前言 由于长期使用测试环境的数据库&#xff0c;时不时会有脏数据删除不干净&#xff0c;对此很需要一个实时将生产库的数据定期备份一份&#xff0c;防止生产库中会有脏数据进来。 1. 基本知识 mysqldump 是MySQL数据库管理系统提供…

HTML+CSS:3D卡片组件

效果演示 实现了一个名为“卡片”的效果&#xff0c;当鼠标悬停在一个特定的元素上时&#xff0c;该元素会变得更亮&#xff0c;并且会在其他元素上方显示一个卡片。当鼠标悬停在卡片上时&#xff0c;卡片会变得更亮&#xff0c;并且会在其他元素上方显示一个提示信息。这个效果…

开源:基于Vue3.3 + TS + Vant4 + Vite5 + Pinia + ViewPort适配..搭建的H5移动端开发模板

vue3.3-Mobile-template 基于Vue3.3 TS Vant4 Vite5 Pinia ViewPort适配 Sass Axios封装 vconsole调试工具&#xff0c;搭建的H5移动端开发模板&#xff0c;开箱即用的。 环境要求&#xff1a; Node:16.20.1 pnpm:8.14.0 必须装上安装pnpm&#xff0c;没装的看这篇…

力扣(leetcode)第118题杨辉三角(Python)

118.杨辉三角 题目链接&#xff1a;118.杨辉三角 给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]] …

c++入门语法—————引用,内联函数,auto关键字,基于范围的for循环,nullptr

文章目录 一.引用1.引例2.注意事项3.应用场景1.做参数&#xff08;a:输出型参数b:内容较大参数&#xff09;2.做返回值&#xff08;a:修改返回值&#xff0c;b:减少拷贝&#xff09; 4.引用和指针的区别 二.内联函数1.为什么有内联函数2.用法和底层3.特性 三.auto关键字1.基础示…

CVE-2024-0352 likeshop v2.5.7文件上传漏洞分析

本次的漏洞研究基于thinkPHP开发开的一款项目..... 漏洞描述 Likeshop是Likeshop开源的一个社交商务策略的完整解决方案&#xff0c;开源免费版基于thinkPHP开发。Likeshop 2.5.7.20210311及之前版本存在代码问题漏洞&#xff0c;该漏洞源于文件server/application/api/contr…

数据库之一 基础概念、安装mysql、sql语句基础

数据库之 基础概念、安装mysql、sql语句基础 【一】存储数据的演变过程&#xff1a; 文件存储&#xff1a; 初始阶段随意存放数据到文件&#xff0c;格式任意。目录规范引入&#xff1a; 软件开发使用目录规范&#xff0c;限制数据位置&#xff0c;建立专门文件夹。本地数据存…

inside 的坑

最近代码里面有一句inside 判断语句&#xff0c;明明条件满足&#xff0c;但是就是判断失败&#xff0c;代码如下&#xff1a; xxx;if(i inside {[7:0]}) begin //i5xxx;end xxx; 翻看sv 手册才发现 inside 后面跟的是range value&#xff0c;必须是从小写到大&#xff0c;也就…