nextTick在官网定义是:当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。而具体原理到底是什么呢,本章来浅析一下
nextTick在官网定义是:当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。而具体原理到底是什么呢,本章来浅析一下
事情的背景是同事在分享flex布局时聊到了一个最小内容尺寸导致子项未均分的场景,恰好自己对这方面没有过多的去深入了解。所以刚好可以借着这个case来探究一下flex布局计算的那些原理。
# prettier ## 前言 > prettier是一个专注于**所有代码格式**的模块 ## npm包 通过 `npm i prettier -g` 全局安装,日常项目中都会带有prettier依赖,所以可以凭自己意愿全不全局安装,全局安装可以在命令行中直接使用prettier,否则需要通过`npx prettier `的方式来运行prettier > 他与eslint不同的点在于,他并不强制要求存在配置文件,会有一套默认的配置 当然尽管它自己不强制要求配置文件,但在我们日常的开发中,往往都会自定义规则,所以我们应该提供一个适用于当前项目的规则配置 ```json //prettier配置文件比较简洁,专注于代码格式,只需要配置规则 module.exports = { trailingComma: 'es5', tabWidth: 4, semi: false, singleQuote: true, }; ``` 我们同样在配置完规则之后提供一个测试文件 ```js //prettier_test.js var a = 1; const test_2 = "test"; console.log('clg'); ``` 然后我们通过`npx prettier --write ./prettier_test.js`来进行格式检查和修正,下面是经过修正之后的结果 ```js var a = 1; const test_2 = "test"; console.log("clg"); ``` ## vscode插件 与eslint的vscode插件原理一致,prettier的vscode插件只是为了方便我们自己,如果我们想在写代码的时候,一Ctrl+S保存就能自动格式化代码,而不需要去额外执行`prettier --write ./prettier_test.js`,这时候就需要插件来帮助我们了 首先我们在应用商店中搜索同名插件`prettier`并进行下载
CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行。它的实现和执行全部由浏览器完成,开发者只需提供配置。
条件类型下的类型提取,infer用于对条件类型下的一部分类型进行提取,可分为对于数组类型的提取、对于字符串类型的提取、对于函数类型的提取、对于索引类型的提取四部分
从0到1实现一个小型comlink(仿comlink),其中记录了2个主要问题。1.为什么在Proxy中返回一个fn,需要bind?2.为什么Await一个Proxy会产生对then的一次拦截?
ArrayBuffer表示一种通用的、固定长度的原始二进制数据缓冲区,一般用于充当一个构造函数,通常我们使用他的视图:TypeArray和DataView。
ServiceWorker是一个运行于浏览器后台的独立线程,通过它我们可以在网络连接不可用时(脱机)提供离线体验、推送通知以及拦截处理来自网页的网络请求等功能。同时它也是PWA(渐进式Web应用)的关键技术之一。
# next.js入门之路-1 ## 从创建一个next.js项目开始 > 关于node环境,笔者在最初开发时,next.js处于v13版本,还可以使用node16来进行开发,在开发途中,官网发布了v14版本,发现只是一些性能上的优化,所以毫不犹豫便直接升级到了v14,注意v14版本要求node18及以上,所以目前开发环境为**nodev18** 使用`create-next-app`来创建项目 ```sh pnpm create next-app ``` 通过`pnpm run dev`来启动我们的项目 通过`pnpm run build`来打包我们的项目 > next.js专门提供可以让我们在本地来查看生产环境的运行情况,在打包完成之后通过`pnpm run start`来启动生产环境下的next.js应用程序 ## 关于静态资源 > `./public/` 被映射到 `/` ## 关于app与~~pages~~ > 本系列暂不记录`pages`模式的相关文件系统 **app模式下,以文件的目录作为route的规则** ### 关于Layout与Template #### layout与page `layout`会在page跳转的时候保持本身的状态,但是这个也分情况 如果跳转之后存在根布局`root layout`,从一个page跳转到另一个page,会导致整个页面加载 如果page的页面只存在“普通”的layout,那么这个layout会是根布局的`children Node` > 关于根布局:根布局是根目录中最顶层的**布局** > > 1.存在着这两个HTML标签`<html><body>` > > 2.app目录下,必须存在着一个根布局:`app/layout.*sx` ##### 测试一下layout与page的层级以及`root layout 与 normal layout` 的区别 ```tsx // app/layout.tsx export default function RootLayout({ children }: { children: React.ReactNode }) { console.log('我是根路由,我被执行了'); return ( <html lang='en'> <body> 我是根布局{children}//注意这里,后面要说到 </body> </html> ); } ``` ```tsx // app/page.tsx import MainLayout from './components/MainLayout'; export default function Home() { console.log('我是app下的page,我被执行了'); return ( <div style={{ backgroundColor: '#ff00ff' }}> 我是app下的page <MainLayout /> </div> ); } ``` ```tsx // app/components/MainLayout export default function HomeLayout() { console.log('我是app下page的子组件,我被执行了'); return <div style={{ height: '200px', width: '200px', backgroundColor: '#ff0f0f' }}>我是homeLayout</div>; } ``` 为了再测试一下根布局与”普通“布局的区别,我们增加一个test route ```tsx // app/test/layout.tsx export default async function RootLayout({ children }: { children: React.ReactNode }) { console.log('我是test的(根
# next.js入门之路-2-loading next.js提供了两种方式在加载路由对应内容的时候显示加载UI,加载UI会在渲染完成之后,自动被交换为路由所渲染的内容 ## 即时加载状态 通过创建与`layout.tsx`同级的`loading.tsx`文件来达到实现`Loading`效果 > 会受到层级影响,子项会被包含在父项中 > 和`layout.tsx`一样,只需要提供就行,next.js自己会检测到 ```tsx // ./loading.tsx export default function Loading() { // You can add any UI inside Loading, including a Skeleton. return <>loading</> } ``` ## 流媒体加载 将页面分解成许多的块,这些块只要自身加载完成就会被转换为真实内容,不用等待其他块中的内容加载完成 例如 ```tsx <div className={style.base_layout}> <header> <Suspense fallback={<Loading></Loading>}> <Header /> </Suspense> </header> <div className={style.base_layout_main}> <section> <main> <Suspense fallback={<Loading></Loading>}> <MainField /> </Suspense> </main> <aside> <Suspense fallback={<Loading></Loading>}> <AsideBar /> </Suspense> </aside> </section> </div> </div> ``` 这样一个布局,这样`header`、`main`、`aside`的loadingUI不会等到都加载完才替换,而是自己本身的内容加载完之后就开始替换 > 两种方式可以共存,一个是page级别的,一个是component级别 ## 小白的加载方式 这种不是官网的,这是我自己在没看文档之前的实现方式,大致思路是通过给定一个`div`并将`z-index`设置为足够大,给定相对定位,这个div的子项可以是一些`loadingUI`,以此来遮住实际的内容,并用一个状态来控制其是否展示,**我们知道当我们使用`use client`的时候,服务端依旧会执行一遍**,拿到一些可以在服务端就生成的部分,一些hooks会以他的初始值作为服务端执行时的值,比如useState,当然他不会执行useEffect这样在dom挂载完毕之后才执行的部分,这个是next.js对react的一些hooks的一些处理,所以我们完全可以设置缺省值为false,并在useEffect中将他变为true,这样也可以做到一种加载Loading,当然在看了官网文档之后,这种方式毫无悬念的被我舍弃了,具体原因是部署到服务器上之后实际体验并不是很好
# next入门之路-4-route > next.js中通过名字来约定路由,具体表现就是文件目录 ## 静态路由 ### 路由定义 + `app/` -> `/` + `app/xxx` -> `/xxx` + `app/x/y/z` -> `/x/y/z` ### 路由跳转 + Link ```tsx <Link href={`/x/y/z`} > 查看更多... </Link> // 跳到/x/y/z页面 ``` + useRouter(Hook) ```tsx 'use client'//需要在客户端组件下才能使用 import { useRouter } from 'next/navigation'; const Component =()=>{ const router = useRouter(); //router.replace('/'); //router.push('/'); return <div></div> } ``` ## 动态路由 ### 路由定义 用[]表示动态的部分,例如不同的id app/blog/[slug]/page.tsx -> app/blog -> x app/blog/[slug]/page.tsx -> app/blog/2 -> {slug:2} app/blog/[slug]/page.tsx -> app/blog/3 -> {slug:3} app/blog/[…slug]/page.tsx -> app/blog/3/4/5 -> {slug:['3','4','5']} ### 可选段(可以匹配到不带参数的路由) app/blog/[[…slug]]/page.tsx -> app/blog -> {} app/blog/[[…slug]]/page.tsx -> app/blog/2 -> {slug:2} app/blog/[[…slug]]/page.tsx -> app/blog/3 -> {slug:3} app/blog/[[…slug]]/page.tsx -> app/blog/3/4/5 -> {slug:['3','4','5']} ### 通过params参数获取值 ```tsx //layout.tsx or page.tsx or route.ts or other
# npx ## 调用项目内部安装的模块 npx主要的使用就在于去调用一些非全局安装的模块,也即是项目内部安装的模块,例如我们想在项目中使用eslint来进行代码规范检查 我们知道,如果我们通过`npm i eslint -g`全局安装eslint,那么就可以在命令行中直接使用`eslint ./index.js`来检查`index.js`文件 而如果我们使用局部安装`npm i eslint`那么我们无法在命令行中直接调用`eslint ./index.js`,只能通过**项目脚本**和package.js的**script字段**来调用 ### script字段方式调用 这里有一个react初始化项目的package的配置文件 ```json "scripts": { "dev": "vite", "build": "tsc && vite build", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview" }, ``` 我们可以通过执行`npm run lint`来调用eslint
# canvas-context该模块用于适配html-canvas、node、offscreen-canvas 三种环境下的canvas context处理
three.js基于r142的渲染流程从创建一个WebGLRenderer开始,WebGLRenderer在创建的时候会通过一些配置参数去创建一个webgl上下文环境,然后进行渲染所需相关的初始化,包括webgl扩展、绘制状态、内存记录、纹理、灯光、几何相关等等管理对象的初始化
# lecture-4 ## 正交矩阵 正交矩阵比较特殊,当且仅当其与其转置矩阵的乘积是单位矩阵时,矩阵是正交的 $$ M是正交矩阵 <=> MM^{T} = I $$ 注意到矩阵M(线性无关)与其逆矩阵$M^{T}$的乘积也是单位矩阵,所以可以有如下结论 $$ M是正交矩阵 <=> M^{T} = M^{-1} $$ > 这是一个非常重要的结论,我们知道求一个矩阵的逆是需要大量的计算的,而如果判断出矩阵是正交的,那么可以直接利用转置矩阵充当逆矩阵完成计算,大大减少了计算耗能 > 旋转矩阵、反射矩阵都是正交矩阵! > 如果我们确定一个矩阵仅包含旋转和反射,那么我们也可以推断出他是正交的 ### 通过直接计算判断矩阵是否正交 $$ M * M^{T} = I \\ \begin{vmatrix} m_{11} & m_{12} & m_{13} \\ m_{21} & m_{22} & m_{23} \\ m_{31} & m_{32} & m_{33} \\ \end{vmatrix} * \begin{vmatrix} m_{11} & m_{21} & m_{31} \\ m_{12} & m_{22} & m_{32} \\ m_{13} & m_{23} & m_{33} \\ \end{vmatrix} = \begin{vmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \\ \end{vmatrix}\\ 即\\ m_{11}m_{11} + m_{12}m_{12} + m_{13}m_{13} = 1,\\ m_{11}m_{21} + m_{12}m_{22} + m_{13}m_{23} = 0,\\ m_{11}m_{31} + m_{12}m_{32} + m_{13}m_{33} = 0,\\ m_{21}m_{11} + m_{22}m_{12} + m_{23}m_{13} = 0,\\ m_{21}m_{21} + m_{22}m_{22} + m_{23}m_{23} = 1,\\ m_{21}m_{31} + m_{22}m_{32} + m_{23}m_{33} = 0,\\ m_{31}m_{11} + m_{32}m_{12} + m_{33}m_{13} = 0,\\ m_{31}m_{21} + m_{32}m_{22} + m_{33}m_{23} = 0,\\ m_{31}m_{31} + m_{32}m_{32} + m_{33}m_{33} = 1,\\ 我们可以将它换一种形式展示出来\\ 设r_1、r_2、r_3均代表M的行:\\ r_1 = \begin{vmatrix}m_{11} & m_{12} & m_{13} \end{vmatrix}、r_2 = \begin{vmatrix}m_{21} & m_{22} & m_{23} \end{vmatrix}、r_3 = \begin{vmatrix}m_{31} & m_{32} & m_{33} \end{vmatrix}\\ 则M = \begin{vmatrix} -r_1- \\ -r_2- \\ -r_3- \\ \end{vmatrix}\\ 则上式可以改写为\\ r_1*r_1 = 1 , r_1*r_2 = 0 , r_1*r_3 = 0\\ r_2*r_1 = 0 , r_2*r_2 = 1 , r_2*r_3 = 0\\ r_3*r_1 = 0 , r_2*r_2 = 0 , r_3*r_3 = 1\\ $$ 同上面最终的变换我们可以知道 + 由于当且仅当矢量是单位矢量时,矢量自己本省的点积才为1,所以可以知道只有当**$r_1、r_2、r_3$都为单位矩阵**时,上述3个结果为1的式子才成立 + 由于当且仅当两个矢量相互垂直时,矢量点积才为0,所以可以知道只有当$r_1、r_2、r_3$都为相互垂直时,上诉6个结果为0的式子才成立 + 如果以列划分也是相同的结果 + 我们知道矢量相乘满足交换律,所以其实点乘为0的式子实际满足的只有3个约束,也即是互相垂直 ## 正交化计算 ### Gram-Schmidt正交化 以3x3为例,$ r_1、r_2、r_3 $表示3x3矩阵M的行,然后根据Gram-Schmidt算法计算一组正交的行矢量 $$ r_1^{'} <= r_1\\ r_2^{'} <= r_2 - \frac{r_2 * r_1^{'}}{r_1^{'} * r_1^{'}} * r_1^{'} \\ r_3^{'} <= r_3 - \frac{r_3 * r_1^{'}}{r_1^{'} * r_1^{'}} * r_1^{'} -\frac{r_3 * r_2^{'}}{r_2^{'} * r_2^{'}} * r_2^{'} \\ $$ 对于每个基矢量,我们的想法是按顺序遍历每个基矢量,并减去与基向量平行的向量,我们希望矢量的三个方向能够变为我们正交基的三个方向, + 对于$ r_1 $我们可以将$r_1$为直接与$r_1^{'}$对其,这样便有一个方向重合了 + 对于$r_2$,由于两个不同方向的矢量才能确定一个平面,那么我们可以让$r_1、r_2$与标准正交基的$r_1^{'}和r_2^{'}$构成的平面对其,这样对于$r_2 $来说我们只需要减去$r_2 $中$r_1 $方向的分量即可得到第二个正交矢量,并且可以知道减去之后$r_2是垂直于r_1$的 + 对于r3,由于我们对$r_2 $的操作,现在$r_3$需要减去$r_2^{'} 以及 r_1^{'}$方向上的分量 > 对于三维来说 有一种更简洁的计算$r_3 $的方法,利用外积来计算$r_3$ <=> $r_1$ x $r_2$ 如此便得到了一组正交基矢量,但是该基矢量不一定是单位矢量,同时Gram-Schmidt算法是有偏差的,这取决于列出的基矢量的顺序,例如$r_1$永远不会改变
# 关乎match、matchAll > g表示全局搜索,如果定义正则表达式时没有g修饰,则只返回首个匹结果,否则返回所有匹配的结果 ## match `match`返回值是一个数组,如果没有任何匹配项则返回`null` ```js const str = 'ababdcachuaaiujde'; const pattern = /(\w)b/g; const pattern2 = /(\w)b/; console.log(str.match(pattern));//[ 'cb', 'ab' ] console.log(str.match(pattern2));//[ 'cb', 'c', index: 1, input: 'acbabdcachuaaiujde', groups: undefined ] ``` > 对于`match`来说,如果正则表达式中有g修饰,结果返回所有与正则表达式匹配的字符串的列表
# 在linux上安装nginx ## 前言 linux系统基本上分两大类: + RedHat系列:Redhat、Centos、Fedora等 + 常见的安装包格式 rpm 包,安装rpm包的命令是 “rpm -参数” + 包管理工具 yum + 支持tar包 + Debian系列:Debian、Ubuntu等 + 常见的安装包格式 deb 包,安装deb包的命令是 “dpkg -参数” + 包管理工具 apt-get + 支持tar包 这里以Ubuntu为例,即使用apt-get ## 安装 ### 使用 `apt-get` 来安装nginx ```sh apt-get install nginx # 安装nginx nginx -v # 查看nginx版本 dpkg -L nginx #查看 Nginx 被安装到了什么地方,有哪些相关目录 ``` > `/etc/nginx/conf.d/` 文件夹,是我们进行子配置的配置项存放处,`/etc/nginx/nginx.conf` 主配置文件会默认把这个文件夹中所有子配置项都引入; > `/usr/share/nginx/html/` 文件夹,通常静态文件都放在这个文件夹,也可以根据自己的习惯放其他地方; ### 使用yum安装nginx ```sh rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm #默认情况Centos7中无Nginx的源,最近发现Nginx官网提供了Centos的源地址
# 反向代理 & 跨域配置 由于跨域机制,在我们请求一些接口时需要去配置代理来帮助我们完成请求,在本地开发可以开启proxy,而上线之后,则需要通过`Nginx`来进行代理 ```nginx # 一个简单的反向代理示例,访问本机localhost跳转到百度 server{ listen 80; server_name localhost; location / { proxy_pass http://www.baidu.com; } } ``` 除了proxy_pass之外,还存在一些额外的指令,比如: + 改变头信息的`proxy_set_header`,他将在客户端请求发送给后端服务器之前,更改来自客户端的请求头信息
# location `localtion`是nginx中的块级指令,`location`指令的功能是用来匹配不同的uri请求,进而对请求做不同的处理和响应 server 块可以包含多个 location 块,location 指令用于匹配 uri,语法: ```nginx location [ =
# dockerFile 指令格式有 2 种: - 注释和指令注释以#开头,后面跟上信息 - 以大写的指令名开头 后面跟上参数 ## 变量 > 变量用`$variable_name`或者`${variable_name}`表示 > > - `${variable:-word}`表示如果`variable`设置,则结果将是该值
# HOOK ## Hook链表 无论是初次挂载还是更新的时候,每调用一次hook函数,便会生成一个hook对象,产生的hook对象有序依次地存入一条hook链中,由fiber.memoizedState保存,另外还有着一个指针**`workInProgressHook`**去指向当前需要挂载或者更新的hook对象,这样就可以得到hook的调用情况,每调用一次hook函数,就将这个指针的指向移到该hook函数产生的hook对象上
# Diff ## 前言 当我们state保存的状态发生改变时,会促使render执行,去得到持有最新状态的ReactElement,但是得到的ReactElement之中若只有少量需要更新,那么显然不能全部去更新它们,此时就需要有一个diff过程来决定哪些节点是真正需要更新的