前端知识补充 2
[toc]
A. Web Component 自定义组件
web Component(后续简称WC)是由谷歌官方出品的一种可以使原生HTML组件化的一种技术.WC可以脱离框架的限制,例如React Vue等框架,可以脱离这几种框架而存在,同时,也可以用在这几种框架里面.WC出现的目的主要是使HTML标签可以聚合成一种原生组件.
进行自定义组件有两种方式,但是在介绍两种方式之前,需要先引入一个概念:shadow DOM
shadow DOM
Web components 的一个重要属性是封装——可以将标记结构、样式和行为隐藏起来,并与页面上的其他代码相隔离,保证不同的部分不会混在一起,可使代码更加干净、整洁。
Shadow DOM 接口可以将一个隐藏的、独立的 DOM 附加到一个元素上.
Shadow DOM 并不是将这些节点真正隐藏起来让用户看不到或者是在浏览器的元素中看不到,而是让JS访问不到.
基本用法:
可以使用 Element.attachShadow()
方法来将一个 shadow root 附加到任何一个元素上。它接受一个配置对象作为参数,该对象有一个 mode
属性,值可以是 open
或者 closed
:open表示可以由JS访问得到,close表示由JS访问不到.
当然,落实到Web Component上则可以这样使用:
let shadow = this.attachShadow({ mode: "open" }); ... shadow.appendChild(elements);
定义自定义组件
为了实现这样一个功能,我们需要定义一个叫做PopUpInfo
的类,他继承于HTMLElement:
class PopUpInfo extends HTMLElement { constructor() { // 必须首先调用 super 方法 super(); // 元素的功能代码写在这里 ... } }
然后,我们使用define()
方法将 custom element 注册到CustomElementRegistry
上,在方法的参数里,我们指定元素的名称,以及定义了元素功能的类。
customElements.define("popup-info", PopUpInfo);
然后就可以在页面上使用我们的自定义组件了.
<popup-info></popup-info>
当然,除了用以上继承于HTMLElement的方法,还有一种继承于自定义内置元素的方法:
class PopUpInfo extends HTMLUListElement { constructor() { // 必须首先调用 super 方法 super(); // 元素的功能代码写在这里 ... } }
接下来,和之前一样,我们使用define()
方法注册一个元素,但不同的是,我们需要添加一个配置对象,用于指定我们需要继承的元素:
customElements.define("popup-info", PopUpInfo, { extends: "ul" });
他继承于ul这样一个内置组件,所以默认会表现出来ul的样子,在页面上使用的话看起来也会有所不同:
<ul is="expanding-list"> ... </ul>
生命周期
当然,在custom element 的构造函数中,可以指定多个不同的回调函数,它们将会在元素的不同生命时期被调用:
connectedCallback
:当 custom element 首次被插入文档 DOM 时,被调用。disconnectedCallback
:当 custom element 从文档 DOM 中删除时,被调用。adoptedCallback
:当 custom element 被移动到新的文档时,被调用。attributeChangedCallback
: 当 custom element 增加、删除、修改自身属性时,被调用。
如何使用?
connectedCallback() { console.log('Custom square element added to page.'); updateStyle(this); } disconnectedCallback() { console.log('Custom square element removed from page.'); } adoptedCallback() { console.log('Custom square element moved to new page.'); } //每当元素的属性变化时,attributeChangedCallback()回调函数会执行。正如它的属性所示,我们可以查看属性的名称、旧值与新值,以此来对元素属性做单独的操作。 attributeChangedCallback(name, oldValue, newValue) { console.log('Custom square element attributes changed.'); updateStyle(this); }
设计自定义组件的方式一:原生DOM操作
具体如下:
class PopUpInfo extends HTMLElement { constructor() { // 必须首先调用 super 方法 super(); // 元素的功能代码写在这里 var wrapper = document.createElement("span"); wrapper.setAttribute("class", "wrapper"); var icon = document.createElement("span"); icon.setAttribute("class", "icon"); icon.setAttribute("tabindex", 0); var info = document.createElement("span"); info.setAttribute("class", "info"); wrapper.appendChild(icon); wrapper.appendChild(info); shadow.appendChild(wrapper); } }
设计自定义组件的方式二:template与slot
第二种方法就是使用template,template在原生html中是不会被解析的.所以我们可以这样自定:
在html中写入:
<template id="my-paragraph"> <p>My paragraph</p> </template>
在js class MyParagraph 类中这样写:
let template = document.getElementById("my-paragraph"); let templateContent = template.content; shadowRoot.appendChild(templateContent.cloneNode(true));
要注意的关键是我们使用 Node.cloneNode() 方法添加了模板的拷贝到阴影的根结点上。深拷贝一个节点,这样我们在用这个模板的时候,别人用的时候就不会受到影响了.
当然,我们也可以使用slot来添加灵活度:
slot具体使用方法和Vue中是一样的.
<template id="my-paragraph"> <p>My paragraph</p> <slot name="my-text"></slot> </template>
<my-paragraph> <span slot="my-text">Let's have some different text!</span> </my-paragraph>
如此即可.
B. JS原生DOM的操作方法汇总
DOM 文档模型又称DOM树, DOM树是由文档 元素 节点组成的.
- 文档:一个页面就是一个文档,
- 元素:文档中的所有标签都称为元素。DOM中使用Element表示
- 节点:文档中的所有内容,在文档中都是节点(标签、属性、文本注释等)DOM中使用node表示
操作DOM的方法都是由document发起的.
获取元素的方法
- getElementById (Id)
- 返回id属性值的元素节点相对应的对象
- getElementsByTagName (标签名)
- 返回的是一个对象数组 (伪数组),如果获取不到元素,则返回为空的伪数组(因为获取不到对象)
- getElementsByCalssName (class类名)
- 返回的是一个对象数组 (伪数组),如果获取不到元素,则返回为空的伪数组(因为获取不到对象)
- querySelector (选择器)
- 参数: 可以是 id 、类名、标签 选择器,需要在选择器前
加符号
例如 .box 、 #nav。让querySelector 知道是什么选择器所以要加符号。 - 返回:该模式匹配的第一个元素,如果没有则为null
- 参数: 可以是 id 、类名、标签 选择器,需要在选择器前
- querySelectorAll (选择器)
- 返回:该模式匹配的所有元素对象集合(伪数组 )
- 获取特殊元素(body、html)
- 获取body :document.body
- 获取html : document.documentElement
获取元素之[节点获取]
页面中所有的内容都为节点,节点使用 node
来表示
DOM树可以把节点划分为不同的层级关系,常见的是父子兄层级关系
元素节点:nodeType 为 1
属性节点:nodeType 为 2
文本节点:nodeType 为 3 (文本节点主要包括文字 空格 换行等)
获取节点:[节点包括:元素节点 属性节点 文本节点]
node.parentNode
:可以返回某个节点的父节点,注意是最近一级的父节点
,如果指定的节点没有父节点则返回null
node.childNodes
:返回包含指定节点的子节点的集合,该集合为即时更新的集合
node.firstChild
:返回第一个子节点,找不到则返回null
node.lastChild
:返回最后一个子节点,找不到则返回null
node.nextSibling
:返回当前元素的下一个兄弟元素节点,找不到则返回null
node.previousSibling
:返回当前元素的下一个兄弟元素节点,找不到则返回null
只获取元素节点(常用)
parentNode.children
是一个只读属性,返回所有的子元素节点
node.children[0]
:返回第一个子元素节点,找不到则返回null
node.children[node.length-1]
:返回最后一个子元素节点,找不到则返回null
如果想只获得元素节点还有另一种方法就是自定义一个获取元素节点的方法,判断nodeType是否为1即可.
C. Canvas学习与使用
D. requestAnimationFrame渲染函数
https://juejin.cn/post/7202785660666495037
requestAnimationFrame
即请求动画帧,它是一个浏览器的宏任务。简单的说,这个api主要是用来做动画的。他的回调函数执行次数通常是每秒(刷新率)次. 刷新率:每隔1000毫秒的60分之一(60HZ)
前端动画方案有
css
动画transition
:过渡动画animation
:直接动画(搭配@keyframes
)
js
动画setInterval
或setTimeout
定时器(比如不停地更改dom元素
的位置,使其运动起来)canvas
动画,搭配js
中的定时器去运动起来(canvas
只是一个画笔,然后我们通过定时器会使用这个画笔去画画-动画)requestAnimationFrame动画(js动画中的较好方案)
requestAnimationFrame
能够做到,精准严格的卡住显示器刷新的时间,比如普通显示器60HZ
它会自动对应17ms
执行一次,高级显示器120HZ
,它会自动对应9ms
执行一次。当然requestAnimationFrame
只会执行一次,想要使其多次执行,要写成递归的形式。
E.toref 与 torefs
F.Pinia使用注意事项
在使用Pinia时,Pinia并不是所有组件或者所有页面都挂载了的.只有引用了一个store文件的组件或者页面才会挂载Pinia.
因此,在需要使用到Pinia的地方请写明:
import { useTableDiffStore } from "@/stores/tableDiff"; ... useTableDiffStore();
即使是这个页面拿到这个store什么也不做,但是需要store里面监听一些数据之类的,都需要useTableDiffStore();
进行挂载.
可以省略 const store = useTableDiffStore(), 只写 useTableDiffStore();
F.v-memo
https://juejin.cn/post/7144884293423071268
Vue 3.2版本新增了v-memo指令,memo从字面意思理解是备忘录。它可以接受一个数组,数组中存放依赖的值,数组中的依赖值都没有发生变化时,使用v-memo的组件/元素将跳过组件更新(包括其子树)。
<template> <div v-memo="[name]"> <div>{{ name }}</div> <div>{{ age }}</div> <div>{{ message }}</div> </div> <button @click="handleBtnClick">change</button> </template> <script setup> import { ref } from 'vue'; const name = ref('loftyamb') const age = ref(24) const handleBtnClick = () => { age.value++ // age值的变化将不会更新到页面中,除非name发生变化 }
上述代码表示只有当name变更的时候,其下的dom元素才会更新.