SolidJs nodiff原理浅析

基本原理

对jsx的理解

jsx是一种声明语句,表示dom结构是怎样的。

虚拟dom的方式中,jsx最终转换成json对象,框架根据json对象patch对比,生成dom,而patch的过程会监听到响应式数据(vue),当数据变化,patch流程重新执行,实现响应式渲染。

solidjs编译时将jsx转换成创建dom的js代码,jsx静态部分直接创建dom,动态部分转换为插入删除等语句。而这些语句使用响应式系统的effect监听响应式数据,从而在数据变化时,重新执行,实现响应式。

例子

scss 复制代码
const App = () => {
  const [state, setState] = createSignal(0);

  const interval = setInterval(() =>
    setState(prv=>prv+1)
  , 1000);

  onCleanup(() => clearInterval(interval));

  return <div>{state()}</div>;
}

转换为
const root = document.getElementById('root');
const App = () => {
  const [state, setState] = createSignal(0);
  const interval = setInterval(() => setState(prv => prv + 1), 1000);
  onCleanup(() => clearInterval(interval));
  return (() => {
    const _el$ = _tmpl$();
    insert(_el$, state);
    return _el$;
  })();
};

上例中jsx转换成创建模板,绑定状态,返回dom三步。

insert函数包含了向dom设置状态的各种case的实现。如果状态是恒定的,直接调用实际的插入操作即可,如果是动态变化的,需要在外层用effect包一层,实现响应式。代码如下

sql 复制代码
function insert(parent, accessor, marker, initial) {
  if (marker !== undefined && !initial) initial = [];
  if (typeof accessor !== "function") return insertExpression(parent, accessor, initial, marker);
  createRenderEffect(current => insertExpression(parent, accessor(), current, marker), initial);
}

实际的insert操作发生在insertExpression中

ini 复制代码
function insertExpression(parent, value, current, marker, unwrapArray) {
  while (typeof current === "function") current = current();
  if (value === current) return current;
  const t = typeof value,
    multi = marker !== undefined;
  parent = multi && current[0] && current[0].parentNode || parent;
  if (t === "string" || t === "number") {
    if (t === "number") value = value.toString();
    if (multi) {
      let node = current[0];
      if (node && node.nodeType === 3) {
        node.data = value;
      } else node = document.createTextNode(value);
      current = cleanChildren(parent, current, marker, node);
    } else {
      if (current !== "" && typeof current === "string") {
        current = parent.firstChild.data = value;
      } else current = parent.textContent = value;
    }
  } else if (value == null || t === "boolean") {
    current = cleanChildren(parent, current, marker);
  } else if (t === "function") {
    createRenderEffect(() => {
      let v = value();
      while (typeof v === "function") v = v();
      current = insertExpression(parent, v, current, marker);
    });
    return () => current;
  } else if (Array.isArray(value)) {
    const array = [];
    const currentArray = current && Array.isArray(current);
    if (normalizeIncomingArray(array, value, current, unwrapArray)) {
      createRenderEffect(() => current = insertExpression(parent, array, current, marker, true));
      return () => current;
    }
    if (array.length === 0) {
      current = cleanChildren(parent, current, marker);
      if (multi) return current;
    } else if (currentArray) {
      if (current.length === 0) {
        appendNodes(parent, array, marker);
      } else reconcileArrays(parent, current, array);
    } else {
      current && cleanChildren(parent);
      appendNodes(parent, array);
    }
    current = array;
  } else if (value.nodeType) {
    if (Array.isArray(current)) {
      if (multi) return current = cleanChildren(parent, current, marker, value);
      cleanChildren(parent, current, null, value);
    } else if (current == null || current === "" || !parent.firstChild) {
      parent.appendChild(value);
    } else parent.replaceChild(value, parent.firstChild);
    current = value;
  } else console.warn(`Unrecognized value. Skipped inserting`, value);
  return current;
}

insertExpression实现的是将dom插入到父元素中。将根组件加到root中,使用这个函数,有两种case,只有一个节点和多个节点,对应代码里两个分支。将动态值设给dom,有三种case:1. 值是字符串/数字,设置textContent 2. 值是布尔或空,将元素清除 3. 值还是个函数,就循环计算出最终的值再设置

相关推荐
10年前端老司机31 分钟前
React无限级菜单:一个项目带你突破技术瓶颈
前端·javascript·react.js
阿芯爱编程5 小时前
2025前端面试题
前端·面试
前端小趴菜056 小时前
React - createPortal
前端·vue.js·react.js
晓13136 小时前
JavaScript加强篇——第四章 日期对象与DOM节点(基础)
开发语言·前端·javascript
菜包eo7 小时前
如何设置直播间的观看门槛,让直播间安全有效地运行?
前端·安全·音视频
烛阴7 小时前
JavaScript函数参数完全指南:从基础到高级技巧,一网打尽!
前端·javascript
chao_7898 小时前
frame 与新窗口切换操作【selenium 】
前端·javascript·css·selenium·测试工具·自动化·html
天蓝色的鱼鱼8 小时前
从零实现浏览器摄像头控制与视频录制:基于原生 JavaScript 的完整指南
前端·javascript
三原9 小时前
7000块帮朋友做了2个小程序加一个后台管理系统,值不值?
前端·vue.js·微信小程序
popoxf9 小时前
在新版本的微信开发者工具中使用npm包
前端·npm·node.js