@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
JavaScript
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
;