Web Components学习(1)
一、什么是web components
开发项目的时候为什么不手写原生 JS,而是要用现如今非常流行的前端框架,原因有很多,例如:
- 良好的生态
- 数据驱动试图
- 模块化
- 组件化
- 等
Web Components 就是为了解决“组件化”而诞生的,它是浏览器原生支持的组件化,不依赖任何库、依赖和打包工具就可以在浏览器中运行。
Vue、React 的组件化并不是真正的组件化,虽然写代码时写的是确实的组件化代码,但是编译后就不再是组件化了。
例如用 Vue + ElementUI 开发的应用,ElementUI 的组件都是 el 开头的,如 <el-button>,但编译后显示在页面上的就不再是 <el-button> 标签了。
这有点类似于 CSS 预处理器(如 Sass、Less),那些在开发阶段编译的变量(如 $color: red;)其实并不是真正的变量,而是伪变量。在编译过后就没有变量的概念了,所以很难和 JS 通信。
例如有一个需求,在页面上给用户提供一个输入框,用户输入什么颜色(如 red、#ff00000),网站就会变成相应颜色的主题色,可是在我们获取到用户输入后,却没有变法将它们赋值给 Sass 变量上去。因为 Sass 代码在编译后已经变成了 CSS 代码,没有 Sass 变量了,例如 color: $color; 编译为 color: red;。
所以此时就需要一个浏览器原生就支持的,不需要编译就能够运行的变量,于是 CSS 变量就出现了(--color: red;、color: var(--color)),可以非常方便地与 JS 进行通信,因为它是浏览器级别地原生变量。
同理,框架的组件化也不是真正的标准,每家都用自己的组件化标准,这就导致了生态的分裂,而且这些框架的组件化也都是靠编译才能实现的,并且非常依赖于这个框架,是一种共生的关系,就像使用 Vue 时,后缀以 .vue 结尾的文件,根本没有办法在浏览器中运行,必须下载 Node、Webpack、vue-loader 等工具进行打包,但还是无法在脱离 Vue 这个框架的安装包的情况下进行运行。
通常来说,浏览器厂商会吸收一些前端非常流行框架之中的可取之处,然后推动其成为标准,并在浏览器中原生实现这些功能,最经典的莫过于 jQuery 的 $() 选择器。
“都 21 世纪了,还提 jQuery?”
尽管这几年风生水起的 Vue 和 React 加剧了 jQuery 的没落,但全世界仍有超过 6600 万个网站在使用 jQuery,同时 jQuery 也给业界留下了产生深远影响的遗产,W3C 就仿照$()函数实现了 querySelector() 和 querySelectorAll() 方法。
而讽刺的是,也正是这两个原生方法的出现,大大加快了 jQuery 的没落,因为它们取代了 jQuery 最常用的功能之一:快捷的选择 DOM 元素。
那么浏览器原生支持的组件化会取代现在所流行的库或框架么?
那么事实真的是这样么,其实不然。
Web Components 与如今非常流行的 MVVM 框架是一种共存的关系,而不是一种互斥的关系,就像 Sass 变量和 CSS 变量,两者可以非常完美的互补,而不是说用了 CSS 变量就不能用 Sass 变量。
再者来说,我们用那些 MVVM 框架也并不仅仅只是为了它们的组件化功能,虽然组件化是其中非常重要的一项功能,但是还有页面路由、数据绑定、模块化、CSS 预处理器、虚拟 DOM、Diff 算法,以及各种庞大的生态等功能。
Web Components 要解决的仅仅只是组件化的这么一项功能。
React 和 Web Components 为了解决不同的问题而生。Web Components 为可复用组件提供了强大的封装,而 React 则提供了声明式的解决方案,使 DOM 与数据保持同步。两者旨在互补。作为开发人员,可以自由选择在 Web Components 中使用 React,或者在 React 中使用 Web Components,或者两者共存。
—— 《Web Components – React》
我们认为 Vue 和 Web Components 主要是互补的技术。Vue 为使用和创建定制元素提供了出色的支持。无论你是将自定义元素集成到现有的 Vue 应用程序中,还是使用 Vue 来构建和分发自定义元素都很方便。
—— 《Vue 与 Web Components | Vue.js》
Web Components 是一个浏览器原生支持的组件化方案,允许你创建新的自定义、可封装、可重用的HTML 标记。不用加载任何外部模块,直接就可以在浏览器中跑。
它的出现原因?因为早期组件生态很乱,有各种各样的框架和库,都有各自的规范,导致一个团队切换框架很困难 。为了解决这种分化的形式,让 Web 组件模型统一化,所以才有了Web Components规范的出现。目标是提供一种权威的、浏览器能理解的方式来创建组件。
为什么要学习它?用一句话总结就是回顾历史展望未来。
2011年提出Web Components概念,React诞生
2013年 Chrome 和 Opera 又联合提出了推出的 V0 版本的 Web Components 规范,React开源
2014年Vue诞生
2016年Web Components 推进到了 V1 版本
由于浏览器兼容性、和主流框架开发效率等等问题导致现在几乎使用不到它,但我们可以学习它的思想,也许未来就会变的有用?
Web Components 由三种技术组成
Custom Elements
可以创建一个自定义标签。根据规范,自定义元素的名称必须包含连词线”-“,用与区别原生的 HTML 元素。
<body><user-card></user-card><script>class UserCard extends HTMLElement {constructor() {super();var el = document.createElement('p');el.classList.add('name');el.innerText = 'User Name';this.append(el);}}window.customElements.define('user-card', UserCard);</script>
</body>

class UserButton extends HTMLButtonElement {constructor() {super();}
}customElements.define('user-button', UserButton, { extends: "button" });
<button is="user-button"></button>
使用生命周期回调函数
在custom element的构造函数中,可以指定多个不同的回调函数,它们将会在元素的不同生命时期被调用:
connectedCallback:当 custom element首次被插入文档DOM时,被调用。disconnectedCallback:当 custom element从文档DOM中删除时,被调用。adoptedCallback:当 custom element被移动到新的文档时,被调用。attributeChangedCallback: 当 custom element增加、删除、修改自身属性时,被调用。
Shadow DOM
经常写video,audio等html元素在带的控制条或者模块,但是这这些模块哪里来的用什么实现的




隐藏有点深刻,难以发现。
那什么是影子DOM
- 影子dom这个东西的存在,主要解决dom树建立时能够实现维护自身边界的问题。这么说有点像vue的scope保证自身不会被外来修饰入侵或者污染。
- 影子dom将对应的dom信息隐藏起来依然能在html文档里渲染出来。但不能通过普通的js方法获取到dom信息
- 影子dom事件捕获遵从常规dom事件,在影子dom内部依然传递,同时也遵从事件冒泡,向整个文档的dom上传递事件。
创建影子树
通过createShadowRoot创建影子树root节点
<!DOCTYPE html>
<html>
<head><title>影子dom</title><link rel="dns-prefetch" href="//dfhs.tanx.com"><style>.box {height: 80px;width: 80px;background-color: red;}</style>
</head>
<body><div id="box" class="box"></div>
</body>
<script>var $box = document.getElementById('box');var shadowRoot = $box.createShadowRoot(); // 获得root//var showRoot = $box.webkitGetShadowRoot() // webkit 支持var children = document.createElement('div');children.setAttribute('style', 'height: 40px; width: 40px; background-color: blue');shadowRoot.appendChild(children);
</script>
</html>

再给影子树节点添加css时不能用过class或者元素选择来添加,否则无效果
<!DOCTYPE html>
<html>
<head><title>影子dom</title><link rel="dns-prefetch" href="//dfhs.tanx.com"><style>.box {height: 80px;width: 80px;background-color: red;}.children {height: 40px;width: 40px;background-color: blue;}div {height: 40px;width: 40px;background-color: blue;}</style>
</head>
<body><video src="test.mp4" height="200px" controls></video><audio src="mp3.mp3" controls></audio><canvas></canvas><div id="box" class="box"></div>
</body>
<script>var $box = document.getElementById('box');var shadowRoot = $box.createShadowRoot(); // 获得root//var showRoot = $box.webkitGetShadowRoot() // webkit 支持var children = document.createElement('div');children.setAttribute('class', 'children');shadowRoot.appendChild(children);
</script>
</html>

通过class选择dom时需要将style也放入影子节点里
<script>var $box = document.getElementById('box');var shadowRoot = $box.createShadowRoot(); // 获得root//var showRoot = $box.webkitGetShadowRoot() // webkit 支持var children = document.createElement('div');children.setAttribute('class', 'children')shadowRoot.innerHTML += '<style>.children { height: 40px; width: 40px; background-color: blue;}</style>';shadowRoot.appendChild(children);
</script>
不能直接获得影子DOM
通过js常规方法不能直接获取到dom节点
var $box = document.getElementById('box');var shadowRoot = $box.createShadowRoot(); // 获得root//var showRoot = $box.webkitGetShadowRoot() // webkit 支持var children = document.createElement('div');children.setAttribute('class', 'children');children.setAttribute('id', 'children');shadowRoot.appendChild(children);// 获得影子dom// 通过idvar getShadowRootById = document.getElementById('children');console.log(getShadowRootById)// 通过节点选择console.log('---------------')var getShadowRootByDomBox = document.body.firstChild.nextSibling; // 获得到box//var getShadowRootByDom = getShadowRootByDomBox.firstChildvar getShadowRootByDom = getShadowRootByDomBox.firstElementChild;console.log(getShadowRootByDom)

影子dom事件绑定
在createElement时拿到的元素,添加addEventListener事件
var $box = document.getElementById('box');var shadowRoot = $box.createShadowRoot(); // 获得root//var showRoot = $box.webkitGetShadowRoot() // webkit 支持var children = document.createElement('div');children.setAttribute('class', 'children')shadowRoot.innerHTML += '<style>.children { height: 40px; width: 40px; background-color: blue;}</style>';shadowRoot.appendChild(children);children.addEventListener('click', function(e) {console.log(e)})
利用content元素select属性将目标内容匹配到template中指定位置,并且目标内容只能在影子元素里
<!DOCTYPE html>
<html>
<head><title>影子dom</title><link rel="dns-prefetch" href="//dfhs.tanx.com"><style>.box {height: 160px;width: 160px;background-color: red;}.children {height: 80px;width: 80px;background-color: blue;}.test-content {background-color: yellow;}</style>
</head>
<body><div id="box" class="box"><div class="test-content">我接着测试</div></div><template class="root-tlp"><style>.test-ctn {color: white;}</style><div><div class="test-ctn" id="test">测试</dt></div><content select=".test-content"></content></template>
</body>
<script>var $box = document.getElementById('box');var shadowRoot = $box.createShadowRoot(); // 获得rootvar children = document.createElement('div');var template = document.querySelector('.root-tlp');shadowRoot.appendChild(document.importNode(template.content, true));document.addEventListener('click', function() {console.log('test-content')})
</script>
</html>

CSS 选择器:
:host, :host(), :host-context()
:host
<template class="root-tlp"><style>.test-ctn {color: white;}:host {font-weight: bold;}</style><div><div class="test-ctn" id="test">测试</dt></div><content select=".test-content"></content></template>

:host()选择器,选择影子dom宿主元素
<body><div id="box" class="box"><div class="test-content">我接着测试</div></div><template class="root-tlp"><style>.test-ctn {color: white;}:host {font-weight: bold;}:host(.box) {color: blue;}</style><div><div class="test-ctn" id="test">测试</dt></div><content select=".test-content"></content></template>
</body>

:host-context()与后代选择器表达式一起使用,以仅选择特定祖先内部的自定义元素的实例
<!DOCTYPE html>
<html>
<head><title>影子dom</title><link rel="dns-prefetch" href="//dfhs.tanx.com"><style>.box {height: 160px;width: 160px;}.children {height: 80px;width: 80px;}</style>
</head>
<body><div id="box" class="test"><div class="box-content" id="box-content"><div class="box-ctn">213</div></div></div><template class="root-tlp"><style>.test-ctn {color: white;}:host {font-weight: bold;}:host(.box-content) {color: blue;background-color:red;}:host-context(.test) {height: 300px;background-color: blueviolet}</style><div><div class="test-ctn" id="test">测试</dt></div><content select=".box-ctn"></content></template>
</body>
<script>var $box = document.getElementById('box-content');var shadowRoot = $box.createShadowRoot(); // 获得rootvar children = document.createElement('div');var template = document.querySelector('.root-tlp');shadowRoot.appendChild(document.importNode(template.content, true));
</script>
</html>

templates and slots
可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。
- 被使用前不会被渲染。
- 被使用前对页面其他部分没有影响,脚本不会运行,图像不会加载,音频不会播放。
<body><p>会影响外部样式</p><template id="my-paragraph"><style>p{color: red;}</style><p>My paragraph</p></template><my-paragraph></my-paragraph><script>customElements.define('my-paragraph',class extends HTMLElement {constructor() {super();let template = document.getElementById('my-paragraph');let templateContent = template.content.cloneNode(true);this.appendChild(templateContent);}})</script>
</body>

slot的使用:
<body><style>p{color: blueviolet;}</style><p>会影响外部样式</p><template id="my-paragraph"><style>p{color: red;}</style><p>My paragraph</p><slot name="my-text">My default text</slot></template><my-paragraph><p slot="my-text">slot text</p></my-paragraph><script>customElements.define('my-paragraph',class extends HTMLElement {constructor() {super();let template = document.getElementById('my-paragraph');let templateContent = template.content.cloneNode(true);this.attachShadow({mode: 'open'}).appendChild(templateContent);}})</script>
</body>

下面挑选了市面上既好玩,颜值又高的组件库来体验以下 Web Components:
- css-doodle:直译 - css 涂鸦
- fancy-components:直译 - 花式组件库,可惜没有官方文档
二、css-doodle
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>css-doodle</title><script src="https://cdnjs.cloudflare.com/ajax/libs/css-doodle/0.6.1/css-doodle.min.js"></script><style>html, body {height: 100%;margin: 0;overflow: hidden;}</style>
</head>
<body><css-doodle>:doodle {@grid: 20 / 100vmax;background: #12152f;}::after {content: '\@hex(@rand(0x2500, 0x257f))';font-size: 5vmax;color: hsla(@rand(360), 70%, 70%, @rand(.9))}</css-doodle>
</body>
</html>

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>css-doodle</title><script src="https://cdnjs.cloudflare.com/ajax/libs/css-doodle/0.6.1/css-doodle.min.js"></script><style>html, body {width: 100%;height: 100%;margin: 0;overflow: hidden;background: #011627;display: grid;place-items: center;/* 公众号:前端学不动 搜:居中篇 */}</style>
</head>
<body><css-doodle>:doodle {@grid: 1x1x100 / 100vmin;animation: r 23s linear infinite}@size: 100% 50%;position: absolute;top: 25%;transform: rotate(@r(360deg));perspective: @r(100px, 200px);::after {content: '';position: absolute;@size: @r(.5vmin, 5vmin);color: @p(#fdfffc, #2ec4b6, #e71d36, #ff9f1c);background: currentColor;box-shadow: @m2(0 0 1.2vmin currentColor);animation: cycle @r(2s) linear infinite;--trans: scaleX(@r(1, 5)) translateZ(@r(10vmin, 20vmin));transform: rotateY(0) @var(--trans)}:empty::after { display: none }@keyframes cycle {to {transform: rotateY(@p(-1turn, 1turn)) @var(--trans)}}@keyframes r {to { transform: rotate(1turn) }}</css-doodle>
</body>
</html>

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>css-doodle</title><script src="https://unpkg.com/css-doodle@0.34.5/css-doodle.min.js"></script><style>html, body {width: 100%;height: 100%;margin: 0;overflow: hidden;background: radial-gradient(#459dc1, #070729);display: grid;place-items: center;/* 公众号:前端学不动 搜:居中篇 */}</style>
</head>
<body><css-doodle><!-- css-doodle.com -->:doodle {@grid: 80x1 / 100vw 100vh;@min-size: 100px;filter: url(#filter);animation: r 23s linear infinite}@size: 100% 50%;position: absolute;top: 25%;transform: rotate(@r(360deg));perspective: 130px;::after {content: '';position: absolute;@size: @r(10px);background: #fff;box-shadow: @m3(0 0 calc(.5vmin + 5px) #fff);animation: cycle @r(2s, 8s) linear infinite;animation-delay: -@r(100s);--trans: scaleX(@r(.1, 5)) translateZ(105px);transform: rotateY(0) @var(--trans)}@keyframes cycle {to {transform: rotateY(@p(-1turn, 1turn)) @var(--trans)}}@keyframes r {to { transform: rotate(@p(-1turn, 1turn)) }}</css-doodle><svg style="width: 0; height: 0;"><filter id="filter"><feGaussianBlur in="SourceGraphic" stdDeviation="5" result="blur"></feGaussianBlur><feColorMatrix in="blur" mode="matrix" values="1 0 0 0 00 1 0 0 00 0 1 0 00 0 0 18 -7"result="goo" ></feColorMatrix><feBlend in="SourceGraphic" in2="goo"></feBlend></filter></svg>
</body>
</html>

三、fancy-components
https://github.com/fancy-components/fancy-components
<!DOCTYPE html>
<html lang="en">
<head><title>fancy-components</title><style>* { padding: 0; margin: 0; }html, body { height: 100%; }body {display: grid;place-items: center;background: #3f2766;}fc-3d-btn {--color: #6e50a6;--shadow-color: rgba(255, 255, 255, .4);--inset-shadow-color: #315;--inset-shadow-color-active: rgba(49, 23, 7, .9);--cover-color: rgba(0, 0, 0, .4);}</style>
</head>
<body><div><!-- html 只支持小写标签,不支持驼峰命名法 --><fc-input white placeholder="Username"></fc-input><br /><fc-input white disabled value="fancy components" placeholder="Username"></fc-input><br /><fc-btn>fancy-components</fc-btn><br /><fc-warp-btn></fc-warp-btn><br /><fc-3d-btn></fc-3d-btn><br /><fc-underline-btn></fc-underline-btn><br /><fc-pixel-btn></fc-pixel-btn><br /><fc-parentheses-btn></fc-parentheses-btn><br /><fc-round-btn></fc-round-btn><br /><fc-arrow-btn></fc-arrow-btn><br /><fc-bubbles click><fc-parentheses-btn>撒花</fc-parentheses-btn></fc-bubbles></div><script type="module">import { FcTypingInput } from 'http://unpkg.com/fancy-components'import { FcDblWarpBtn } from 'http://unpkg.com/fancy-components'import { FcWarpBtn } from 'http://unpkg.com/fancy-components'import { Fc3DBtn } from 'http://unpkg.com/fancy-components'import { FcUnderlineBtn } from 'http://unpkg.com/fancy-components'import { FcPixelBtn } from 'http://unpkg.com/fancy-components'import { FcParenthesesBtn } from 'http://unpkg.com/fancy-components'import { FcRoundBtn } from 'http://unpkg.com/fancy-components'import { FcArrowBtn } from 'http://unpkg.com/fancy-components'import { FcBubbles } from 'http://unpkg.com/fancy-components'// 注册组件// 可以传递一个重命名组件名的字符串,必须是小写,且用 `-` 连接// 不传参数默认组件名就是 fc-typing-inputnew FcTypingInput('fc-input')new FcDblWarpBtn('fc-btn')new FcWarpBtn()new Fc3DBtn()new FcUnderlineBtn()new FcPixelBtn()new FcParenthesesBtn()new FcRoundBtn()new FcArrowBtn()new FcBubbles()</script>
</body>
</html>

四、在脚手架中使用 Web Components 组件库
Vue 2 中使用
npm i -g @vue/cli
vue create vue2-app
cd vue2-app
npm i fancy-components
npm run serve
// src\main.js
import Vue from 'vue'
import App from './App.vue'
import { FcBubbles } from 'fancy-components'// 禁用 no-new 校验规则
/* eslint-disable no-new */
new FcBubbles()Vue.config.productionTip = falsenew Vue({render: h => h(App)
}).$mount('#app')
<!-- src\App.vue -->
<template><div id="app"><fc-bubbles click><img alt="Vue logo" src="./assets/logo.png"></fc-bubbles><!-- <FcBubbles click><img alt="Vue logo" src="./assets/logo.png"></FcBubbles> --></div>
</template>
...
Web Components 原生组件的地位和 HTML 标签的地位是相同的,大写的驼峰命名组件会被当做 Vue 组件,原生组件要和 HTML 标签一样,不要写成驼峰命名。React 框架中也一样。
Vue CLI 旧版本中使用 Web Components 控制台可能会发出警告,原因是 Vue 将 原生组件当作 Vue 组件去判断,警告组件没有注册,解决办法就是配置
ignoredElements让 Vue 忽略原生组件:
Vue.config.ignoredElements = [// 正则匹配/^fc-/,// 或字符串'css-coodle'
]
Vue 3 中使用
vue create vue3-app
cd vue3-app
npm i fancy-components
npm run serve
// src\main.js
import { createApp } from 'vue'
import App from './App.vue'
import { FcBubbles } from 'fancy-components'/* eslint-disable no-new */
new FcBubbles()createApp(App).mount('#app')
<!-- src\App.vue -->
<template><fc-bubbles click><img alt="Vue logo" src="./assets/logo.png"></fc-bubbles><HelloWorld msg="Welcome to Your Vue.js App"/>
</template>
使用上如 Vue2 一样,但实际上会报错:
[Vue warn]: Failed to resolve component: fc-bubbles
If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.
原因与 Vue CLI 旧版本创建的 Vue2 应用一样,解决办法依然是配置忽略原生组件(自定义元素),参考:Vue 与 Web Components
// vue.config.js
module.exports = {chainWebpack: config => {config.module.rule('vue').use('vue-loader').tap(options => ({...options,compilerOptions: {// 将所有 fc- 开头的标签名都视为自定义元素isCustomElement: tag => tag.startsWith('fc-')}}))}
}
重启应用,警告已经不见了,但是点击仍然没有生效,打开 Element 面板发现组件的 click 属性并没有添加上,而其他属性如 click1 可以添加,这可能是因为 Vue3 认为 click 是一个不能直接添加的关键字,测试发现只需将 click 改成大写 Click 即可添加上。
<fc-bubbles Click><img alt="Vue logo" src="./assets/logo.png"></fc-bubbles>
在 Vite 中使用 Web Components 组件库
# npm 6.x
npm create vite@latest vite-vue-app
√ Select a framework: » vue
√ Select a variant: » vuecd vite-vue-app
npm install
npm i fancy-components
npm run dev
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import { FcBubbles } from 'fancy-components'new FcBubbles()createApp(App).mount('#app')
<!-- src\App.vue -->
<!-- 注意 Click 大写 -->
<fc-bubbles Click><img alt="Vue logo" src="./assets/logo.png" /></fc-bubbles>
还要配置忽略的自定义元素:
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'// https://vitejs.dev/config/
export default defineConfig({plugins: [vue({template: {compilerOptions: {// 将所有 fc- 开头的标签名都视为自定义元素isCustomElement: tag => tag.startsWith('fc-')}}})]
})
不需要重启即可生效。
相关文章:
Web Components学习(1)
一、什么是web components 开发项目的时候为什么不手写原生 JS,而是要用现如今非常流行的前端框架,原因有很多,例如: 良好的生态数据驱动试图模块化组件化等 Web Components 就是为了解决“组件化”而诞生的,它是浏…...
Element-UI实现复杂table表格结构
Element-UI组件el-table用于展示多条结构类似的数据,可对数据进行排序、筛选、对比或其他自定义操作。将使用到以下两项,来完成今天demo演示:多级表头:数据结构比较复杂的时候,可使用多级表头来展现数据的层次关系。合…...
Azure AD 与 AWS 单一帐户SSO访问集成,超详细讲解,包括解决可能出现的错误问题
本教程介绍如何将 AWS Single-Account Access 与 Azure Active Directory (Azure AD) 相集成。 将 AWS Single-Account Access 与 Azure AD 集成后,可以: 在 Azure AD 中控制谁有权访问 AWS Single-Account Access。让用户使用其 Azure AD 帐户自动登录…...
lvgl 笔记 按钮部件 (lv_btn) 和 开关部件 (lv_switch)
按钮基础使用方法: lv_btn 和 lb_obj 使用方法一样,只是外表并不相同,基础创建方法只需一行代码。 lv_obj_t* btn lv_btn_create(lv_scr_act()); 添加大小和位置: lv_obj_t* btn lv_btn_create(lv_scr_act()); lv_obj_set_s…...
Python高频面试题——生成器(最通俗的讲解)
生成器定义在 Python 中,使用了 yield 的函数被称为生成器(generator)。跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。 在调用生成器运行的过程中…...
品牌软文怎么写?教你几招
软文是什么?软文的本质就是广告,当然不是明晃晃的推销,而是自然隐晦地植入产品信息,引导更多用户自愿下单。 品牌软文对于写手的经验、内容的质量要求都相对较高,否则写出来的软文无法达到预期的效果。品牌软文怎么写…...
Kubernetes (k8s) 污点(Taint)介绍、示例
Kubernetes (k8s) 污点(Taint) 是一种机制,用于标记一个节点(Node)不可被调度的状态。它可以将一个污点标记添加到节点上,以防止 Pod 被调度到该节点上。污点可以用于实现各种策略,例如分离故障…...
Docker学习(二十一)构建 java 项目基础镜像
目录1.下载 JDK 包2.编写 Dockerfile3.构建镜像4.创建容器测试1.下载 JDK 包 JDK各版本官网下载地址: https://www.oracle.com/java/technologies/downloads/archive/#JavaSE 这里我们以 JDK 8u351 为例,点击 Java SE (8U211 and later)。 点击下载 jd…...
python中的上下文原理
目录with语句上下文管理器原理自定义上下文管理器contextmanager 装饰器with语句 在我们日常使用场景中,经常会操作一些资源,比如文件对象、数据库连接、Socket连接等,资源操作完了之后,不管操作的成功与否,最重要的事…...
可复用测试用例描述要素
测试用例的输入、操作、预期结果和评估标准、前提条件是测试用例不可少的要素,但对于可复用测试用例而言,这是不够的。本文在文献规定的测试用例要素基础上,增加了新的内容。从而从多个角度完整地对可复用测试用例进行了描述,为可…...
lnmp中遇到open_basedir配置无效问题
在使用LNMP包安装PHP时,发现直接修改php.ini的配置是无法生效的,其原因竟然是因为nginx的配置文件,覆盖了php.ini的配置。 解决: 1、找到nginx的open_basedir配置文件:(下面是我的文件地址) …...
SpringBoot【知识加油站】---- REST开发
SpringBoot【知识加油站】---- REST开发1. REST 简介2. REST 风格3. RESTful 入门案例1. REST 简介 REST:Representaional State Transfer,表现形式状态转换 传统风格资源描述形式 http://localhost/user/getById?id1 http://localhost/user/saveUser…...
三 Go的语言容器
1. 数组 数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,所以在Go语言中很少直接使用数组 声明语法: var 数组变量名 [元素数量]Typepackage main import ("fmt" ) fu…...
2023年全国最新会计专业技术资格精选真题及答案16
百分百题库提供会计专业技术资格考试试题、会计考试预测题、会计专业技术资格考试真题、会计证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 一、单选题 1.下列各项关于会计监督职能的表述中,正确的是ÿ…...
模板进阶(仿函数,特化等介绍)
非类型模板参数 模板参数有类型形参和非类型形参; 类型形参:使用typename或者class修饰的参数类型名称 非类型形参:一个普通常量作为模板参数形参,不能为浮点数,字符类型以及类对象; #include<iostrea…...
Beats:在 Docker 中同时部署 Metricbeat 和 Elasticsearch
在本教程中,我们将部署一个 metricbeat 来监控正在运行的容器的健康状况和系统指标。 为什么需要监控,为什么需要 Metricbeat? 一个常见的问题,但很少有人回答。 首先,无论我们部署的是 docker 容器还是老式的金属箱&…...
编码技巧——Redis Pipeline
本文介绍Redis pipeline相关的知识点及代码示例,包括Redis客户端-服务端的一次完整的网络请求、pipeline与client执行多命令的区别、pipeline与Redis"事务"、pipeline的使用代码示例; pipeline与client执行多命令的区别 Redis是一种基于客户…...
ArcGIS制图技巧:制图入门与点、线、面状符号制作
目的: 1、了解地图制作目的; 2、了解在ArcMap平台中制作地图大致过程。 3、掌握地形图生成的操作; 4、掌握地形图的正确输出方法。 5、理解点状符号、线状符号、面状符号的基本概念; 6、理解地形点状符号、线状符号、面状符…...
Java基础 关于字典数据维护接口设计
开发环境 Eclipse2022JDK1.8 目录 1. 概述 2. 实现步骤 2.1 定义通用接口 2.2 定义实体类 2.3 接口扩展 2.4 接口实现 2.5 功能测试 3. 结语 1. 概述 每一个信息系统或多或少都带有一些数据字典,在维护上,基本上分为增删改查,也就是对数据…...
从零开始学架构——复杂度来源
复杂度来源——高性能 对性能孜孜不倦的追求是整个人类技术不断发展的根本驱动力。例如计算机,从电子管计算机到晶体管计算机再到集成电路计算机,运算性能从每秒几次提升到每秒几亿次。但伴随性能越来越高,相应的方法和系统复杂度也是越来越高。现代的计算机CPU集成…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
