position

CSS 中 position 属性用于指定元素的定位方法的类型(static、relative、absolute、fixed、sticky)。 静态定位 position: static 此属性未 HTML 元素默认定位,一个元素没有以任何特殊的方式定位,它总是按照页面的正常流程定位。 在此属性下,属性值 top、left、right、bottom 和 z-index 对HTML元素没有影响。 <div class="parent"> <div class="child"></div> </div> <div class="sibling"></div> <style type="text/css"> .parent { width: 480px; height: 320px; background-color: #008800; } .child { width: 240px; height: 160px; background-color: #ff0000; } .sibling { width: 240px; height: 160px; background-color: #0000ff; } </style> 效果如图: 为什么使用它呢?将元素设置为 position: static 的唯一原因是强制删除应用于无法控制的元素上的某些定位。 相对定位 position: relative 此属性相对于其正常位置的,在不改变布局的情况下根据元素的当前位置定位元素。 ...

October 8, 2024

选择器

CSS选取器分类 通配符选择器 *{ margin:0; padding:0; } 元素(标签)选择器 p{ color:red; } 类选择器 .warning{ color:red; } ID选择器 #warning{ color:red; } 优先级 !important>行内样式>ID选择器>类、伪类、属性>元素、伪元素>继承>通配符 属性选取器 示例html <ul> <li foo>1</li> <li foo="abc">2</li> <li foo="abc efj">3</li> <li foo="abcefj">4</li> <li foo="efjabc">5</li> <li foo="ab">6</li> </ul> [attribute] [foo]{ background-color:red; } 选择所有带 foo 属性的元素 [attribute=value] 选择 attribute=value 的所有元素。 [foo=abc]{ background-color:red; } [attribute~=value] 选择 attribute 属性包含单词 value 的所有元素。 [foo~=abc]{ background-color:red; } [attribute^=value] 选择其 attribute 属性值以 value 开头的所有元素。类似正则的 ^,以什么开始 ...

October 8, 2024

伪类和伪元素

什么是伪类和伪元素? 伪类:以冒号(:)开头,用于选择处于特定状态的元素。 伪元素:以双冒号(::)开头,用于在文档中插入虚构的元素。 这么说有点抽象,我们来看具体案例。例如下面的伪类语法表达的意思是:文档里的那些已经被用户访问过的 a 标签,当鼠标悬浮在它上面的时候,颜色为红色。 a:visited:hover { color: red; } 效果如下: 可以看到,无论是 visited还是 hover,都表示了 a 标签的某种状态,选择器始终选择的是伪类前面的元素,只不过当出现伪类描述的状态时,样式才生效而已。 注意,上面使用的是无痕浏览器打开的页面,是不可能出现 visited 状态的,因为无痕不记录访问历史,我们可以点击控制台的 :hov 图标手动勾选 :visited 和 :hover 来模拟 a 标签「已访问」和「悬浮」两种状态。 我们再来看一下伪元素的案例,下面的语法表达的意思是: div 标签里面首字的颜色为红色。 div::first-letter { color: red; } 效果如下: 与伪类不同的是,首字并不是 div 元素的某种状态,而是浏览器创建出来的一个虚拟的元素,我们可以在上图的右下角看到 Pseudo ::first-letter element提示,相当于浏览器自动选中了「彻」字。 注意:::first-letter 伪元素只作用于块状元素上面,如果把 div 换成 span,就无法选中首字了,除非手动设置 span 的 display 属性为 block 或 inline-block 等块状值。 从上面的案例当中,我们可以得出两个结论: 伪类用于向某些已经存在的选择器添加特殊效果(当状态改变时) 伪元素用于将特殊效果添加到不存在的虚拟元素中(浏览器自动创建) 也就是说伪类的本质还是类(class),作用于标签本身,只不过限定了状态条件;而伪元素的本质是元素(element),作用于该虚拟元素的内容本身。 伪类有哪些? 按照功能,可划分为以下几类: 动态伪类::visited、:focus、:hover等 状态伪类::disabled、:empty、:required 等 结构伪类::first-child、:nth-of-type等 其他伪类::target、:lang、:not()等 下面的表格详细记录了各种伪类及其描述: 伪类 描述 兼容性 :active 元素处于活动状态时 ✅ :focus 元素已获取焦点时 ✅ :hover 元素处于悬浮状态时 ✅ :link 链接未访问时 ✅ :visited 链接已访问时 ✅ :first-child 元素是首个子元素时 ✅ :last-child 元素是最后一个子元素时 ✅ :nth-child() 元素是第 n 个子元素时 ✅ :nth-last-child() 元素是倒数第 n 个子元素时 ✅ :only-child 元素是唯一子元素时 ✅ :first-of-type 元素是首个特定类型的子元素时 ✅ :last-of-type 元素是最后一个特定类型的子元素时 ✅ :nth-of-type() 元素是第 n 个特定类型的子元素时 ✅ :nth-last-of-type() 元素是倒数第 n 个特定类型的子元素时 ✅ :only-of-type 元素是唯一的特定类型的子元素时 ✅ :not 不满足指定条件时 ✅ :target 元素 id 匹配到哈希值时 ✅ :root 元素是文档树的根元素时 ✅ :lang() 匹配到指定语言时 ✅ :empty 元素处于没有子元素状态时 ✅ :invalid 和 :valid 表单项是否有效 ✅ :required 和 :optional 表单项是否必填 ✅ :in-range和 :out-of-range 表单项是否超出范围 ✅ :read-only和 :read-write 表单项是否只读 ✅ :enabled和 :disabled 表单项是否禁用 ✅ :fullscreen 当前处于全屏显示模式时 ⚠️ :blank 输入框处于输入为空状态时 ❌ :dir() 匹配到特定文字书写方向时 ❌ 上表中的兼容性表示: ...

October 8, 2024

watch与watchEffect

Watch 基本用法 当我们需要在数据变化时执行一些“副作用”:如更改 DOM、执行异步操作,我们可以使用 watch 函数: <script setup> import { ref, watch } from 'vue' const question = ref('') const answer = ref('This is answer. ;-)') // 侦听一个 ref watch(question, async (newQuestion, oldQuestion) => { answer.value = 'Thinking...' const res = await fetch('https://...') answer.value = (await res.json()).answer }) </script> <template> <input v-model="question" /> <p>{{ answer }}</p> </template> watch() 一共可以接受三个参数,侦听数据源、回调函数和配置选项。 侦听数据源 watch 的第一个参数可以是不同形式的“数据源”,它可以是: 一个 ref 一个计算属性 一个 getter 函数(有返回值的函数) 一个响应式对象 以上类型的值组成的数组 const x = ref(1) const y = ref(1) const doubleX = computed(() => x.value * 2) const obj = reactive({ count: 0 }) // 单个 ref watch(x, (newValue) => { console.log(`x is ${newValue}`) }) // 计算属性 watch(doubleX, (newValue) => { console.log(`doubleX is ${newValue}`) }) // getter 函数 watch( () => x.value + y.value, (sum) => { console.log(`sum of x + y is: ${sum}`) } ) // 响应式对象 watch(obj, (newValue, oldValue) => { // 在嵌套的属性变更时触发 // 注意:`newValue` 此处和 `oldValue` 是相等的 // 因为它们是同一个对象! }) // 以上类型的值组成的数组 watch([x, () => y.value], ([newX, newY]) => { console.log(`x is ${newX} and y is ${newY}`) }) 注意,你不能直接侦听响应式对象的属性值,例如: ...

October 5, 2024

响应式原理

什么是响应式系统 在 Vue 中,数据驱动视图是一个非常重要的概念。当数据发生变化时,视图会自动更新,这是因为 Vue 实现了一个响应式系统,通过追踪依赖关系来自动更新视图。 简单来说,响应式系统就是 Vue 能够监听数据变化,并在变化时自动更新 DOM 的核心机制。在 Vue3 中,响应式系统的改进使得响应式数据可以更加高效地更新视图,同时也提升了开发体验和性能。 Vue2 的响应式原理 在 Vue2 中,响应式原理是通过 Object.defineProperty 来实现的。Vue 会在数据对象上定义 getter 和 setter,来实现数据变化时更新视图的功能。 举个例子,当一个数据对象被定义为响应式时,我们可以通过访问这个对象的属性来触发 get 操作。具体实现如下: Object.defineProperty(obj, 'foo', { get: function() { console.log('get foo') return obj._foo }, set: function(newVal) { console.log('set foo') obj._foo = newVal } }) 在上面的代码中,我们通过 Object.defineProperty 方法来监听对象上的属性 foo,当访问 foo 属性时,会触发 get 操作,并输出日志。同理,当给 foo 属性赋值时,会触发 set 操作,并输出日志。 在 Vue2 中,我们可以通过使用上面的方法来将数据对象中的所有属性转化成响应式的,以实现数据变化时更新视图的功能。 Vue3 的响应式原理 在 Vue3 中,响应式原理的实现方式和 Vue2 相比,有了相当大的变化。Vue3 直接使用了 ES6 Proxy 对象来拦截对对象的访问操作。Proxy 可以监听到对对象进行的访问、赋值等操作,并在这些操作发生时通知相关依赖以维护响应式系统的更新。 ...

October 5, 2024

Vue3 API

📍前言 最近在一次理解vue项目的代码时,发现周一对好多API都不太熟悉。这间接导致的问题是,代码理解速度要比平常要慢很多。于是乎,赶忙把vue API的学习提上了日程。 在下面的文章中,将地板式地扫盲vue3文档中API模块的所有内容,融入周一的理解进行深入介绍。下面就来一起看看吧~🍬 一、🖇框架搭建 1、关于文档 首先附上官方文档的具体材料:cn.vuejs.org/api/ 2、VUE3 API整体盘点 在vue3的全新API中,有部分在vue2的基础上沿用了。还有另外一部分,是vue3所新增加的。我们先来看vue3 API文档主要包含哪些内容? vue3 API主要包含以下六个部分: 全局API —— 全局会用到的API 组合式API —— vue3所拥有的组合式API 选项式API —— vue2所拥有的选项式API 内置内容 —— 指令、组件、特殊元素和特殊属性 单文件组件 —— 语法定义、 进阶API —— 渲染函数、服务端渲染、TS工具类型和自定义渲染 下面将依据上面提到的六大点内容,来进行相应的剖析和讲解。 二、🎨全局API vue3的全局API包含两个部分:应用实例和通用API。那它们各自都有哪些内容呢? 1、应用实例 2、通用API 三、🚲组合式API 谈到vue3 ,相信大家最为熟悉的就是 composition API 了,也就是 组合式 API 。那么,vue3 的 组合式 API 都给我们带来了什么呢? 1、setup 2、响应式:核心 3、响应式:工具函数 4、响应式:进阶 5、生命周期钩子 6、依赖注入 四、🌠选项式API 选项式API 即 options API 。可能有的小伙伴会觉得它在 vue2 项目下会更为常见一些。但在 vue3 项目中,也是有一些 选项式API 值得我们去挖掘的。那都有哪些内容呢,我们来一探究竟。 ...

October 5, 2024

服务器渲染

前言 什么是SSR(Server-Side Rendering)?类似于用Java、ASP.NET、php开发前端页面,服务端准备数据并执行页面渲染,然后把完整的HTML发送给客户端。现代化的SSR通常和Vue、React等前端框架强关联,通过Node.js在服务端提前将page、component使用同构方式渲染为html代码,其目的是在实现SSR时尽量复用一套渲染机制。例如Vue框架就同时支持了CSR(Client-Side Rendering)和SSR。 SSR能解决什么问题? SEO: Google、百度、Yandex、Bing或者Yahoo等搜索引擎会通过网络爬取你的页面并建立索引,如果的信息越完整,那么你的网站就更容易被检索到。使用SSR技术,让页面在服务端提前渲染好再返回给客户端,这样各类搜索引擎就能拿到比较完整的页面信息,提升检索质量。 首屏渲染:SPA(Single Page Application)将页面渲染放到客户端执行,并且在渲染之前要加载大量的Javascript代码,所以首屏渲染需要花费较长时间。而SSR直接在服务端渲染完再返回客户端,使用户能够快速看到页面内容。 有哪些框架支持SSR? 开源库 支持语言 star数 描述 Nuxt Vue 54.9k 快速构架、类型安全、高性能、易扩展 Next React 127k 老牌框架、社区完善、Data Fetch、Styling quasar Vue 26k 丰富的UI、支持桌面端和移动端 Remix React 29.9k 极致的用户体验、开箱即用 本知识点将以 Nuxt 为例,介绍如何实现 SSR。 什么是Nuxt 基于Vue、React、Angular实现的full-stack框架数不胜数,但就Vue框架领域内,应该数Nuxt最为Top 1。Nuxt保持了Vue生态的“开箱即用”特性,类似于Vite,几条指令就可直接run起来。如果你熟悉Vue,那么基于Nuxt写应用,体验上和写Vue客户端应用极其相似。 Nuxt提前预制了一套目录结构,并自动处理路由、导入、数据获取,开发人员仅需按约定的规则实现页面、组件、数据API即可,极大降低开发人员学习成本。Nuxt的特性可总结为: 基于文件的路由: 页面统一添加到pages/目录下,例如添加index.vue、about.vue、contact.vue,就可以通过/访问首页,通过/about返回关于页面; 代码分割:得益于Vue的Virtual Node,Nuxt根据目录规则提前生成完整的依赖树,这样能够将所有code拆分为最小单元的chunks, 从而减少应用初始化加载时间; 开箱即用的SSR:Nuxt内部使用Vue的SSR,除了Vue实现SSR、CSR本身的差异,可以像开发CSR的Vue应用一样来开发SSR,降低研发心智负担; 自动导入:在实现页面或组件时,不需要像CSR手动导入(import Card from './Card.Vue')依赖,当Nuxt识别到有使用外部组件,bundle过程会自动识别; 数据获取工具:提供一套组合式FetchAPI,实现client、server端的同构; 配置好的构建工具:默认使用Vite构建,同时支持Webpack、Rspack; Nuxt的架构设计和vue/core很相似,将整体功能拆分为独立的package: 核心引擎:nuxt; bundlers: 打包器包含@nuxt/vite-builde、nuxt/webpack-builder、@nuxt/rspack-builder; 命令行工具:nuxi; 服务端引擎:nitro; 开发套件:@nuxt/kit; 上手体验 初始化项目 使用nuxi提供的指令初始化项目: npx nuxi@latest init nuxt-learn-examples 如果安装过程下载失败,可能需要配置hosts: 185.199.108.133 raw.githubusercontent.com 最新nuxt版本3.14初始化的项目目录比较简单,少了pages、plugins等目录,入口为app.vue: ...

October 5, 2024

状态管理

本知识点将以 Pinia 为例,介绍 Vue 中的状态管理。 前言 Pinia简称小菠萝🍍,是一个专为Vue 3设计的现代化状态管理库,为Vue 3开发的,它提供了一种简单、可扩展和类型安全的方式来管理应用程序的状态。 与Vue 2中的Vuex相比,Pinia提供了更好的TypeScript支持,具有更好的类型定义和类型推断,可在编译时捕获错误,提供更高的代码可靠性和开发体验。它是专为Vue 3设计的,充分利用了Vue 3的新特性,如Composition API,以提供更直接、自然和灵活的状态管理体验。Pinia的核心概念是Store,它类似于Vuex中的模块,用于管理应用程序的状,可以将相关的状态和逻辑组合到单个Store中,使代码更清晰、结构更有组织性。除此之外海提供了许多有用的特性和功能,例如模块化组织、状态持久化、插件扩展等。 总的来说,Pinia是一个功能强大而灵活的状态管理解决方案,适用于各种规模的Vue 3应用程序。它提供了现代化的特性和工具,帮助我们更好地组织、管理和扩展应用程序的状态,同时提供了更好的类型安全和开发体验。 安装 运行安装命令 npm install pinia 在main.ts中引入 // main.ts import { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue' const pinia = createPinia() const app = createApp(App) app.use(pinia) app.mount('#app') 初始化Store 新建stores文件,用于存放所有的store,然后创建index.ts。 同过 defineStore() 定义一个store,它接受一个参数作为仓库名称,也就是Id。它返回一个函数,默认我们使用user开头的风格来接收。第二个参数为一个Setup函数或者Option对象。 import { defineStore } from 'pinia' export const useUsersStore = defineStore('users', { // 其他配置... }) Option Store 这种方式熟悉Vuex的很了解,传入一个带有 state、actions 与 getters 属性的 Option 对象 export const userUsersStore = defineStore('users', { state: () => { return { name: 'inkun', current: 100 } }, getters: { getName: (state) => state.name + '🐔你好帅' }, actions:{ getUserInfo { ... } } }) 在 Option Store 中: ...

October 5, 2024

vue路由

从后端路由开始 路由这个概念最先是后端出现的。在以前用模板引擎开发页面时,经常会看到这样 http://www.xxx.com/login 大致流程可以看成这样: 浏览器发出请求 服务器监听到80端口(或443)有请求过来,并解析url路径 根据服务器的路由配置,返回相应信息(可以是 html 字串,也可以是 json 数据,图片等) 浏览器根据数据包的 Content-Type 来决定如何解析数据 简单来说路由就是用来跟后端服务器进行交互的一种方式,通过不同的路径,来请求不同的资源,请求不同的页面是路由的其中一种功能。 前端路由 1. hash 模式 随着 ajax 的流行,异步数据请求交互运行在不刷新浏览器的情况下进行。而异步交互体验的更高级版本就是 SPA —— 单页应用。单页应用不仅仅是在页面交互是无刷新的,连页面跳转都是无刷新的,为了实现单页应用,所以就有了前端路由。 类似于服务端路由,前端路由实现起来其实也很简单,就是匹配不同的 url 路径,进行解析,然后动态的渲染出区域 html 内容。但是这样存在一个问题,就是 url 每次变化的时候,都会造成页面的刷新。那解决问题的思路便是在改变 url 的情况下,保证页面的不刷新。在 2014 年之前,大家是通过 hash 来实现路由,url hash 就是类似于: http://www.xxx.com/#/login 这种 #。后面 hash 值的变化,并不会导致浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面。另外每次 hash 值的变化,还会触发hashchange 这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化。然后我们便可以监听hashchange来实现更新页面部分内容的操作: function matchAndUpdate () { // todo 匹配 hash 做 dom 更新操作 } window.addEventListener('hashchange', matchAndUpdate) 2. history 模式 14年后,因为HTML5标准发布。多了两个 API,pushState 和 replaceState,通过这两个 API 可以改变 url 地址且不会发送请求。同时还有popstate 事件。通过这些就能用另一种方式来实现前端路由了,但原理都是跟 hash 实现相同的。用了 HTML5 的实现,单页路由的 url 就不会多出一个#,变得更加美观。但因为没有 # 号,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。为了避免出现这种情况,所以这个实现需要服务器的支持,需要把所有路由都重定向到根页面。 ...

October 5, 2024

异步组件

当我们的项目达到一定的规模时,对于某些组件来说,我们并不希望一开始全部加载,而是需要的时候进行加载;这样的做得目的可以很好的提高用户体验。 为了实现这个功能,Vue3中为我们提供了一个方法,即defineAsyncComponent,这个方法可以传递两种类型的参数,分别是函数类型和对象类型,接下来我们分别学习。 传递工厂函数作为参数 defineAsyncComponent方法接收一个工厂函数是它的基本用法,这个工厂函数必须返回一个Promise,Promise的resolve应该返回一个组件。 我们这里以Vue Cli创建的项目为例,这里我稍微做了一下修改,将头部的图片拆分为一个组件,代码如下: <template> <logo-img /> <hello-world msg="Welcome to Your Vue.js App" /> </template> <script setup> import LogoImg from './components/LogoImg.vue' import HelloWorld from './components/HelloWorld.vue' </script> 现在我们就将<hello-world>组件修改为异步组件,示例代码如下: <template> <logo-img /> <hello-world msg="Welcome to Your Vue.js App" /> </template> <script setup> import { defineAsyncComponent } from 'vue' import LogoImg from './components/LogoImg.vue' // 简单用法 const HelloWorld = defineAsyncComponent(() => import('./components/HelloWorld.vue'), ) </script> 我们这里为了看到效果,将import延迟执行,示例代码如下: <script setup> import { defineAsyncComponent } from 'vue' import LogoImg from './components/LogoImg.vue' // 定义一个耗时执行的函数,t 表示延迟的时间, callback 表示需要执行的函数,可选 const time = (t, callback = () => {}) => { return new Promise(resolve => { setTimeout(() => { callback() resolve() }, t) }) } // 定义异步组件,这里这样写是为了查看效果 const HelloWorld = defineAsyncComponent(() => { return new Promise((resolve, reject) => { ;(async function () { try { await time(2000) const res = await import('./components/HelloWorld.vue') resolve(res) } catch (error) { reject(error) } })() }) }) </script> 代码运行结果如下所示: ...

October 5, 2024