UNPKG

@purevue/runtime-dom

Version:

This package is the **DOM runtime** of [PureVue](https://www.npmjs.com/package/@purevue/vue).

238 lines (231 loc) 7.22 kB
'use strict'; var runtimeCore = require('@purevue/runtime-core'); var shared = require('@purevue/shared'); /** * DOM 平台的节点操作集合 * 提供给 renderer 用于创建、更新和删除真实 DOM 节点 */ /** * 插入节点 * @param child 要插入的子节点 * @param parent 父节点 * @param anchor 参照节点,如果传入则插入到该节点之前,否则追加到末尾 */ function insert(child, parent, anchor = null) { parent.insertBefore(child, anchor); } /** * 删除节点 * @param child 要删除的子节点 */ function remove(child) { const parent = child.parentNode; if (parent) parent.removeChild(child); } /** * 创建元素节点 * @param tag 元素标签名,例如 'div'、'span' * @returns 新创建的元素 */ function createElement(tag) { return document.createElement(tag); } /** * 创建文本节点 * @param text 文本内容 * @returns 新创建的文本节点 */ function createText(text) { return document.createTextNode(text); } /** * 设置文本节点的内容 * @param node 文本节点 * @param text 要设置的文本内容 */ function setText(node, text) { node.nodeValue = text; } /** * 设置元素的文本内容 * @param el 元素节点 * @param text 要设置的文本内容 */ function setElementText(el, text) { el.textContent = text; } /** * 创建空节点 * @param text 要设置的文本内容 */ function createComment(text) { return document.createComment(text); } var nodeOps = /*#__PURE__*/Object.freeze({ __proto__: null, createComment: createComment, createElement: createElement, createText: createText, insert: insert, remove: remove, setElementText: setElementText, setText: setText }); /** * 更新宿主元素的属性/事件/样式等 * * @param el 宿主元素 (DOM 元素) * @param key 属性名 (class/style/onClick/自定义 attr 等) * @param prevValue 旧值,用于对比是否需要更新或移除 * @param nextValue 新值,用于设置到 DOM 上 */ function patchProp(el, key, prevValue, nextValue) { // 1. class if (key === 'class') { /** * class 特殊处理,直接赋值给 el.className * - 如果 newValue 为空则置空字符串 */ el.className = nextValue || ''; } // 2. style else if (key === 'style') { /** * style 是一个对象,需要逐个属性设置 * - 新样式直接覆盖 * - 旧样式有但新样式没有的要清空 */ const style = el.style; if (nextValue) { for (const k in nextValue) { style[k] = nextValue[k]; } } if (prevValue) { for (const k in prevValue) { if (!nextValue || !(k in nextValue)) { style[k] = ''; } } } } // 3. 事件 onXxx else if (/^on[A-Z]/.test(key)) { /** * 事件绑定 (例如 onClick -> click) * - 先移除旧的监听器 * - 再绑定新的监听器 */ const eventName = key.slice(2).toLowerCase(); if (prevValue) { el.removeEventListener(eventName, prevValue); } if (nextValue) { el.addEventListener(eventName, nextValue); } } // 4. 普通属性/attribute else { /** * 普通属性:直接通过 setAttribute 设置 * - 如果 newValue 是 null/false -> 移除属性 * - 否则更新为新值 */ if (nextValue == null || nextValue === false) { el.removeAttribute(key); } else { el.setAttribute(key, nextValue); } } } /** 渲染器 */ const renderer = runtimeCore.createRenderer({ ...nodeOps, patchProp }); /** * 创建应用实例 * @param rootComponent 根组件 * @returns App 实例,包含 mount、use 等方法 * Vue 团队刻意避免用 Class(轻量、便于tree-shaking) */ function createApp(rootComponent) { /** 已注册的插件 */ const installedPlugins = new WeakSet(); /** 上下文,存储全局组件、指令、provide/inject 数据等 */ const context = runtimeCore.createAppContext(); const app = (context.app = { _container: null, _context: context, mount(rootContainer) { // 获取真实 DOM 容器 const container = typeof rootContainer === 'string' ? document.querySelector(rootContainer) : rootContainer; // 1. 创建根 vnode const vnode = runtimeCore.createVNode(rootComponent); // 挂载上下文 vnode.appContext = context; // 2. 调用 renderer 的 render renderer.render(vnode, container); // 3. 保存挂载容器 app._container = container; // 子组件在渲染时可以通过 vnode.appContext 访问全局配置、全局组件和 provide 注入的值 return vnode.component.proxy; }, use(plugin, ...options) { // 1. 防止重复安装 if (installedPlugins.has(plugin)) { console.warn(`Plugin has already been applied to target app.`); } // 2. 对象插件:plugin.install(app, ...options) else if (plugin && shared.isFunction(plugin.install)) { installedPlugins.add(plugin); plugin.install(app, ...options); } // 3. 函数插件:plugin(app, ...options) else if (shared.isFunction(plugin)) { installedPlugins.add(plugin); plugin(app, ...options); } // 4. 不合法的插件 else { console.warn(`A plugin must either be a function or an object with an "install" ` + `function.`); } return app; }, component(name, component) { if (!component) { // 如果没有传 component 参数,返回已注册组件 return context.components[name]; } // 如果组件已经注册,提示警告 if (context.components[name]) { console.warn(`Component "${name}" has already been registered in target app.`); } // 注册组件 context.components[name] = component; // 支持链式调用 return app; }, // TODO unmount() { }, directive() { return app; }, provide(key, value) { context.provides[key] = value; return app; }, }); return app; } Object.defineProperty(exports, "toDisplayString", { enumerable: true, get: function () { return shared.toDisplayString; } }); exports.createApp = createApp; Object.keys(runtimeCore).forEach(function (k) { if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, { enumerable: true, get: function () { return runtimeCore[k]; } }); }); //# sourceMappingURL=index.cjs.js.map