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. 值还是个函数,就循环计算出最终的值再设置

相关推荐
前端市界12 分钟前
当20个并发请求拖垮你的应用:从TCP握手到HTTP/2的终极排错指南
前端
量子-Alex13 分钟前
【大模型与智能体论文】REACT:协同语言模型中的推理与行动
前端·react.js·语言模型
冷崖31 分钟前
QML-动画
前端
太过平凡的小蚂蚁32 分钟前
适配器模式:让不兼容的接口协同工作
java·前端·javascript
锈儿海老师42 分钟前
超越平台:Vercel 的野心是定义编程语言的未来吗?
前端·javascript·架构
泷羽Sec-静安2 小时前
Less-7 GET-Dump into outfile-String
android·前端·网络·sql·安全·web安全
IT_陈寒2 小时前
从2秒到200ms:我是如何用JavaScript优化页面加载速度的🚀
前端·人工智能·后端
天天向上10242 小时前
vue 网站导航栏
前端·javascript·vue.js
云外天ノ☼3 小时前
一、Node.js入门实战指南:从零搭建你的第一个后端
前端·javascript·笔记·node.js
未来之窗软件服务3 小时前
未来之窗昭和仙君(四十八)开发商品进销存修仙版——东方仙盟筑基期
前端·仙盟创梦ide·东方仙盟·昭和仙君·东方仙盟架构