当前位置: 首页 > news >正文

【小程序 - 大智慧】Expareser 组件渲染框架

5.png


目录

  • 问题思考
  • 课程目标
  • Web Component
    • 类型说明
    • 定义组件
    • 属性添加
  • Shadow DOM
  • Template and Slot
  • Exparser 框架原理
    • 自定义组件
    • 内置组件
  • 下周计划


问题思考

首先,给大家抛出去几个问题:

  1. 前端框架 Vue React 都有自己的组件库,但是并不兼容,那么 不依赖框架能 自定义组件 吗?
  2. 微信小程序开发的时候都会自定义组件是吧,那么调试控制台出现的 shadow-root 是什么,有注意吗?
  3. 微信小程序编写 wxml 的时候,为什么和 html 语法不一致,多出来 view text 这些标签,里面究竟是如何实现的,彼此有什么关联?

课程目标

通过本节课程的学习,希望大家掌握如下的目标:

  1. 弄懂上述问题背后的执行逻辑
  2. 能够利用原生 Web Component 自定义一个简易的组件

Web Component

使用自定义元素 - Web API | MDN

Web Component直译过来就是 web 组件的意思,就是说明离开了前端框架的帮助,我们依然可以用原生组件来进行开发复用。

类型说明

如同官网所说,继承特定元素类得到的组件是 自定义内置元素组件(可以得到特定类型的属性和方法),继承元素基类得到的组件是 独立自定义元素,本质上两种没什么区别,接下来我们重点就放在第二个上面。

定义组件

<button is="my-button-one">内置按钮</button>
<my-button-two></my-button-two>// 01 定义一个内置元素的按钮
class MyButtonOne extends HTMLButtonElement {constructor() {self = super();}// 元素添加到文档调用connectedCallback() {// 1.创建一个 divconst div = document.createElement("div");// 2.设置 div 的样式div.style.width = "100px";div.style.height = "50px";div.style.textAlign = "center";div.style.lineHeight = "50px";div.style.cursor = "pointer";self.style.marginBottom = "20px";// 3.设置 div 的内容div.innerHTML = "自定义按钮";// 4.将 div 添加到页面self.appendChild(div);}
}// 02 定义一个自定义的按钮
class MyButtonTwo extends HTMLElement {constructor() {// 先调用父类构造器,实例化 HTMLElement ,这样才能有 html 元素的基本属性super();}// 元素添加到文档调用connectedCallback() {console.log("自定义元素添加到页面", this);// 1.创建一个 divconst div = document.createElement("div");// 2.设置 div 的样式div.style.width = "100px";div.style.height = "50px";div.style.backgroundColor = "red";div.style.color = "white";div.style.textAlign = "center";div.style.lineHeight = "50px";div.style.cursor = "pointer";// 3.设置 div 的内容div.innerHTML = "自定义按钮";// 4.将 div 添加到页面this.appendChild(div);}// 元素从文档中移除时调用disconnectedCallback() {console.log("自定义元素从页面移除");}// 元素被移动到新文档时调用adoptedCallback() {console.log("自定义元素被移动到新文档");}// 监听属性变化attributeChangedCallback(name, oldValue, newValue) {console.log(`属性 ${name} 已由 ${oldValue} 变更为 ${newValue}`);}
}// 组件注册
customElements.define("my-button-one", MyButtonOne, { extends: "button" });
customElements.define("my-button-two", MyButtonTwo);// 监听组件状态
customElements.whenDefined("my-button-two").then(() => {console.log("my-button-two 组件已定义");
});

自定义组件的命名规则是有限制的:

  • 自定义元素的名称,必须包含短横线(-)。它可以确保html解析器能够区分常规元素和自定义元素,还能确保html标记的兼容性。
  • 自定义元素只能一次定义一个,一旦定义无法撤回。
  • 自定义元素不能单标记封闭。比如 <custom-component />,必须写一对开闭标记。比如 <custom-component></custom-component>

上面两个就是最基本的自定义组件,但是这个也没有样式 class 属性传值 事件方法都没有,下面我们一步步加上。

属性添加

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><style>* {margin: 0;padding: 0;}body {width: 100px;margin: 200px auto;background-color: #f5f5f5;}.my-button-two {width: 180px;height: 50px;background-color: red;color: white;text-align: center;line-height: 50px;cursor: pointer;}</style><my-button-two color="pink" text="Custom Component" @click="clickButton()"></my-button-two><title>02_属性添加</title></head><body><script>// 自定义方法const clickButton = () => {alert("点击了自定义按钮");};class MyButtonTwo extends HTMLElement {// 监控属性变化static observedAttributes = ["color", "text", "@click"];constructor() {// 先调用父类构造器,实例化 HTMLElement ,这样才能有 html 元素的基本属性super();}// 元素添加到文档调用connectedCallback() {// 1.创建一个 divconst div = document.createElement("div");// 2.设置 div 的样式div.className = "my-button-two";// 3.设置 div 的内容const bgColor = this.getAttribute("color");const textValue = this.getAttribute("text");const clickValue = this.getAttribute("@click");// 需要在同一个 js 执行环境内部执行div.addEventListener("click", () => {eval(clickValue);});div.style.backgroundColor = bgColor;div.innerHTML = textValue;// 4.将 div 添加到页面this.appendChild(div);}// 元素从文档中移除时调用disconnectedCallback() {console.log("自定义元素从页面移除");}// 元素被移动到新文档时调用adoptedCallback() {console.log("自定义元素被移动到新文档");}// 监听属性变化attributeChangedCallback(name, oldValue, newValue) {console.log(`属性 ${name} 已由 ${oldValue} 变更为 ${newValue}`);}}customElements.define("my-button-two", MyButtonTwo);// 监听组件状态customElements.whenDefined("my-button-two").then(() => {console.log("my-button-two 组件已定义");});</script></body>
</html>

对着调试控制台我们可以发现,当前 html 写的样式可以影响到组件内部,这并不符合我们之前说的组件和外部彼此 属性隔离 的特点,这就需要了解到下一个概念了。

Shadow DOM

使用影子 DOM - Web API | MDN

影子 DOM(Shadow DOM)允许你将一个 DOM 树附加到一个元素上,并且使该树的内部对于在页面中运行的 JavaScriptCSS 是隐藏的。

在这里插入图片描述

有一些 影子 DOM 术语 需要注意:

  • 影子宿主(Shadow host):影子 DOM 附加到的常规 DOM 节点。
  • 影子树(Shadow tree):影子 DOM 内部的 DOM 树。
  • 影子边界(Shadow boundary):影子 DOM 终止,常规 DOM 开始的地方。
  • 影子根(Shadow root):影子树的根节点。

这里的 影子宿主(Shadow host) 可以选取普通的 div 标签,但是由于我们是自定义元素,这里的 挂载节点 就是 自定义组件 Web Component 了,接下来我们举一个例子:

const shadow = this.attachShadow({ mode: "open" });// 这里的 this 就是标识 自定义组件 DOM 元素
// mode 分为 open closed 表示能否通过 dom.shadowRoot 获取
// 不能获取的话,只能在内部通过 shadow 访问了
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><style>* {margin: 0;padding: 0;}body {width: 100px;margin: 200px auto;background-color: #f5f5f5;}.my-button-two {width: 180px;height: 50px;background-color: red;color: white;text-align: center;line-height: 50px;cursor: pointer;}</style><my-button-two color="pink" text="Custom Component" @click="clickButton()"></my-button-two><title>03_shadow dom</title></head><body><script>// 自定义方法const clickButton = () => {alert("点击了自定义按钮");};class MyButtonTwo extends HTMLElement {// 监控属性变化static observedAttributes = ["color", "text", "@click"];constructor() {// 先调用父类构造器,实例化 HTMLElement ,这样才能有 html 元素的基本属性super();}// 元素添加到文档调用connectedCallback() {// 隔离 DOMconst shadow = this.attachShadow({ mode: "open" });// 1.创建一个 divconst div = document.createElement("div");// 2.设置 div 的样式div.className = "my-button-two";// 3.设置 div 的内容const bgColor = this.getAttribute("color");const textValue = this.getAttribute("text");const clickValue = this.getAttribute("@click");// 需要在同一个 js 执行环境内部执行div.addEventListener("click", () => {eval(clickValue);});div.style.backgroundColor = bgColor;div.innerHTML = textValue;// 4.将 div 添加到页面shadow.appendChild(div);}// 元素从文档中移除时调用disconnectedCallback() {console.log("自定义元素从页面移除");}// 元素被移动到新文档时调用adoptedCallback() {console.log("自定义元素被移动到新文档");}// 监听属性变化attributeChangedCallback(name, oldValue, newValue) {console.log(`属性 ${name} 已由 ${oldValue} 变更为 ${newValue}`);}}customElements.define("my-button-two", MyButtonTwo);// 监听组件状态customElements.whenDefined("my-button-two").then(() => {console.log("my-button-two 组件已定义");});</script></body>
</html>

这里我们可以看到 文档的样式已经无法影响我们的自定义组件了,这是因为被 shadow 阻隔了,接下来就可以继续完善这段逻辑了。

Template and Slot

使用模板和插槽 - Web API | MDN

前端组件开发中有两套我们熟悉的 Template(模板)和 Slot(插槽),接下来就利用这两个功能继续完善一下我们的代码逻辑。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><style>* {margin: 0;padding: 0;}body {width: 100px;margin: 200px auto;background-color: #f5f5f5;}.my-button-two {width: 180px;height: 50px;background-color: red;color: white;text-align: center;line-height: 50px;cursor: pointer;}</style><template id="button-template"><style>.my-button-two {width: 180px;height: 50px;background-color: red;color: white;text-align: center;line-height: 50px;cursor: pointer;}</style><div class="my-button-two"><slot name="text"></slot></div></template><my-button-two id="my-button-two" color="pink" @click="clickButton()"><span slot="text">Custom Component</span></my-button-two><title>04_Tempalte and Slot</title></head><body><script>// 自定义方法const clickButton = () => {alert("点击了自定义按钮");};class MyButtonTwo extends HTMLElement {// 监控属性变化static observedAttributes = ["color", "text", "@click"];constructor() {// 先调用父类构造器,实例化 HTMLElement ,这样才能有 html 元素的基本属性super();}// 元素添加到文档调用connectedCallback() {// 隔离 DOMconst shadow = this.attachShadow({ mode: "closed" });// 1.获取模板const template = document.querySelector("#button-template");// 2.克隆模板const content = template.content.cloneNode(true);// 3.显示文本const clickValue = this.getAttribute("@click");// 4.执行函数const clickEvent = content.querySelector(".my-button-two");clickEvent.addEventListener("click", () => {eval(clickValue);});// 5.将 template 添加到页面shadow.appendChild(content);}// 元素从文档中移除时调用disconnectedCallback() {console.log("自定义元素从页面移除");}// 元素被移动到新文档时调用adoptedCallback() {console.log("自定义元素被移动到新文档");}// 监听属性变化attributeChangedCallback(name, oldValue, newValue) {console.log(`属性 ${name} 已由 ${oldValue} 变更为 ${newValue}`);}}customElements.define("my-button-two", MyButtonTwo);// 监听组件状态customElements.whenDefined("my-button-two").then(() => {console.log("my-button-two 组件已定义");});</script></body>
</html>

艺龙酒店科技官网

举例 video 标签就是利用这套机制封装的…

Exparser 框架原理

Exparser 是微信小程序的组件组织框架,内置在小程序基础库中,为小程序提供各种各样的组件支撑。内置组件和自定义组件都有 Exparser 组织管理。

Exparser 的组件模型与 WebComponents 标准中的 Shadow DOM 高度相似,Exparser 会维护整个页面的节点树相关信息,包括节点的属性、事件绑定等,相当于一个简化版的 Shadow DOM 实现。Exparser 的主要特点包括以下几点:

  • 基于 Shadow DOM 模型:模型上与 WebComponentsShadow DOM 高度相似,但不依赖浏览器的原生支持,也没有其他依赖库;实现时,还针对性地增加了其他 API 以支持小程序组件编程。
  • 可在纯 JS 环境中运行:这意味着逻辑层也具有一定的组件树组织能力。
  • 高效轻量:性能表现好,在组件实例极多的环境下表现尤其优异,同时代码尺寸也较小。

自定义组件

上图是小程序利用 shadow dom 实现 样式和JS 逻辑隔离的组件,这只是第一层,里面的 view text 也是由 Exparser 从普通 div span 封装得来的,接下来让我们深入了解下:

内置组件

接下来带大家一步步过一遍微信小程序内置组件是如何渲染的

// 1.在微信开发工具找到解析命令 wcc
// wcc 是将 wxml 解析为 js 文件,然后逻辑线程注入 webview 执行的
微信web开发者工具\code\package.nw\node_modules\wcc-exec// 2.将命令文件移动到文件目录下,开始执行解析
./wcc -js index.wxml >> dom.js

  • 可以看到本质上就是一个封装好的 $gwx 函数,它的作用是生成微信自定义的组件和虚拟 dom 节点( diff 算法),用来给后面的 Exparser 生成真实的 DOM 节点
  • 那这个函数是在哪里调用的呢,我们继续向下看
// 1. 调试控制台打开当前页面的 webview
document.getElementsByTagName('webview')
document.getElementsByTagName('webview')[0].showDevTools(true, null)// 2. 可以发现编译后的 wxml 会利用 js 脚本以一定格式插入到页面中执行
var decodeName = decodeURI("./pages/command_component/index.wxml")
var generateFunc = $gwx(decodeName)generateFunc()// 3.传入数据
generateFunc({logs:[1,2,3]})


<view wx:for="{{ logs }}" wx:key="index"><text>{{ item }}</text>
</view>

可以看到如上图所示的虚拟节点数组,接下来我们详细剖析一下

  1. $gwx(decodeName) 不直接返回 dom 树,而是返回一个函数的原因是因为需要动态注入和相关配置,函数能够很好的把控时机
  2. 利用动态传参我们发现,包含循环数组和 key 的会带有 virtual 标识,用来后面的 DIff 算法比较
  3. document.dispatchEvent 触发自定义事件 将 generateFunc 当作参数传递给底层渲染库

  1. 可以看得到无论是 view 还是 text 底层都是通过 div span 的自定义组件构成的
  2. 这一切来源于 Exparser 框架,在 渲染层 会内置一系列方法,大致和上面自定义 web component 一致,进行对组件的定义,注册后将 js 脚本引入页面,那么当前页面就可用了
  3. 接下来带大家进行源码的拆解

下周计划

  1. 继续深入小程序原理(收益不高)
  2. 扩展前端其他的技术方向(感兴趣建议)
    1. 前端组件库实现拆解
    2. 前端调试能力提升
    3. 前端工程化能够了解

相关文章:

【小程序 - 大智慧】Expareser 组件渲染框架

目录 问题思考课程目标Web Component类型说明定义组件属性添加 Shadow DOMTemplate and SlotExparser 框架原理自定义组件内置组件 下周计划 问题思考 首先&#xff0c;给大家抛出去几个问题&#xff1a; 前端框架 Vue React 都有自己的组件库&#xff0c;但是并不兼容&#…...

vue + echarts 快速入门

vue echarts 快速入门 本案例即有nodejs和vue的基础&#xff0c;又在vue的基础上整合了echarts Nodejs基础 1、Node简介 1.1、为什么学习Nodejs(了解) 轻量级、高性能、可伸缩web服务器前后端JavaScript同构开发简洁高效的前端工程化 1.2、Nodejs能做什么(了解) Node 打破了…...

服务器几核几G几M是什么意思?如何选择?

服务器几核几G几M是什么意思&#xff1f;我们建站、搭建网络平台都要用到云服务器&#xff0c;不管在腾讯云、阿里云还是别的云服务平台选购&#xff0c;都会接触到服务器配置。云服务器就是把物理服务器&#xff08;俗称“母鸡”&#xff09;&#xff0c;用虚拟机技术虚拟出多…...

K8S服务发布

一 、服务发布方式对比 二者主要区别在于&#xff1a; 1、部署复杂性&#xff1a;传统的服务发布方式通常涉及手动配置 和管理服务器、网络设置、负载均衡等&#xff0c;过程相对复 杂且容易出错。相比之下&#xff0c;Kubernetes服务发布方式 通过使用容器编排和自动化部署工…...

Allen Institute for Artificial Intelligence (Ai2) 发布开源多模态语言模型 Molmo

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

Html CSS 布局,位置处理 居中 对齐

Html CSS 布局&#xff0c;位置处理 1、居中布局 1、div 让内部div居中对齐 html <div class"container"><div class"item">I am centered!</div> </div>style .container {border: 2px solid rgb(75, 70, 74);border-radius:…...

Spring MVC系统学习(二)——Spring MVC的核心类和注解

Spring MVC&#xff08;Model-View-Controller&#xff09;是Spring框架的一个模块&#xff0c;用于构建基于Web的应用程序。它使用模型、视图和控制器分离的设计模式&#xff0c;使得Web开发更加模块化和灵活。在学习Spring MVC时&#xff0c;有几个核心类和注解是非常关键的&…...

conda虚拟环境安装包、依赖同一管理

在 Python 的虚拟环境中&#xff0c;每个环境都是独立的&#xff0c;这意味着即使两个环境需要相同的库&#xff0c;它们也会分别安装各自的副本。这样做是为了避免不同项目之间相互影响&#xff0c;确保每个项目都有一个干净且隔离的环境。 方法一&#xff1a;使用 Conda 的共…...

Unity网络开发记录(四):在unity中进一步封装客户端类

在上一篇文章中&#xff0c;简单的封装了一下服务端中相关的socket对象&#xff0c;为了可以更方便的使用。所以在本篇中&#xff0c;进一步封装一下在unity中的相关客户端类 封装客户端类&#xff0c;首先采用单例模式&#xff0c;然后采用两个队列来存储我们相关的收发信息 p…...

Linux内核中的UART驱动-详解Linux内核UART驱动:结构与功能分析

一、UART概述 UART&#xff08;Universal Asynchronous Receiver/Transmitter&#xff09;&#xff0c;即通用异步收发器&#xff0c;是一种串行通信接口&#xff0c;用于在计算机和外部设备之间传输数据。它特别适用于短距离、低速、串行和全双工的数据传输。在Linux内核中&a…...

威胁检测与防范:如何及时、准确对抗安全风险

随着技术的飞速发展&#xff0c;网络空间中的威胁日益多样化、隐蔽化&#xff0c;给个人、企业乃至国家的信息安全带来诸多挑战。面对严峻的网络威胁&#xff0c;传统的防火墙、入侵检测系统&#xff08;IDS&#xff09;等防御手段虽能在一定程度上抵御外部攻击&#xff0c;但依…...

数据结构串的kmp相关(求next和nextval)

傻瓜版&#xff0c;用来演示手算过程&#xff0c;个人理解用的&#xff0c;仅供参考。...

创建游戏暂停菜单

创建用户控件 设置样式 , 加一层 背景模糊 提升UI菜单界面质感 , 按钮用 灰色调 编写菜单逻辑 转到第三人称蓝图 推荐用 Set Input Mode Game And UI , 只用仅UI的话 增强输入响应不了 让游戏暂停的话也可以用 Set Game Paused , 打勾就是暂停 , 不打勾就是继续游戏 , 然后…...

seata服务端部署

1.下载seata 官网下载地址&#xff1a;http://seata.io/zh-cn/blog/download.html 或者下载 作者已经下载的压缩包1.4.0 注意&#xff01;&#xff01;&#xff01; 要参考对应的版本&#xff0c;否则可能出现无法正常启动的情况。 参考文档 下载完毕后解压压缩文件 2.修改配…...

理解Python闭包概念

闭包并不只是一个python中的概念&#xff0c;在函数式编程语言中应用较为广泛。理解python中的闭包一方面是能够正确的使用闭包&#xff0c;另一方面可以好好体会和思考闭包的设计思想。 1.概念介绍 首先看一下维基上对闭包的解释&#xff1a; 在计算机科学中&#xff0c;闭包…...

51单片机的教室智能照明系统【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块DS1302时钟模块光照传感器红外传感器温度传感器LED等模块构成。适用于教室灯光全自动控制、教室节能灯控制、教室智能照明等相似项目。 可实现功能: 1、LCD1602实时显示时间、温度、光照强度等信息 2、光照强度传…...

一款资产进行快速存活验证工具

01工具介绍 &#xff08;下载地址见最后&#xff09; 在日常工作的渗透测试过程中&#xff0c;经常会碰到渗透测试项目&#xff0c;而Web渗透测试通常是渗透项目的重点或者切入口。通常拿到正规项目授权后&#xff0c;会给你一个IP资产列表和对应的Web资产地址&#xff0c;这时…...

I/O中断处理过程

中断优先级包括响应优先级和处理优先级&#xff0c;响应优先级由硬件线路或查询程序的查询顺序决定&#xff0c;不可动态改变。处理优先级可利用中断屏蔽技术动态调整&#xff0c;以实现多重中断。下面来看他们如何运用在中断处理过程中&#xff1a; 中断控制器位于CPU和外设之…...

关于PHP 匿名函数在处理数据结构中的应用

PHP 的匿名函数&#xff08;也称为闭包&#xff09;在处理数据结构时非常有用。它们可以在需要一次性函数的情况下使用&#xff0c;例如数组函数的回调、事件处理或作为其他函数的参数。以下是一些常见的应用场景&#xff1a; 数组操作&#xff1a; 使用 array_map、array_fil…...

安卓13默认使用大鼠标 与配置分析 andriod13默认使用大鼠标 与配置分析

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.彩蛋1.前言 android13里面的鼠标貌似比以前版本的鼠标小了,有些客户想要把这个鼠标改大。这个功能,android有现成的,就在这里,设置 =》无障碍 =》色彩和动画 =》 大号鼠标指针。 我们通过…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

基于PHP的连锁酒店管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...

android RelativeLayout布局

<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...

【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验

Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...

深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向

在人工智能技术呈指数级发展的当下&#xff0c;大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性&#xff0c;吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型&#xff0c;成为释放其巨大潜力的关键所在&…...

【WebSocket】SpringBoot项目中使用WebSocket

1. 导入坐标 如果springboot父工程没有加入websocket的起步依赖&#xff0c;添加它的坐标的时候需要带上版本号。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dep…...