先让我来解释一下什么是前端框架。所谓的前端框架,就是一种能够让我们避免去写常规的HTML和JavaScript代码
html
<p id="cool-para"></p>
<script>
const coolPara = 'Test';
const el = document.getElementById('cool-para');
el.innerText = coolPara;
</script>
而是让我们能够编写出像这样简约的HTML和JavaScript代码(Vue):
vue
<script setup>
const coolPara = 'Test';
</script>
<template>
<p>{{ coolPara }}</p>
</template>
或者是下面这个(react)
react
export default function Para() {
const coolPara = 'Lorem ipsum';
return <p>{ coolPara }</p>;
}
这样一个框架的优点是显而易见的。记住诸如document、innerText和getElementById等单词或短语是很困难的。当然看玩笑的,这个不是主要的原因。
特点
Reactivity ✨
第一个主要 原因是,在第二个和第三个例子中, 我们可以只设置或更新变量的值,标记(即元素)就会更新,而无需显式设置其 .coolPara``<p>``innerText
这称为反应性,UI 与这样的数据相关联 仅更改数据即可更新 UI 的方式。
Composability ✨
第二个主要 原因是能够定义组件和 重用它,而不必在每次我们需要使用它时重新定义它。这 称为可组合性。
常规HTML + JavaScript默认没有这个。所以 以下代码不会执行应有的操作:
html
<!-- Defining the component -->
<component name="cool-para">
<p>
<content />
</p>
</component>
<!-- Using the component -->
<cool-para>Lorem ipsum.</cool-para>
反应性和可组合性是Vue、React等常见前端框架给我们的两个主要优势。
这些抽象不是免费提供的,我们必须预先加载一堆框架特定的概念,处理它们在以难以解释的神奇方式工作时出现的缺陷,更不用说,还有一大堆容易出错的依赖项。
但是,事实证明,使用现代Web API来实现这两点并不难。而且,对于大多数用例,我们可能实际上并不需要使用通常的框架以及它们的复杂性的混乱。
介绍
Reactivity
解释反应性的简单语句是,当数据更新时,自动更新 UI。
第一部分是知道数据何时更新 。这 不幸的是,这不是普通对象 可以做的事情。我们不能 只需附加一个调用以侦听数据的侦听器 更新事件。ondataupdate
幸运的是,JavaScript 正好允许我们做到这一点, 它被称为代理
。
Proxy对象
Proxy
允许我们从常规 对象创建代理对象:
js
const user = { name: 'Lin' };
const proxy = new Proxy(user, {});
然后,此代理对象可以侦听对数据的更改。
在上面的例子中,我们有一个代理对象 ,但它并不是真的 当知道已经改变时,做任何事情。name
为此,我们需要一个处理程序 ,它是一个对象,它告诉代理对象在数据更新时要做什么。
js
const handler = {
set(user, value, property) {
console.log(`${property} is being updated`);
return Reflect.set(user, value, property);
},
};
//用handler创建一个代理
const user = { name: 'Lin' };
const proxy = new Proxy(user, handler);
现在,每当我们使用该对象进行更新时,我们都会收到一条消息,说.name``proxy``"name is being updated"
下面来看这样写的好处:
- 代理方法具有通用性,并且可以重用处理器
- 在代理对象上设置的任何值都可以递归地转换为代理
- 现在这个神奇的对象可以无论嵌套多深都能对数据更新作出反应。
除此之外,您还可[处理其他几个访问事件,例如读取、更新、删除属性等。
实现
更新用户界面
如果您还记得,反应 性的第二部分是自动更新 UI 。为此,我们需要获取要更新的相应 UI 元素。但在此之前,我们 需要首先根据需要标记 UI 元素。
为此,我们将使用 data-attributes
,该功能允许我们在元素上设置任意值:
html
<div>
<h1 data-mark="name"></h1>
</div>
data-attributes的优点在于,我们现在可以使用以下方法找到所有合适的元素:
js
document.querySelectorAll('[data-mark="name"]');
现在我们只需设置所有适当元素 :innerText
js
const handler = {
set(user, value, property) {
const query = `[data-mark="${property}"]`;
const elements = document.querySelectorAll(query);
for (const el of elements) {
el.innerText = value;
}
return Reflect.set(user, value, property);
},
};
const user = new Proxy({ name: 'Lin' }, handler);
就是这样,这就是反应性的关键!
由于我们的一般性质,对于设置的任何 属性,所有适当的 UI 元素都将更新。handler``user
这就是 JavaScript 功能的强大之处,具有 零依赖性和一些聪明,它可以给我们这些神奇的反应 对象。Proxy
现在进入第二个主要问题
可组合性
事实证明,浏览器已经有专门用于此的整个功能 叫Web组件
很少使用它,因为它使用起来有点痛苦。
对于可组合性,我们首先需要定义组件。
标记用于包含标记,即 不由浏览器呈现。例如,您可以添加以下内容 HTML 中的标记:<template>
html
<template>
<h1>Will not render!</h1>
</template>
并且它不会被渲染。您可以将它们视为不可见的容器 为您的组件。
下一个构建块是 定义组件内容的放置位置。这 使组件能够与不同的内容重用,即它变得可组合 。<slot>
例如,下面是一个将其文本着色为红色的 h1 元素。
html
<template>
<h1 style="color: red">
<slot />
</h1>
</template>
在我们开始使用我们的组件之前------比如上面的红色 h1,我们需要 注册它们。
在注册红色 h1 组件之前,我们需要一个名称来注册它 由。我们可以为此使用该属性:name
html
<template name="red-h1">
<h1 style="color: red">
<slot />
</h1>
</template>
现在,使用一些JavaScript,我们可以获取组件及其名称:
js
const template = document.getElementsByTagName('template')[0];
const componentName = template.getAttribute('name');
最后使用 customElements.define
注册它:
js
customElements.define(
componentName,
class extends HTMLElement {
constructor() {
super();
const component = template.content.children[0].cloneNode(true);
this.attachShadow({ mode: 'open' }).appendChild(component);
}
}
);
下面我们把这两个放到一起
总结
快速回顾一下,我们做了两件事:
- 我们创建了一个反应式数据结构,即代理对象 ,在设置值时可以更新我们标记的任何元素。
- 我们定义了一个自定义组件,该组件将呈现它的 内容为红色 H1。
red-h1
现在我们可以将它们放在一起:
```html
markdown
并让自定义组件呈现我们的数据并在我们时更新 UI 更改数据。
当然,通常的前端框架不只是这样做,它们有 专门的语法,例如 Vue 中的[模板语法](https://vuejs.org/guide/essentials/template-syntax.html)和 React 中的 [JSX](https://react.dev/learn/writing-markup-with-jsx),这使得编写复杂的前端相对更简洁 否则会是这样。
由于这种专门的语法不是常规的JavaScript或HTML,因此它不是 可由浏览器解析,因此它们都需要专门的工具来编译 在浏览器之前,它们可以简化为常规的JavaScript,HTML和CSS。 了解他们。
即使没有专门的语法,你也可以做很多通常的事情 前端框架以类似的简洁性完成,只需使用 和 即可。`Proxy``WebComponents`
这里的代码过度简化,并将其转换为框架 你必须充实它。
在开发此功能时,我计划维护两个硬约束:
1. 没有依赖关系。
2. 在可以使用之前没有构建步骤。