Skip to main content

7 posts tagged with "javascript"

View All Tags

深度思考扩展

· 3 min read
marvin-season
Maintainer of Docusaurus

S

DeepSeek 的爆火带来的深度思考模式,需要在已有流式对话中实现【前端】

T

在原有的对话基础上加入深度思考的交互,并支持不同的思考风格

A

解析深度思考内容,开发新的深度思考组件,尽可能不改变原有组件实现深度思考的功能扩展,=> HOC正是这一思想

R

扩展原有组件,实现深度思考功能

Concrete

原有组件

function Content({content}) {
return <div>{content}</div>
}

目标组件

错误的方式:直接修改原组件

function ContentWithThink({content, think}) {
return <div>
<div>{think}</div>
<div>{content}</div>
</div>
}

推荐的方式:HOC


const withThink = <P extends object>(
Component: ComponentType<P>,
ThinkComponent: FunctionComponent<ThinkContentStyleProps>) =>
{
return (props: P & { content: string }) => {
const {
content,
think_content,
closedMatch,
openMatch
} = parseThinkContent(props.content)

return (
<>
<Think
closedMatch={!!closedMatch}
openMatch={!!openMatch}
think_content={think_content}
ThinkComponent={ThinkComponent} />
<Component {...props} content={content} />
</>
)
}
}

export const ContentWithThink = memo(withThink(Content, ThinkContentStyle), (prev, next) => {
return prev.content === next.content
})

withThink是一个HOC组件,用于扩展传入的Content组件,其中ThinkContentStyle为配置思考组件的风格提供了入口, HOC中的Think组件则定一个思考组件的逻辑以及布局

const Think = ({ closedMatch, openMatch, think_content, ThinkComponent }: ThinkProps) => {
const [status, setStatus] = useState<ThinkStatus>(ThinkStatus.completed)

const match = useMemo(() => {
return openMatch || closedMatch
}, [openMatch, closedMatch])

useEffect(() => {
if (openMatch) {
setStatus(ThinkStatus.thinking)
}
}, [openMatch])

useEffect(() => {
if (closedMatch) {
setStatus(ThinkStatus.completed)
}
}, [closedMatch])

useEffect(() => {
EE.on(ThinkEvent, ({ thinkStatus }: { thinkStatus: ThinkStatus; }) => {
// 完整匹配到了
if (closedMatch) {
setStatus(ThinkStatus.completed)
return
}
setStatus(thinkStatus)
})
return () => {
EE.off(ThinkEvent)
}
}, [status])

return <>{match && <ThinkComponent think_content={think_content} status={status} />}</>
}

How To Use

直接替换原来的组件为高阶组件

() => <ContentWithThink content={content} className={className}/>

Recap

  • 使用高阶组件扩展了业务功能,尽可能的没有操作原有的代码
  • 将新功能全部聚合在HOC中

LazyLoad

· One min read
marvin-season
Maintainer of Docusaurus

参考资料

https://developer.mozilla.org/en-US/docs/Web/Performance/Guides/Lazy_loading

Intersection Observer API

  • row: 10000
  • startIndex: 7
  • end: 17
  • acceleration: 4
  • buffer: 1
  • rows: [{"id":6},{"id":7},{"id":8},{"id":9},{"id":10},{"id":11},{"id":12},{"id":13},{"id":14},{"id":15},{"id":16},{"id":17}]
6
7
8
9
10
11
12
13
14
15
16
17

How

借助 IntersectionObserver API, 监听将要进入视口内的dom,当该 dom 出现在视口中时,加载更多!

Pseudo

const observer = new IntersectionObserver((entries, observer) => {
if('discover'){
// load more
}
})

observer.observe('dom' as HTMLElement)

Images and iframes

示例图片

以图片为例,打开控制台,筛选网络中的图片,然后滚动上述视口,当图片靠近视口区域时,可以看到图片资源加载

loading="lazy"

<img loading="lazy" src="image.jpg" alt="..." />
<iframe loading="lazy" src="video-player.html" title="..."></iframe>

Comprehension of JavaScript

· 6 min read
marvin-season
Maintainer of Docusaurus

As we all know, JavaScript is a single-threaded language. This means that only one task can be executed at a time. So, if you have a long-running task, it will block the execution of other tasks.

Actually, JavaScript is a single-threaded language, but it has a non-blocking I/O model. This means that JavaScript can perform multiple tasks at the same time. How does JavaScript achieve this? The answer is Event Loop.

流数据渲染优化

· 3 min read
marvin-season
Maintainer of Docusaurus

切入点

如何对流失数据优化,提高渲染性能。从以下两点切入:

懒加载渲染: 可视区外部的信息不用渲染,当滚动到可视区时再渲染。 注意,此时需要一个buff来缓冲渲染到页面的信息,防止给用户造成流暂停的假象

增量式渲染: 已经渲染到页面上的数据不用重复渲染,只渲染新增的数据。

AI Chat

· 4 min read
marvin-season
Maintainer of Docusaurus

概要

核心数据结构: 消息列表 (messages)

核心操作:调用模型接口,生成消息

次要操作:复制、引用、重新生成、编辑、参考附件选择

UI:聚合上操作以及数据

class CoreUI {
useMessage(){
// connect to message
return this
}
useHandle(){
// connect to handle
return this
}
useChat(){
// connect to AI
return this
}
render(){
// render ui
return <></>
}
}
new CoreUI().useMessage().useHandle().useChat().render();

Concept

S-S-E

Server-Send-Event: data is flowing from server to client format: tag:string for example:

data: "{ name: 'marvin', age: 20 }"

在SSE中 数据以固定的格式传输到客户端,在使用之前客户端或许需要先进行解析。

React Think

· 2 min read
marvin-season
Maintainer of Docusaurus

Web开发的本质就是将 数据 呈现到页面,平滑的交互带来好的用户体验(User Perceived Performance),性能优化也此为主。

原生JS

如果不使用 React,我们可以使用原生JS来实现页面的交互,比如下面的例子:

const addbtn = document.getElementById("add");
const counter = document.getElementById("counter");

addbtn.onclick = function() {
// some business logic
num.innerText = parseInt(counter.innerText) + 1;
};

可以看到 数据UI有很强的粘性,业务代码不够清晰,维护性差。且频繁的操作DOM会导致性能问题。

如何优化?

不直接操作DOM,而是在DOM和数据之间加一层,当我们需要更新数据时去通知这一层,至于更新优化的逻辑,全部集中在这一层。

React就扮演了这样的角色。

React如何工作

https://github.com/acdlite/react-fiber-architecture

https://jser.pro/ddir/rie?reactVersion=18.3.1&snippetKey=hq8jm2ylzb9u8eh468

以数据为核心,驱动视图更新 React架构如下: Scheduler -> Reconciler -> Renderer

const Counter = () => {
const [counter, setCounter] = useState(0);
return <>
<span>{counter}</span>
<button onClick={() => setCounter(prev => prev + 1)}>add</button>
</>
}

调用setCounter触发更新任务,Scheduler依据任务的优先级选择任务,将任务交给 Reconciler, Reconciler 负责找出变化的部份,将变化的部份交给Render,Render负责将变化的部份渲染到页面上。 Reconciler的工作是最复杂的,初次渲染时,Reconciler会生成一颗Fiber树,当更新时Reconciler 利用双缓存机制克隆原来的树,将更新后的节点更新到新树上,最后将新树替换原来的树。