⚠️温馨提示: 文档中包含【2个】暂不支持的区域,请通过搜索关键字【暂不支持的文档区域】进行后续处理
很多同学可能第一次来,我分享了很多干货公开课,希望对你的技术成长有帮助!免费分享都在这里,需要 文档和配套视频找咨询老师 领取~
【飞书文档】备战金九银十,进阶大前端,涨薪全突破! 跳槽涨薪需求同学必看
【飞书文档】面试被算法干吐了?字节大佬带你突破极限,冲击 30K+ 必掌握算法与 WebAssembly 技术 冲刺年包 50W+ 同学必看
【飞书文档】高级前端专家如何做性能优化?特邀字节大佬细数飞书应用优化细节【超详细内培版】
【飞书文档】高级前端专家如何做项目架构与工程化设计? 特邀字节大佬细数字节开源项目架构细节【超详细内培版】
【飞书文档】小小微前端,轻松拿捏,特邀字节大佬开讲微前端架构与源码剖析【内部培训版】 35K+ 同学必看
【飞书文档】高级前端专家如何做性能优化?特邀字节大佬细数飞书应用优化细节(二)【内部培训版】
【飞书文档】前端破局 AI 应用开发,特邀字节大佬分享字节系 AI 场景落地应用与 AI 引擎编排流程 探索前端新方向同学福音
【飞书文档】React18 源码深度剖析,字节面试官教你轻松拿捏高级前端专家面试框架原理题
【飞书文档】Vue3 源码深度剖析,字节面试官教你轻松拿捏高级前端专家面试框架原理题
【飞书文档】Vite 构建过程与源码深度剖析,你怎么也想不到一线大厂工程化构建面试会问这么深!
【飞书文档】脚手架/CLI 工具原理与开发实践,特邀字节前端专家带你体悟大厂团队基建与研发工作流 团队基建必看
【飞书文档】Webpack 原理深度解读与面试专项突击,字节面试官带你手撕难缠打包构建面试原理题 工程化与构建原理深入
【飞书文档】冲击中大厂筹备与涨薪突击最优方案,特邀字节面试官带你体验大厂面试全流程
【飞书文档】字节面试专场——中厂在职学员冲击大厂 30K+,看看面试官如何评价 大厂模拟面试,问得很深慎看
【飞书文档】前端性能与异常监控平台全链路设计与实践,字节架构师:掌握这整套拿个 40K+ 不在话下吧
- 编辑器底层基础处理
- 光标、框选、缓冲区等
- 编辑器编译与转换器——json2html、html2json 或 md2html、html2md
- 协同、冲突解决、undo/redo
- 【 初中级 】有没有做过富文本编辑器开发?怎么实现的?
- 【 中高级 】从零到一自研一个富文本编辑器,需要考虑哪些问题,怎么实现?
- 【 专家级 】架构设计一个类似飞书文档、notion 的产品,并详细说明具体细节?
⚠️ 暂不支持的文档区域,【文档小组件】
- Github copilot
- Gemini
- cursor
【 初中级 】有没有做过富文本编辑器开发?怎么实现的? |
竞品介绍
飞书文档 和 Notion :
- 提供丰富的文本格式支持、多媒体嵌入、实时协作等强大功能。
- 用户可以通过快捷命令(如
/、@)快速插入不同类型的块(Block),提高操作效率。
其他常见的富文本编辑器 :
- Google Docs 和 Microsoft Word Online :支持复杂的文档编辑功能,如文本格式化、插入图片、撤销重做、表格和绘图等。
- Quill 和 Draft.js :开源富文本编辑器,允许开发者在其基础上构建自定义的编辑器解决方案,支持插件扩展和丰富的 API。
富文本开发核心
富文本编辑器的开发核心在于支持多样化的文本操作和功能,包括:
- 文本格式化 :加粗、斜体、下划线、颜色、背景色等多种文本样式。
- 多媒体插入 :图片、视频、文件、链接等。
- 撤销重做 :用户在编辑过程中可以随时撤销和重做操作。
- 跨浏览器兼容性 :在不同浏览器和平台上行为一致。
- 协作功能 :多用户实时协作,显示协同光标和实时文档同步。
实现这些功能的基础是使用
contenteditable属性,使元素变为可编辑状态,并结合 JavaScript 监听用户操作。
contenteditable 基础
contenteditable属性 :- 是 HTML 属性,可以使任何元素变为可编辑状态。通过设置
contenteditable="true",用户可以直接在元素内输入或删除文本。 - 常用于构建富文本编辑器的编辑区域,简单高效。
- 是 HTML 属性,可以使任何元素变为可编辑状态。通过设置
示例 :创建一个可编辑的
div元素。
<div class="editor-content" contenteditable="true">
<p>这是一个简单的富文本编辑器。</p>
</div>
-
- 在 JavaScript 中,结合事件监听,实现输入事件的捕获和处理:
document.querySelector('.editor-content').addEventListener('input', function (e) {
console.log('用户输入了内容:', e.target.innerHTML);
});
浏览器 execCommand (编辑器小白级别)
document.execCommand 是一种浏览器 API,用于在 HTML 文档中执行与文档内容相关的命令。该方法最初是为实现富文本编辑功能而设计的,可以让开发者轻松实现一些常见的编辑操作,如加粗文本、插入链接、剪切、复制、粘贴等。尽管这种方法在过去非常流行,但随着 Web 技术的发展,它的使用逐渐减少,并在一些现代浏览器中被标记为过时或即将废弃。
基本语法
document.execCommand(command, showUI, value);
- command (字符串): 要执行的命令名称,如
'bold'、'italic'、'copy'等。 - showUI (布尔值): 指示是否显示默认的用户界面,通常传入
false,因为许多命令都不支持或忽略这个参数。 - value (字符串): 与某些命令一起使用的值,如在插入链接时的 URL。对于不需要值的命令,该参数可以省略或传入
null。
常见的命令
以下是一些常见的命令及其用途:
- 'bold' : 使选中的文本加粗。
document.execCommand('bold');
- 'italic' : 使选中的文本倾斜。
document.execCommand('italic');
- 'underline' : 为选中的文本添加下划线。
document.execCommand('underline');
- 'copy' : 复制当前选中的内容到剪贴板。
document.execCommand('copy');
- 'cut' : 剪切当前选中的内容,并将其放入剪贴板。
document.execCommand('cut');
- 'paste' : 从剪贴板粘贴内容到光标所在的位置。
document.execCommand('paste');
- 'createLink' : 为选中的文本创建超链接。
document.execCommand('createLink', false, 'https://example.com');
- 'unlink' : 移除选中文本中的链接。
document.execCommand('unlink');
示例
下面是一个简单的示例,用于实现富文本编辑功能:
<div contenteditable="true" id="editor">
这是一个可编辑的区域。
</div>
<button onclick="document.execCommand('bold')">加粗</button>
<button onclick="document.execCommand('italic')">倾斜</button>
<button onclick="document.execCommand('underline')">下划线</button>
重要提示 :尽管 document.execCommand 目前在许多浏览器中仍然可用,但建议开发者尽量避免在新项目中使用它,并逐步迁移到更现代、更可靠的替代方案。
document.execCommandAPI :是一个用于执行编辑命令的旧版浏览器 API,例如加粗、斜体、删除等。它可以简化一些常见的编辑操作。
常用命令包括:
document.execCommand('bold'):将选中的文本加粗。document.execCommand('italic'):将选中的文本变为斜体。document.execCommand('underline'):为选中的文本添加下划线。
局限性 :
- 兼容性问题 :该 API 在现代浏览器中的支持逐渐减少,部分功能在某些浏览器中可能表现不一致。
- 灵活性不足 :无法实现复杂的格式和定制化需求。
替代方案 :手动操作 DOM(使用
Selection和Range)来实现自定义的文本编辑功能。
Selection 和 Range
Selection 和 Range 是 Web API 中的两个重要接口,它们用于处理用户在网页中的文本或元素选择。这两个接口提供了对文档中选定内容的精确控制,可以用来创建富文本编辑器、处理用户选择、实现自定义的文本操作等。
Selection 接口
Selection 对象表示用户或脚本在文档中选中的文本范围。它通常与光标或鼠标选择操作相关联。
常用属性
anchorNode: 返回Selection的起始节点(锚点)。anchorOffset: 返回Selection的起始节点内的偏移量。focusNode: 返回Selection的结束节点(焦点)。focusOffset: 返回Selection的结束节点内的偏移量。isCollapsed: 如果选区的起始点和结束点在同一位置,则返回true,否则返回false。这意味着没有实际的文本被选中。rangeCount: 返回当前选区中的Range对象的数量。通常是 1,但在某些浏览器中可以选择多个不连续的区域。
常用方法
getRangeAt(index): 返回选区中的指定Range对象。通常使用getRangeAt(0)来获取当前选区。addRange(range): 向选区添加一个Range对象。如果当前已经存在选区,新的范围会与之合并。removeAllRanges(): 移除所有选区。collapse(node, offset): 将选区折叠到文档中的一个特定点,即将选区的起点和终点设置为相同的值。extend(node, offset): 将当前选区的终点扩展到指定的节点和偏移量。collapseToStart(): 将选区折叠到起始点。collapseToEnd(): 将选区折叠到结束点。
示例
const selection = window.getSelection();
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
console.log(range.toString()); // 输出当前选中的文本
}
Range 接口
Range 对象表示文档中连续的一段内容。它可以包含部分或整个节点。 Range 允许开发者在文档中创建、修改、删除特定的选区,而不仅仅局限于用户选择的内容。
常用属性
startContainer: 返回Range起点所在的节点。startOffset: 返回Range起点在startContainer内的偏移量。endContainer: 返回Range终点所在的节点。endOffset: 返回Range终点在endContainer内的偏移量。collapsed: 如果Range的起点和终点在同一位置,则返回true,否则返回false。
常用方法
setStart(node, offset): 设置Range的起点为指定节点的特定偏移量。setEnd(node, offset): 设置Range的终点为指定节点的特定偏移量。selectNode(node): 将Range设置为包含整个指定节点。selectNodeContents(node): 将Range设置为包含指定节点的所有内容。collapse(toStart): 将Range折叠到起点或终点。toStart为true时折叠到起点,反之折叠到终点。cloneRange(): 创建并返回一个与当前Range完全相同的副本。deleteContents(): 删除Range包含的文档内容。extractContents(): 删除Range的内容并返回一个DocumentFragment,其中包含了被删除的内容。insertNode(node): 将一个节点插入到Range的起点位置。surroundContents(newParent): 将Range的内容放入到一个新创建的父节点newParent中。
示例
const range = document.createRange();
const textNode = document.getElementById('someTextNode');
range.selectNodeContents(textNode);
// 获取选中的内容
const selectedContent = range.cloneContents();
console.log(selectedContent.textContent);
// 删除选中的内容
range.deleteContents();
// 插入新内容
const newNode = document.createTextNode('This is new content.');
range.insertNode(newNode);
结合使用 Selection 和 Range
Selection 和 Range 通常会一起使用。例如,使用 Selection 对象获取当前用户选中的内容,然后使用 Range 对象来精确操作选区中的内容。
示例:加粗用户选中的文本
const selection = window.getSelection();
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
// 创建一个新的 <strong> 元素
const strongNode = document.createElement('strong');
// 将选中的文本放入 <strong> 元素中
strongNode.appendChild(range.extractContents());
// 将 <strong> 元素插入到文档中
range.insertNode(strongNode);
}
实际应用
- 富文本编辑器 : 使用
Selection和Range接口可以轻松实现文本的加粗、斜体、链接插入等功能。 - 文本高亮 : 通过
Range可以实现自定义的文本高亮效果。 - 自定义剪切板操作 : 使用
Selection可以捕获用户的选择,并执行定制化的复制或剪切操作。
示例:使用 contenteditable 、 Selection 和 Range 实现富文本编辑器功能
- 基本编辑功能 :加粗选中的文本。
<div class="editor-content" contenteditable="true">
<p>这是一个简单的富文本编辑器。</p>
</div>
<button onclick="applyBold()">加粗</button>
<script>
function applyBold() {
const selection = window.getSelection();
if (!selection.rangeCount) return;
const range = selection.getRangeAt(0);
// 创建一个 <strong> 标签包裹选中的文本
const strong = document.createElement('strong');
strong.appendChild(range.extractContents());
range.insertNode(strong);
// 清除选区,并将光标设置在新的 <strong> 标签之后
selection.removeAllRanges();
const newRange = document.createRange();
newRange.setStartAfter(strong);
newRange.collapse(true);
selection.addRange(newRange);
}
</script>
- 添加和移除文本样式 :支持加粗、斜体等多种样式。
function exec(command) {
const selection = window.getSelection();
if (!selection.rangeCount) return;
const range = selection.getRangeAt(0);
let wrapper;
// 根据命令类型创建相应的标签
switch (command) {
case 'bold':
wrapper = document.createElement('strong');
break;
case 'italic':
wrapper = document.createElement('em');
break;
default:
return;
}
wrapper.appendChild(range.extractContents());
range.insertNode(wrapper);
// 清除选区并更新光标位置
selection.removeAllRanges();
const newRange = document.createRange();
newRange.setStartAfter(wrapper);
selection.addRange(newRange);
}
- 选择文本并将光标移到特定位置 :
document.querySelector('.editor-content').addEventListener('mouseup', () => {
const selection = window.getSelection();
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
console.log('当前选中的文本:', range.toString());
}
});
额外功能
添加加粗样式 :
- 选中一段文本,调用
exec('bold')方法,将选中的文本包裹在<strong>标签中。
- 选中一段文本,调用
插入文本或节点 :
- 选中一个位置或范围,使用
range.insertNode(newNode)将自定义节点插入到指定位置。
- 选中一个位置或范围,使用
撤销和重做 :
- 结合
Selection和Range对象操作,实现对用户操作的记录和回退功能。
- 结合
【 中高级 】从零到一自研一个富文本编辑器,需要考虑哪些问题,怎么实现? |
文本格式化和处理
问题 :如何支持加粗、斜体、下划线、颜色、背景色等多种格式?
实现方法 :
- 使用 JavaScript 手动操作 DOM,为选中的文本节点添加或移除样式标签(如
<strong>、<em>)。 - 通过
isWrapped方法判断选区是否已应用某种格式,通过exec方法对选区应用或移除格式。
- 使用 JavaScript 手动操作 DOM,为选中的文本节点添加或移除样式标签(如
代码示例 :
isWrapped方法 :检查当前选区是否已被包装指定格式。
function isWrapped(range, command) {
let container = range.commonAncestorContainer;
if (container.nodeType === Node.TEXT_NODE) {
container = container.parentNode;
}
// 遍历父节点,检查是否有指定格式的节点
while (container && container !== document) {
switch (command) {
case "bold":
if (container.nodeName === "B" || container.nodeName === "STRONG")
return true;
break;
case "italic":
if (container.nodeName === "I" || container.nodeName === "EM")
return true;
break;
case "underline":
if (container.nodeName === "U") return true;
break;
case "color":
if (container.nodeName === "SPAN" && container.style.color)
return true;
break;
default:
return false;
}
container = container.parentNode;
}
return false;
}
exec方法 :应用或移除文本样式。
function exec(command, extra) {
if (!selection) return;
const range = selection.getRangeAt(0);
const isContentWrapped = isWrapped(range, command);
if (isContentWrapped) {
unwrap(range, command); // 解除格式
} else {
// 应用新格式
let wrapper;
switch (command) {
case "bold":
wrapper = document.createElement("strong");
break;
case "italic":
wrapper = document.createElement("em");
break;
case "underline":
wrapper = document.createElement("u");
break;
case "color":
wrapper = document.createElement("span");
wrapper.style.color = extra.color;
break;
default:
break;
}
if (!wrapper) return;
const selectedText = range.extractContents();
wrapper.appendChild(selectedText);
range.insertNode(wrapper);
// 合并相邻的相同格式标签
mergeAdjacentNodes(wrapper);
}
updateSelectionAfterCommand(range);
}
unwrap方法 :解除选中的文本格式。
function unwrap(range, command) {
let container = range.commonAncestorContainer;
if (container.nodeType === Node.TEXT_NODE) {
container = container.parentNode;
}
function unwrapNode(node) {
if (!node) return;
const parentNode = node.parentNode;
if (!parentNode) return;
if (
(command === "bold" &&
(node.nodeName === "B" || node.nodeName === "STRONG")) ||
(command === "italic" &&
(node.nodeName === "I" || node.nodeName === "EM")) ||
(command === "underline" && node.nodeName === "U") ||
(command === "color" && node.nodeName === "SPAN" && node.style.color)
) {
const beforeRange = document.createRange();
beforeRange.setStartBefore(node);
beforeRange.setEnd(range.startContainer, range.startOffset);
const afterRange = document.createRange();
afterRange.setStart(range.endContainer, range.endOffset);
afterRange.setEndAfter(node);
if (!beforeRange.collapsed) {
const beforeNode = document.createDocumentFragment();
beforeNode.appendChild(beforeRange.extractContents());
parentNode.insertBefore(beforeNode, node);
}
if (!afterRange.collapsed) {
const afterNode = document.createDocumentFragment();
afterNode.appendChild(afterRange.extractContents());
parentNode.insertBefore(afterNode, node.nextSibling);
}
const selectedContents = range.extractContents();
parentNode.insertBefore(selectedContents, node);
parentNode.removeChild(node);
} else {
unwrapNode(node.parentNode);
}
}
unwrapNode(container);
}
核心逻辑说明 :
- 应用格式 :使用
exec方法,通过判断选区是否已经被包装来决定是应用还是移除某种格式。 - 解除格式 :使用
unwrap方法递归遍历父节点,解除指定的格式化标签。
- 应用格式 :使用
光标与选区管理
问题 :如何正确管理光标和文本选区的位置?
实现方法 :
- 使用
selectionchange事件监听用户的选区变化。 - 创建自定义光标元素,在用户编辑内容或移动光标时,更新光标位置。
- 使用
代码示例 :
onMounted(() => {
const customCursor = document.createElement("div");
customCursor.classList.add("custom-cursor");
document.body.appendChild(customCursor);
cursorRef.value = customCursor;
document.addEventListener("selectionchange", () => {
const innerSelection = window.getSelection();
const isInEditor =
innerSelection?.focusNode &&
document.querySelector(".editor-content")?.contains(innerSelection?.focusNode);
if (!isInEditor) {
customCursor.style.display = "none";
return;
}
selection = innerSelection;
if (selection.rangeCount > 0 && !selection.isCollapsed) {
customCursor.style.display = "none"; // 隐藏光标
} else {
customCursor.style.display = "block"; // 显示光标
updateCursorPosition();
}
});
});
function updateCursorPosition() {
if (!selection || !cursorRef.value) return;
const range = selection.getRangeAt(0);
const rect = range.getBoundingClientRect();
const cursor = cursorRef.value;
cursor.style.top = `${rect.top}px`;
cursor.style.left = `${rect.left - 1}px`;
}
核心逻辑说明 :
- 自定义光标 :通过
selectionchange事件实时监听用户的选区变化,判断光标位置并根据情况显示或隐藏自定义光标。 - 光标位置更新 :在用户光标位置变化时,使用
updateCursorPosition函数动态更新自定义光标的位置。
- 自定义光标 :通过
性能优化
问题 :在处理大量文本和复杂内容时,如何保证编辑器的响应速度?
实现方法 :
- 使用虚拟 DOM 技术和延迟渲染优化大量 DOM 操作。
- 在高频事件(如键盘输入、鼠标移动)中使用防抖和节流技术减少不必要的渲染。
性能优化措施 :
- 防抖(Debounce) :对于频繁的输入事件,延迟函数的执行,减少不必要的重绘。
- 节流(Throttle) :限制高频事件的触发次数,例如在鼠标拖动或滚动时限制每秒触发的次数。
跨浏览器兼容性
问题 :在不同的浏览器上,如何确保编辑器行为一致?
实现方法 :
- 使用标准化的 Web API,如
Selection和Range,避免依赖特定浏览器行为。 - 对常见的浏览器行为差异进行检测和兼容性处理。
- 使用标准化的 Web API,如
兼容性措施 :
- 使用 Polyfill :为不支持的浏览器提供兼容代码。
- 检测浏览器行为差异 :如在操作节点时,使用统一的方法避免不同浏览器的行为差异。
数据序列化与反序列化(编译器)
大部分编辑器都会提供编译器,例如 markdown、mdx 会有 hyper,tiptap 类会有基于 JSONContent 的结构,通常来说:
- 基于 markdown 会有更高性格,更大约束
- 基于 JSONContent 会有更灵活特性,对于自定义节点等极其便捷,可以 tiptap custom node 为例
问题 :如何将编辑器内容转换为可存储的格式(如 JSON)并支持从这种格式解析回来?
实现方法 :
实现 JSON 和 HTML 的双向转换函数:
html2Json:将 HTML 解析为结构化的 JSON 数据。json2Html:从 JSON 数据生成 HTML 字符串。
代码示例 :
HTML 转换为 JSON :
export function html2Json(element) {
if (!element) return null;
switch (element.nodeType) {
case Node.ELEMENT_NODE: {
const tagName = element.tagName.toLowerCase();
if (tagName === "p") {
return {
type: "paragraph",
content: Array.from(element.childNodes)
.map(html2Json)
.filter(Boolean),
};
} else if (tagName === "strong") {
return {
type: "text",
marks: [{ type: "bold" }],
text: element.textContent,
};
} else if (tagName === "em") {
return {
type: "text",
marks: [{ type: "italic" }],
text: element.textContent,
};
}
break;
}
case Node.TEXT_NODE: {
return {
type: "text",
text: element.textContent,
};
}
default:
return null;
}
}
- JSON 转换为 HTML :
export function json2Html(json) {
if (!json || !json.type) return "";
switch (json.type) {
case "doc":
return json.content.map(json2Html).join("");
case "paragraph":
return <p>${json.content.map(json2Html).join("")}</p>;
case "text": {
let text = json.text;
if (json.marks) {
json.marks.forEach((mark) => {
switch (mark.type) {
case "bold":
text = <strong>${text}</strong>;
break;
case "italic":
text = <em>${text}</em>;
break;
default:
break;
}
});
}
return text;
}
default:
return "";
}
}
核心逻辑说明 :
- HTML 转 JSON :通过递归遍历 DOM 元素,构建结构化的 JSON 数据。
- JSON 转 HTML :从 JSON 数据生成对应的 HTML 字符串,支持富文本格式。
以 Quill 为例分析【重点】
Quill 的基本架构
- 内容模型 :
Quill 采用了一个名为“Delta”的数据模型来表示文档的内容。Delta 是一种 JSON 格式的文档结构,它可以表示文本、格式和嵌入对象(如图像或视频)。这种数据结构与 HTML DOM 无关,使得文档的操作更加灵活和高效。
- 渲染引擎 :
Quill 的渲染引擎将 Delta 数据模型转换为 HTML,显示在编辑器中。每次用户进行编辑操作时,Quill 更新 Delta 并重新渲染编辑器内容。
- 事件处理与模块 :
Quill 使用模块化设计,通过各种插件和模块来处理不同的编辑操作。这些模块处理用户输入、光标移动、文本格式化等任务。Quill 还支持自定义模块,让开发者可以扩展编辑器的功能。
Quill 数据协议
Quill 采用了一个名为“Delta”的数据模型来表示文档的内容。Delta 是一种 JSON 格式的文档结构,它可以表示文本、格式和嵌入对象(如图像或视频)。这种数据结构与 HTML DOM 无关,使得文档的操作更加灵活和高效。
Quill 使用 Selection 和 Range
它利用了 Selection 和 Range 接口来处理用户的光标位置、文本选择和内容操作。
Selection: Quill 使用SelectionAPI 来确定用户当前选择的文本范围,并将其与 Delta 模型进行同步。这对于处理用户的选择、移动光标、以及执行特定的文本操作(如加粗或插入链接)是至关重要的。Range: 在需要精确处理选区时,Quill 使用RangeAPI 来获取或设置用户选择的具体范围。例如,当用户选择一段文本并应用某种格式时,Quill 会使用Range来确定这段文本的开始和结束位置,然后应用相应的格式。
Quill 的操作流程示例
用户输入或编辑操作 :
- 用户通过键盘输入文本,或者通过鼠标选择一段文本。
- Quill 捕获用户的输入事件,并根据当前的光标位置或选区来修改 Delta 数据结构。
Delta 数据更新 :
- Quill 更新 Delta 模型以反映用户的操作,例如插入新文本或应用文本格式。
- Delta 是一种不可变的数据结构,因此每次更新都会创建一个新的 Delta。
渲染和同步 :
- Quill 根据更新后的 Delta 模型重新渲染编辑器内容。
- 使用
Selection和RangeAPI,Quill 同步光标位置或选区,使用户界面与内部数据保持一致。
事件触发 :
- Quill 触发相应的事件,通知应用程序其他部分(如工具栏)有内容更新。
- 开发者可以通过事件监听器来扩展 Quill 的功能或与其他部分进行交互。
【 专家级 】架构设计一个类似飞书文档、notion 的产品,并详细说明具体细节 |
详细分析飞书文档与 Notion 产品功能
核心功能 :
- 实时协作 :支持多用户同时编辑,同步光标位置和编辑内容变化,提供实时反馈。
- 丰富的文本和块样式支持 :支持多种文本格式(如标题、粗体、斜体、下划线)、块元素(如列表、表格、代码块)和多媒体嵌入(如图片、视频)。
- 历史版本管理 :记录每次编辑的历史版本,支持查看、回滚和恢复。
- 撤销重做支持 :提供撤销和重做功能,允许用户快速回退和恢复操作。
- 快捷命令(如
/** 命令)和智能推荐功能**:提供快速插入块元素的方式,用户输入/后弹出操作选项,如插入图片、表格、代码块等;@功能用于提及用户或文档链接,方便协作和文档互联。
基于 Block 设计的物料
Block 设计 :
- 文档内容被视为多个块(Block)的组合,每个块都是一个独立的元素,可以单独存在、编辑、删除或移动。
- 块的类型可以多样化,例如文本段落、标题、列表、表格、代码块、图片等。
实现方法 :
- 每个块作为一个独立的组件 :在前端架构中,每个块都是一个 React 组件。每个组件根据其类型和内容进行独立渲染和管理。
- 数据结构 :使用 JSON 来描述文档的结构,每个块对应一个 JSON 对象,包含类型、内容、样式等信息。
- 渲染逻辑 :通过遍历 JSON 数据结构,使用
json2Html方法将每个块渲染为相应的 HTML 元素。 - 支持嵌套块和拖拽排序 :允许块嵌套,例如列表中的项目可以是另一个列表;支持用户拖拽排序,改变块的位置。
代码示例 :
// 基于 JSON 描述的 Block 数据结构
const blocks = [
{
type: "heading",
level: 1,
content: "Welcome to Feishu Docs",
},
{
type: "paragraph",
content: "This is a collaborative document editor.",
},
{
type: "list",
items: [
{ type: "text", content: "Rich text support" },
{ type: "text", content: "Real-time collaboration" },
],
},
];
// 渲染函数,将 JSON 数据转换为 HTML
function renderBlocks(blocks) {
return blocks.map((block) => {
switch (block.type) {
case "heading":
return `<h${block.level}>${block.content}</h${block.level}>`;
case "paragraph":
return `<p>${block.content}</p>`;
case "list":
return `<ul>${block.items.map(item => `<li>${item.content}</li>`).join('')}</ul>`;
default:
return "";
}
}).join('');
}
const htmlOutput = renderBlocks(blocks);
document.getElementById("editor").innerHTML = htmlOutput;
数据协议【重点】
Quill
Quill 是一个流行的开源富文本编辑器,以其灵活性和可扩展性而著称。它采用了名为“Delta”的数据模型来表示文档的内容。
Delta 数据模型
- Delta 是一种 JSON 格式的文档结构,用于表示文本、格式和嵌入对象(如图像或视频)。这种数据结构独立于 HTML DOM,专注于内容的逻辑表示,而不是具体的呈现方式。
- 文本操作 在 Delta 中以“操作”(operations)的形式记录,包括插入、删除和保留(retain)。这使得文档内容的变更能够精确地描述,并支持撤销/重做、协作编辑等高级功能。
- 格式化与嵌入 :Delta 可以精确地描述文本的格式(如加粗、斜体、链接等)和嵌入对象的位置与属性。这种模型的灵活性允许对内容进行高效的操作,而不必依赖 DOM 操作。
Delta 的基本结构
Delta 是一个由多个操作(operations)组成的数组。每个操作可以是以下三种类型之一:
- Insert (插入)
- Delete (删除)
- Retain (保留)
Insert 操作
用于在文档中的某个位置插入文本或嵌入对象。Insert 操作可以附带格式信息。
{
"insert": "Hello, World!"
}
插入操作也可以包含格式信息:
{
"insert": "Hello, World!",
"attributes": {
"bold": true,
"italic": true
}
}
除了插入文本,还可以插入嵌入对象,例如图片、视频等:
{
"insert": {
"image": "https://example.com/image.png"
}
}
Delete 操作
用于删除指定数量的字符。Delete 操作只需要指定要删除的字符数。
{
"delete": 5
}
这将删除文档中的 5 个字符。
Retain 操作
用于保留指定数量的字符,通常用于在文档中移动光标,或者应用格式而不改变内容。Retain 操作也可以附带格式信息。
{
"retain": 7
}
这将保留文档中的 7 个字符。
Retain 操作也可以应用格式:
{
"retain": 7,
"attributes": {
"bold": true
}
}
这将使保留的 7 个字符变为加粗。
完整的 Delta 示例
以下是一个包含插入、删除和保留操作的完整 Delta 示例,展示了如何表示复杂的编辑操作:
[
{ "insert": "Hello " },
{ "attributes": { "bold": true }, "insert": "World" },
{ "insert": "!" },
{ "retain": 1, "attributes": { "italic": true } },
{ "delete": 5 }
]
这个 Delta 描述了以下操作:
- 插入文本 "Hello "。
- 插入加粗的文本 "World"。
- 插入标点 "!"。
- 将光标向后移动 1 个字符,并将其格式设置为斜体(假设之前的 "!" 已被选中)。
- 删除 5 个字符(假设这些字符位于 "World" 之后,Delta 的实际应用中,这可能用于撤销前面的某些操作)。
复杂场景中的 Delta
Delta 的设计使得它可以描述复杂的文档编辑场景,包括协作编辑、撤销重做和内容同步。通过将多个 Delta 合并,应用程序可以实现多用户协作的功能,并确保不同用户的编辑操作不会相互冲突。
特点
- 与 HTML DOM 无关 :Delta 数据模型与 HTML DOM 的独立性意味着它可以轻松在不同的呈现环境中使用,包括服务端渲染、跨平台应用等。
- 模块化与扩展性 :Quill 通过模块系统提供可扩展的功能,可以自定义工具栏、格式和事件处理器。开发者可以编写自己的模块来扩展编辑器的功能。
Tiptap
Tiptap 是一个基于 ProseMirror 构建的富文本编辑器框架,旨在提供比传统富文本编辑器更高的灵活性和可定制性。Tiptap 的设计初衷是为现代 Web 应用提供一个高度可定制的编辑解决方案。
ProseMirror 数据模型
- 基于 Schema 的模型 :Tiptap 使用 ProseMirror 的 Schema 来定义文档的结构。Schema 定义了文档中允许的节点类型、嵌套规则和属性,使得编辑器可以支持复杂的文档结构,如表格、列表、嵌套的引用等。
- 可扩展的节点与标记 :Tiptap 允许开发者定义自定义的节点(nodes)和标记(marks),这些都是 ProseMirror Schema 的一部分。这使得开发者可以轻松实现特定的文档结构和格式需求。
- 命令与插件系统 :Tiptap 提供了一个强大的命令和插件系统,允许开发者通过命令(commands)来实现复杂的用户交互和文本操作。
特点
- 高度可定制化 :通过插件和 Schema 定义,Tiptap 提供了极高的定制性,适合复杂的编辑需求,如编写代码编辑器、学术文章编辑器等。
- 基于 Vue.js :Tiptap 的核心基于 Vue.js,虽然也支持 React 等框架,但其与 Vue.js 的集成尤为紧密,适合使用 Vue 的开发者。
/ 命令与 @ 等功能
/命令 :- 提供快捷操作入口,如插入不同类型的块(图片、表格、代码块等)。
- 实现方法 :捕获用户输入的
/符号,弹出下拉菜单显示可选项(如插入标题、列表、表格等)。
@功能 :- 支持用户提及(mention)功能和文档链接功能。用户输入
@后,可以选择用户进行提及或链接到其他文档。 - 实现方法 :捕获用户输入的
@符号,根据输入内容动态显示推荐选项(用户列表或文档列表)。
- 支持用户提及(mention)功能和文档链接功能。用户输入
代码示例 :
// 捕获输入事件
document.querySelector('.editor-content').addEventListener('input', function (e) {
const inputText = e.target.innerText;
if (inputText.endsWith('/')) {
showCommandMenu(e.target); // 显示命令菜单
}
if (inputText.endsWith('@')) {
showMentionMenu(e.target); // 显示提及菜单
}
});
function showCommandMenu(target) {
// 显示插入命令的选项菜单
console.log("显示插入命令菜单");
}
function showMentionMenu(target) {
// 显示用户提及的选项菜单
console.log("显示提及选项菜单");
}
历史记录栈与 Undo/Redo
问题 :如何实现撤销和重做功能?
实现方法 :
- 使用 命令模式 和 历史记录栈 来管理用户操作。每次用户执行操作时,将该操作的命令对象推入栈中。
- 当用户点击撤销时,从栈中弹出最后一个命令对象并调用其
undo方法;当用户点击重做时,调用redo方法。
代码示例 :
class Command {
constructor(execute, undo, value) {
this.execute = execute;
this.undo = undo;
this.value = value;
}
}
const historyStack = [];
const undoStack = [];
function executeCommand(command) {
command.execute(command.value);
historyStack.push(command);
}
function undoCommand() {
const command = historyStack.pop();
if (!command) return;
command.undo(command.value);
undoStack.push(command);
}
function redoCommand() {
const command = undoStack.pop();
if (!command) return;
command.execute(command.value);
historyStack.push(command);
}
// 示例:加粗命令
const boldCommand = new Command(
(value) => document.execCommand('bold', false, value),
(value) => document.execCommand('undo', false, value)
);
// 执行加粗命令
executeCommand(boldCommand);
// 撤销加粗
undoCommand();
协同光标实现与协同编辑
问题 :如何实现多用户实时协同编辑,显示协同光标?
实现方法 :
- 前端使用 WebSocket 进行实时通信 :每个客户端(用户)连接到 WebSocket 服务器,通过发送和接收光标位置和编辑内容,保持实时同步。
- 后端 WebSocket 服务器 :接收来自客户端的消息,并将消息广播给所有其他客户端,确保所有用户的编辑状态一致。
- 其他方案还有:yjs 等
前端实现:
const socket = new WebSocket('ws://localhost:8080');
// 发送光标位置和编辑内容到服务器
function sendCursorAndContent(cursorPosition, content) {
const message = JSON.stringify({
type: 'update',
cursor: cursorPosition,
content: content,
});
socket.send(message);
}
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'update') {
updateRemoteCursor(data.cursor);
updateRemoteContent(data.content);
}
};
function updateRemoteCursor(cursor) {
// 更新其他用户的光标位置
}
function updateRemoteContent(content) {
// 更新其他用户的文档内容
}
- 服务端实现:
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
ws.on('message', (message) => {
wss.clients.forEach((client) => {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
});
});
console.log('WebSocket server is running on ws://localhost:8080');
核心逻辑说明 :
- 实时通信 :每个客户端通过 WebSocket 与服务器保持连接,当用户操作光标或编辑文档内容时,客户端发送消息到服务器。
- 消息广播 :服务器将接收到的消息广播给所有其他客户端,所有用户的文档内容和光标状态保持同步。
冲突解决
问题 :如何解决多用户协同编辑时产生的冲突?
实现方法 :
乐观锁机制 :在客户端进行本地操作的同时,将编辑操作发送到服务器,假设不会发生冲突。服务器收到操作后检查冲突,如果没有冲突,将更新广播给其他客户端;如果有冲突,通知冲突的客户端进行回滚或合并。
基于 OT(Operational Transformation)或 CRDT(Conflict-free Replicated Data Type)算法 :
- OT 算法 :通过操作变换来处理冲突,确保多个用户的编辑操作按照相同的顺序合并,从而达成一致。
- CRDT 算法 :通过状态同步的方式,确保所有客户端可以并行编辑数据并最终收敛到同一个状态,避免冲突。
核心逻辑说明 :
- 冲突检测和解决 :实时协作编辑时,使用乐观锁或算法(如 OT 或 CRDT)来检测和解决冲突,确保所有客户端的文档内容一致。
- 确保最终一致性 :通过变换操作或合并状态,所有用户的编辑内容最终一致。
代码示例
使用乐观锁机制
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'update') {
if (isConflict(data)) {
resolveConflict(data);
} else {
updateRemoteCursor(data.cursor);
updateRemoteContent(data.content);
}
}
};
function isConflict(data) {
// 简单示例:判断操作时间戳是否冲突
return data.timestamp < lastOperationTimestamp;
}
function resolveConflict(data) {
console.log("冲突检测到,正在解决...");
// 合并冲突操作或回滚
}
使用 OT 算法
// OT 算法的基本操作变换逻辑
function transformOperation(localOp, remoteOp) {
// 根据操作类型和位置进行变换
if (localOp.position < remoteOp.position) {
return localOp;
} else {
localOp.position += remoteOp.length;
return localOp;
}
}
socket.onmessage = (event) => {
const remoteOp = JSON.parse(event.data);
const localOp = getLocalOperation(); // 本地未同步的操作const transformedOp = transformOperation(localOp, remoteOp);
applyOperation(transformedOp);
};
简历优化(30K+ 简历样板)
技能描述
熟练掌握 HTML、CSS、JavaScript、Typescript 以及 OOP、FP、 AOP 等设计思想
掌握样式体系构建与落地,对 css 预编译、css in js、module css 以及 utility-first CSS 有深入研究,并从零改良过样式体系以支持 SSR、SSG
熟悉 React、Vue 相关技术栈,熟悉 React、Vue 及相关技术框架的实现原理
掌握构建工具 Webpack、Vite 等,掌握编译工具 Babel,并深入理解其原理,并参与 Rspack 构建
丰富的数据可视化经验,熟悉 Canvas、svg 开发范式,理解 Echarts、Antv 原理,能根据业务需求基于 d3、zrender 开发自定义渲染引擎
丰富的跨端开发经验,熟练使用 Taro、Flutter、React-Native 开发跨端应用,对构建 hybird App 有丰富经验,深入理解跨端开发编译原理
基于 Node.js 开发脚手架、打包构建优化工具及中间件服务
掌握常用设计模式、算法与安全知识,追求开发高质量、高可维护性代码,追求极致产品体验
团队管理经验,并在项目架构设计与性能优化方面具有丰富经验
算法与编程技术
- 精通各种算法题的分类及解决方法,包括排序与查找、数据结构、动态规划、贪心算法、回溯算法、分治算法、图论算法、数学算法等。
- 重点掌握动态规划的基本概念、解题步骤和经典题目。
3D 数字孪生平台开发经验
- 熟练使用 WebGL 和 WebAssembly 技术,开发高效的 3D 渲染引擎。
- 精通正射影像和倾斜摄影技术,具备 Tile 和模型(包括白膜和精模)的处理经验,能够使用 Blender 进行模型制作。
- 熟悉材质、光效和粒子系统的实现与优化。
全面性能优化能力
- 具备打包构建优化经验,熟练使用 Webpack 进行模块打包,掌握 chunk、treeshaking、happypack、cache-loader 等优化技巧,并使用 Webpack Module Federation 进行模块联邦管理。
- 精通资源优化,能够有效进行图片、字体压缩,管理请求队列,并通过 OSS 和 CDN 提升资源加载速度。
- 具有应用性能优化经验,包括数据结构优化和应用模块更新。
- 深入了解缓存机制,熟悉强缓存(Expiration、Cache-Control)、协商缓存(Etag)和策略缓存(Service-Worker)的配置与管理。
项目描述
这个环节至关重要,很多同学不重视,简历随便写一写就开始投递了,结果投出去几百份可能一家公司面试都没有,结果就在怀疑前端行情出问题了
STAR 法则:
- 遇到了什么问题 question,需求
- 怎么评估解决方案,方案对比,方案落地 react 状态管理(redux、mobx、jotai、recoil)Vue3 -> Pinia
- 具体方案落地
- 结果反思,细节优化思考
大家先看这段描述:
技术栈:Java、Vue2、echarts、WEui、BaiduMap、JavaScript 、HTTP数据库:MySQL管理工具:SVN
责任描述:
1)产品前端研发负责人,主要负责整体样式沟通,样式调配实现;门户、管理平台、移动端、智端、可视化等前端内容实现;
2)实现组织管理、人员管理、党员关系转接、待办通知、绩效考核(复杂功能算法实现)、发展党员(25个流程)、可视化等主体功能,兼容性优化、适配1920*1080屏幕以及响应式布局实现;
3)相关功能开发,包含前后端、数据库;
4)门户框架搭建、门户整体设计、后端接口、门户前端 UI 实现等;
5)微信小程序框架搭建、小程序页面设计及开发、知识图谱技术预演;
6 )项目经理工作辅助,包含需求沟通、UI设计沟通、交付材料项目经历整理、前端代码质量管理、部分功能设计。
深度优化一下
工作内容和成果
- 【 架构设计 】参与智慧管理平台整体架构设计、技术选型与方案评审,担任全栈开发,完成相关核心模块
- 【 企微开发 】对接企业微信生态,基于企微 SDK 完成平台支付、消息推送、机器人等功能开发
- 【 可视化 】主导完成平台可视化渲染引擎(可视化图表的组件,数据协议)设计与开发,基于 echarts (svgRenderer、canvasRenderer 一千万行数据的表格渲染【不能使用 虚拟滚动 】 canvas table,chunk)封装业务图表库,服务于平台可视化场景
- 【 地图开发 】使用百度地图 SDK,封装业务地图渲染器(MapRenderer),包含:地图撒点、地区数据下钻等功能
- 【 小程序与App 】基于 uniapp 实现智慧党建用户端多端开发落地,产物编译为 H5、微信小程序两端应用
- 【 团队基建 】推进团队业务组件库、图表库与基础库沉淀,完成 10+ 个业务组件沉淀,以此提升了团队协同开发效率
- 【 优化 】设计产品响应式系统,基于 media query 设计响应式端点规则,适配不同端应用的展示
- 【 自研 OA 打通 】...
STAR
【技术栈】
- Java、Vue2、ECharts、WEui、BaiduMap、JavaScript、HTTP
- 数据库 :MySQL
- 管理工具 :SVN
【产品前端研发负责人】
- 情境 (Situation) :担任产品前端研发负责人,负责门户、管理平台、移动端、智端、可视化等前端内容的实现。
- 任务 (Task) :主要任务是与设计团队沟通,调配和实现整体样式,确保产品界面的一致性和用户体验。
- 行动 (Action) :我协调设计与开发团队,定期召开样式沟通会,亲自进行样式的调配与实现,并负责不同平台和设备的前端内容开发。
- 结果 (Result) :成功实现了多个平台的前端开发工作,提升了产品的用户体验和一致性,获得了团队和用户的高度评价。
【实现复杂功能及优化】
- 情境 (Situation) :项目需要实现复杂功能算法和大规模功能模块,包括组织管理、人员管理、党员关系转接、待办通知、绩效考核、发展党员(25个流程)和可视化功能。
- 任务 (Task) :负责上述复杂功能的实现,并优化其兼容性和响应式布局。
- 行动 (Action) :通过设计和实现复杂算法,确保各模块的功能性;进行兼容性优化,使系统适配1920*1080屏幕和响应式布局。
- 结果 (Result) :成功实现并优化了所有复杂功能,系统在不同设备和分辨率下均表现良好,提高了用户操作的流畅度和满意度。
【全面功能开发】
- 情境 (Situation) :需要进行前后端和数据库的全面开发,确保系统各功能模块的无缝集成。
- 任务 (Task) :开发和实现相关功能,包括前端UI、后端接口和数据库交互。
- 行动 (Action) :采用Java、Vue2等技术,开发并调试各功能模块,与后端团队密切合作,确保接口的准确性和数据的一致性。
- 结果 (Result) :成功完成了所有功能模块的开发和集成,系统运行稳定,性能优异,受到了客户的好评。
【微信小程序开发与知识图谱预演】
- 情境 (Situation) :项目需要开发微信小程序,并进行知识图谱技术的预演。
- 任务 (Task) :负责小程序框架的搭建、页面设计及开发,同时进行知识图谱的技术预演。
- 行动 (Action) :使用WEui、JavaScript等技术,设计并开发小程序页面,进行知识图谱的技术预演和验证。
- 结果 (Result) :成功搭建了微信小程序框架,完成了页面设计和开发工作,知识图谱技术预演顺利,通过了技术验证。
【项目经理工作辅助】
- 情境 (Situation) :在项目中辅助项目经理,确保项目需求沟通顺畅,UI设计协调到位,交付材料齐全,前端代码质量高。
- 任务 (Task) :辅助项目经理进行需求沟通、UI设计沟通、交付材料整理、前端代码质量管理以及部分功能设计。
- 行动 (Action) :积极参与需求和UI设计的沟通,整理和管理项目交付材料,进行代码审查和质量管理,并参与功能设计。
- 结果 (Result) :成功辅助项目经理完成了项目的各项工作,提高了项目的开发效率和交付质量,确保了项目的顺利进行。
其实还不够,这些项目才是求职香饽饽
你可能不具备这些项目的实战经验,很多同学写了很多年管理系统,简单增删改查项目,如果不跳出这个圈子,很难在薪资上有非常大的突破!
大厂 UI 组件库(Vue3)整体设计与开发实践(monorepo 架构)
大厂业务 Hooks 库(React 18)整体设计与开发实践(从零到一的架构、规范流程)
企业级脚手架工具开发实践
企业级文档编辑器飞书文档开发实践
前端性能、异常与行为监控
3D 可视化数字孪生低代码实战 💥
- 基于 cesium(arcGis、超图) 方案的 WebGIS 开发实践
- 基于 openlayer、mapbox 开发
- 基于 WebGL 3D 可视化开发实践
年包 50W+ 薪资长线规划
核心要素
全面的技术储备
框架基础
- Vue 和 React 经验:包括 Vue3 + Typescript 和 React18(Hooks、Concurrent)
- 掌握框架原理:React 生态库(React-Router、Redux)和 Vue 生态库原理
工程化能力
- 构建工具:Webpack、Vite、Rspack、ESBuild、swc
- CI/CD 自动化:自动化构建和自动化部署
基建能力
- Node.js、命令行工具开发(Cli)
- UI 库、图表库、工具库开发
业务领域经验
- 管理系统和图表类应用
- 可视化、编辑器、云表格、低代码平台、SaaS 产品、数字孪生、三维可视化
项目经验
- 参与过至少两个大型项目,并主导过一个复杂项目
- 管理系统:包括项目搭建、技术方案选择、技术栈构建、CI/CD 流程
- 其他领域项目:如可视化、编辑器、云表格、低代码平台、SaaS 产品、数字孪生、三维可视化
面试表现
个人介绍
- 准备个人介绍草稿,涵盖基本信息、技术栈和项目重难点
STAR 法则
- 问题描述:阐述遇到的问题或需求
- 解决方案评估:方案对比与选择(如 React 状态管理:redux、mobx、jotai、recoil,Vue3 使用 Pinia)
- 方案实施:具体的实施步骤
- 反思与优化:项目反思及优化建议
面试准备
- 重点复习知识点:如 v8 内存管理、Promise A+ 规范、事件循环、this、面向对象编程原型
- 技术储备:结合项目经验展示技术在项目中的应用
学历提升
- 现有学历:大专
- 未来计划:尽快取得本科证书(不需要注明具体年限)
- 学历背景:民办学校
- 内推建议:应对学历和工作经历问题,通过内推提升机会
补足短板
项目简单,管理后台一做就是大半年,天天 CRUD
- 看开源项目(react-hook-form ts 类型、hook 处理、状态管理、架构 Provider,keyPath)
- 找一些不错的项目练手
- 拿好的项目,学习完放在简历里, 可视化、编辑器、云表格、低代码、SaaS 产品、数字孪生、三维可视化
- 1.自研:15K, 2.外包:20K ,这个火坑, 灰(供需决定)
技术栈掌握不深不广,只会用 Vue2 (Vue3 + Typescript),React18(React stack reconciler)
架构、方案设计没碰过
- React,create-react-app、umi,没有真正从零到一去设计初始化过一个项目
- Vue,Vue CLI,Vite/Webpack。Vue2 CLI 创出来项目 1. webpack、2.vite
没有专精的技能或业务
- 自驱 (假定自己是 Leader),项目的赢利点、商业价值【 可视化、编辑器、白板、团队基建、AI产品 】
- 与生俱来有些东西,好奇心、自驱力、清晰规划
- 我:管理平台,HPE(官网、后台管理),云表格(维格表、飞书云表格)、云编辑器(CKEditor 老【html string】)(语雀、【石墨文档】)
妙码学院——全程陪跑、督学、内推
⚠️ 暂不支持的文档区域,【文档小组件】
- 跨端开发 Taro(Taro 编译器 compiler、运行时 runtime)、uniapp
- 协同编辑器(文档类、画板类)wangEditor、CKEditor
- 团队基建工程(UI 库、图表库、Cli -> 产生产物 npm 包)
- 3D 可视化数字孪生 bigdata
- 低代码平台
课程体系 2.0 升级,除了基础知识夯实,融入了更多项目实战内容,包含:
企业级脚手架工具开发实践
企业级文档编辑器飞书文档开发实践
大厂 UI 组件库(Vue3)整体设计与开发实践
大厂业务 Hooks 库(React 18)整体设计与开发实践
埋点与数据监控平台实战
3D 可视化数字孪生实战 💥
- 基于 cesium 方案的 WebGIS 开发实践
- 基于 WebGL 3D 可视化开发实践
妙码学院全网独家项目实战矩阵
所有项目实战,均完全 从零到一手写 , 纯原创 ,项目架构与编码规范真一线大厂级。
微前端在分拆原子应用场景下的落地与实践
协同编辑器从零到一架构实现
推动团队基建落地
- React UI 库
- Vue Composition API 库
- 企业级脚手架
数字孪生平台整体架构设计
企业级无代码可视化平台实践(Vue3)
低代码平台设计与实现
- 编排引擎
- 流程引擎
- 编辑器
- 代码执行器与 JavaScript 沙箱
用户行为分析 SDK 及监控可视化整体实现
可视化渲染引擎设计与实现
基于 RAG / Agent 前端 AI 应用流程编排平台
不同阶段对前端人的硬性要求
以下是对您提供的内容进行权威和专业化改写的建议:
1~3年
在此阶段,重点在于评估个人的基础知识和热情。对前端基础、计算机原理、网络通信和算法等领域的要求较高。由于在此阶段难以评估业务深度,因此更多关注基础知识的掌握程度。
- 关键在于通过学术教育或网络资源加强基础知识;
- 在简历中以多种方式展示对前端的热情,展现个人潜力;
- 积极探索前沿技术,关注国内外技术动态;
- 尝试开发小型项目或参与社区开源项目;
- 建立技术博客,以输出促进知识吸收。
3~5年
此阶段通常是向成为独立工程师发展的关键时期,避免重复使用有限的经验。
关注社区中关于进阶的资料和路线,强化基础知识;
深入掌握常用框架的高级用法,探索其原理;
在业务开发中不仅完成功能,还需考虑项目结构设计、封装基础工具、设计和开发基础组件;
思考提高团队效率的方法,例如:
- 集成代码检验和风格统一插件(如 eslint、stylelint、prettier、spellcheck);
- 从工程化角度提高本地开发效率,优化webpack构建,探索esbuild、vite等工具;
- 对于多项目开发,整理差异和统一部分,建立内部脚手架以减少重复工作;
- 尝试搭建CI/CD平台,维护公司内部的通用npm包;
- 培养软技能,如沟通协作,协调各角色共同推进目标。
5年以上
进入此阶段,可能朝技术专家或管理方向发展。期望您能够独立负责高复杂度项目,突破关键技术难题。
- 负责技术调研,关注行业趋势,选择最优技术方案,具备决策能力;
- 拥有丰富的技术经验和技术储备,能够解决遇到的困难,并有自己的方法论;
- 协助或主导业务目标制定,合理推动项目达成预期效果;
- 是否具有团队领导经验,能够协调跨团队项目,处理团队成员情绪问题,解决技能分布不平衡等问题;
- 打造技术氛围,促进团队共同成长。
职业规划指导
评论区选取三位同学互动,其他同学也可以联系咨询老师,文字方式提供辅导解答。
【未知组件reminder】
