笔记涵盖自 2022 年 1 月 22 日搭建博客起,所学的前端相关开发技能。
大部分初学/过时内容已及时更新,但可能仍有遗漏。
笔记仅供自用参考,让您见笑还请海涵
共包含以下方面:
Vue 笔记、Vue 风格指南、TypeScript 笔记、Axios 相关、Vite 相关、Pinia 相关、Vue-Router 相关、Sass 笔记、Nuxt 目录结构、Git 笔记、ES6 笔记、CSS BEM 架构、V8 引擎原理、RESTful Api 规范、VueUse 常用函数、Mock 配置、Element-plus引入、Vue3 插件库、自动引入相关、DevTool Manual 开发者工具手册、安全相关、空格相关、接口文档示例

Vue 笔记

善用计算属性缓存

计算属性与动态样式绑定相结合:

const classObject = computed(() => ({
active: isActive.value && !error.value,
'text-danger': error.value && error.value.type === 'fatal'
}))
<div :class="classObject"></div>

直接绑定样式::对象::

在计算属性中使用 reverse()sort() 的时候务必小心!这两个方法将变更原始数组,计算函数中不应该这么做。请在调用这些方法之前创建一个原数组的副本:

- return numbers.reverse()
+ return [...numbers].reverse()

事件处理器

<!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
<!-- 例如:事件处理器不来自子元素 -->
<div @click.self="doThat">...</div>

watch 监听器

watch 等监听器会在宿主组件卸载时自动停止,但异步创建的不会,需要手动停止以防内存泄露。

使用一个对象绑定多个 prop

const post = { id: 1, title: 'My Journey with Vue' }
<BlogPost v-bind="post" />

透传 attribute

<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>

Hooks 与 Utils

Hooks

在 Vue3 中通常指的是 Composition API 中的一部分,包括 refreactivewatchcomputed 以及生命周期钩子函数等。我们可以通过这些 hooks 在 setup 方法中组合和复用逻辑,解决了 Vue2 中复杂组件逻辑难以管理的问题。

Hooks 一般是带有生命周期或者是 Vue API 的,涉及到变量保存等。

Utils

指的是一些工具函数或工具类,用来执行一些公共的、与业务无关的操作,比如日期格式化、数据验证等。它们通常并不依赖 Vue 的 API,也不包含任何状态。

Utils 一般是 pure 函数,工具类方法。

keep-alive

<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" :key="$route.name" v-if="$route.meta.keepAlive" />
</keep-alive>
<component :is="Component" :key="$route.name" v-if="!$route.meta.keepAlive" />
</router-view>

Vue 风格指南

v-for 添加 key
• 多个 attribute 的元素应该分多行撰写
• 避免使用元素选择器,最好使用类选择器
• 父子组件名
• 避免 v-ifv-for 用在一起
• Props 写完整
• 组件名 多单词

基础组件名,单例组件名

基础组件名

应用特定样式和约定的基础组件(也就是展示类的、无逻辑的或无状态的组件)应该全部以一个特定的前缀开头,比如 BaseAppV

单例组件名

只应该拥有单个活跃实例的组件应该以 The 前缀命名,以示其唯一性。

这不意味着组件只可用于一个单页面,而是每个页面只使用一次。这些组件永远不接受任何 prop,因为它们是为你的应用定制的,而不是它们在你的应用中的上下文。如果你发现有必要添加 prop,那就表明这实际上是一个可复用的组件,只是目前在每个页面里只使用一次。

TypeScript 笔记

JSON 2 TS 工具

接口返回值的类型

使用 JSON2TS 工具把 JSON 的 interface 转出。

数组类型

:number[]
Array<number>

推荐第一种。

联合类型

(number | string)[]
number | string[]

类型别名(简化书写)

type CustomArray = (number | string)[]
let arr1: CustomArray = [1, 'a', 2, 'b']
let arr2: CustomArray = [1, 'a', 2, 'b', 3]

函数类型(给参数和返回值添加类型)

function add(num1: number, num2: number): number {
return num1 + num2
}

也可以:

const add = (num1: number, num2: number): number => {
return num1 + num2
}

对象类型

let person: { name: string; sayHi(): void } = { …… }

let person: { name: string; sayHi: () => void } = { …… }

接口(描述对象的类型,达到复用的目的)

interface IPerson {
name: string
sayHi(): void
}
let person: IPerson = { …… }

接口的继承

interface Point3D extends Point2D {
z: number
}

元组(确切标记处元素的个数 + 类型)

let position: [number, number] = [1, 1]

类型推断(编辑器功能)

类型断言

const a = XXX as HTMLElement

也可以使用 <>

const a = <HTMLElement>XXX

字面量类型(配合联合类型,用于明确的表示一组可选值列表)

function changeDirection(direction: 'up' | 'down' | 'left' | 'right') { …… }

枚举(类似字面量类型 + 联合类型)

enum Direction { Up, Down, Left, Right }
function changeDirection(direction: Direction) { …… }

访问时通过 . 操作符:Direction.Up

typeof 类型上下文

相当于 TS 帮你推断上文变量类型。

class

extends 继承父类

implements 实现接口

可见性

readonly 只读属性

类型兼容性

接口兼容性、函数兼容性

交叉类型 &(类似于接口继承)

interface A { …… }
interface B { …… }
type C = A & B

泛型 Type(保证类型安全的同时,让函数等与多种类型一起工作,从而实现复用)

function id<Type>(value: Type): Type {
return value
}

泛型约束:

function id<Type>(value: Type[]): Type[] {
return value
}

类型声明文件 .d.ts 用于给第三方库的代码(JavaScript 代码)提供类型

let notSure: any = 4;
let list: any[] = [1, true, "free"];

数组:

let list1: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3];

元组:

let x: [string, number];
x = ['hello', 10]; // OK
x = [10, 'hello']; // Error

枚举:

enum Color { Red, Green, Blue }
let c: Color = Color.Green;

联合类型与类型断言

function getLength(x: number | string) {
if ((<string>x).length) {
return (x as string).length
} else {
return x.toString().length
}
}
console.log(getLength('abcd'), getLength(1234))

接口

interface IPerson {
// 只读属性
readonly id: number
name: string
age: number
// ?可选属性
sex?: string
}

interface SearchFunc {
// 参数的类型 与 返回值的类型
(source: string, subString: string): boolean
}
const mySearch: SearchFunc = function (source: string, sub: string): boolean {
return source.search(sub) > -1
}

类类型:实现接口

  1. 一个类可以实现多个接口:
interface Alarm { alert(): any; }
interface Light { lightOn(): void; }

class Car implements Alarm, Light {
alert() {
console.log('Car alert');
}
lightOn() {
console.log('Car light on');
}
lightOff() {
console.log('Car light off');
}
}
  1. 一个接口可以继承多个接口:
interface LightableAlarm extends Alarm, Light {}

class Student {
// 声明属性
fullName: string;
// 构造方法
constructor(public firstName: string, public middleInitial: string, public lastName: string) {
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
// 一般方法
greet(): string {
return 'Hello ' + this.fullName
}
}
// 创建类的实例
const user = new Student("Jane", "M.", "User");
// 调用实例的方法
console.log(user.greet())

我们在引用任何一个类成员的时候都用了 this。它表示我们访问的是类的成员。

类的继承

class Animal {
run(distance: number) {
console.log(`Animal run ${distance}m`)
}
}
class Dog extends Animal {
cry() {
console.log('wang! wang!')
}
}
const dog = new Dog()
dog.cry()
dog.run(100) // 可以调用从父中继承得到的方法

在 TypeScript 里,成员都默认为 public

你可以使用 readonly 关键字将属性设置为只读的。只读属性必须在声明时或构造函数里被初始化。

参数属性

在上面的例子中,我们必须在 Person 类里定义一个只读成员 name 和一个参数为 name 的构造函数,并且立刻将 name 的值赋给 this.name,这种情况经常会遇到。参数属性可以方便地让我们在一个地方定义并初始化一个成员。下面的例子是对之前 Person 类的修改版,使用了参数属性:

class Person2 {
constructor(readonly name: string) { }
}
const p = new Person2('jack')
console.log(p.name)

作用同下:

class Person {
readonly name: string = 'abc'
constructor(name: string) {
this.name = name
}
}

注意看我们是如何舍弃参数 name,仅在构造函数里使用 readonly name: string 参数来创建和初始化 name 成员。我们把声明和赋值合并至一处。

函数:默认值与可选值

function buildName(firstName: string = 'A', lastName?: string): string { … }

Axios 相关

引入

npm install axios

注意需要开启跨域设置:

// vite.config.ts
server: {
port: 5173,
proxy: {
'/api': {
target: 'https://api.bilibili.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '') // 不可以省略 rewrite
}
}
}
// src/api/axios.ts
import axios from 'axios';

const instance = axios.create({
baseURL: '/api', // 更改为您的 API 基本路径
timeout: 5000
// 其他可选配置
});

export default instance;

当前实践

// apis/index.ts
/**
* 这里用于存放请求的 url 路径
*/
const api = {
getUser: '/getUser',
login: '/login',
getBiliData: '/x/web-interface/view?bvid=BV1es411Z7VB',
}
export default api
import axios from 'axios';
// 创建 axios 实例
const instance = axios.create({
// 为每一个请求加上基础路径 '/api',而 '/api' 会被 proxy 转发到 'http://127.0.0.1'
baseURL: '/api',
timeout: 5000, // 设置超时时间,单位为毫秒
} as any);
// 封装的 GET 请求函数
export const get = async (url: string, params?: any) => {
try {
const response = await instance.get(url, { params });
return response.data
} catch (error) {
throw new Error(error);
}
};
// 封装的 POST 请求函数
export const post = async (url: string, data?: any) => {
try {
const response = await instance.post(url, data);
return response.data
} catch (error) {
throw new Error(error);
}
};
// 使用
import api, { get, post } from "@/apis";
onMounted(async () => {
try {
let response = await get(api.getUser);
console.log(response);
response = await get(api.getBiliData);
console.log(response);
} catch (error) {
console.error(error);
}
})

封装

为什么要封装,怎么封装?

Axios 本身已经是一个非常方便易用的 HTTP 请求库,但是在实际开发中,我们经常需要对请求进行一些统一的处理,如添加请求头、处理请求参数、统一处理响应数据等,这些处理逻辑会让我们的代码变得冗长且难以维护。因此,我们需要对 Axios 进行封装,封装后的 Axios 可以实现以下功能:

  1. 统一处理请求和响应:封装后的 Axios 可以实现统一处理请求和响应,如添加请求头、处理请求参数、统一处理响应数据等,从而可以提高代码的可维护性和可读性。
  2. 简化代码:封装后的 Axios 可以将常用的请求方法、请求参数、响应处理等进行封装,从而可以简化代码,减少重复代码。
  3. 方便管理:封装后的 Axios 可以将请求和响应的处理逻辑进行封装,从而可以方便地进行管理和维护。
  4. 提高可重用性:封装后的 Axios 可以实现通用的请求和响应处理逻辑,从而可以提高代码的可重用性,减少重复开发。

总之,封装后的 Axios 可以让我们更方便地进行 HTTP 请求和响应处理,提高代码的可维护性和可读性,减少重复代码,提高代码的可重用性。

拦截器(属于封装)

拦截器可以在请求或响应被发送或处理之前,对其进行拦截和处理,从而可以实现以下作用:

  1. 添加公共配置:通过拦截器可以在请求和响应中添加一些公共的配置,如添加请求头、设置请求超时时间、设置响应数据格式等。
  2. 统一处理错误:通过拦截器可以对请求和响应中的错误进行统一处理,如网络错误、请求超时、响应数据格式错误等。
  3. 权限验证:通过拦截器可以对请求进行权限验证,如验证用户是否登录、是否有操作权限等。
  4. 数据转换:通过拦截器可以对请求和响应中的数据进行转换,如将请求数据转换为 FormData 格式、将响应数据转换为 JSON 格式等。
  5. 拦截重复请求:通过拦截器可以拦截重复请求,避免重复发送相同的请求,从而提高请求效率。
import axios from 'axios';

// 添加请求拦截器
axios.interceptors.request.use(
config => {
// 在请求发送之前做些什么
config.headers.Authorization = 'Bearer ' + localStorage.getItem('token');
return config;
},
error => {
// 对请求错误做些什么
return Promise.reject(error);
}
);

// 添加响应拦截器
axios.interceptors.response.use(
response => {
// 对响应数据做些什么
return response;
},
error => {
// 对响应错误做些什么
if (error.response) {
switch (error.response.status) {
case 401:
// 未登录,跳转到登录页面
break;
case 403:
// 没有权限,提示用户
break;
case 404:
// 请求的资源不存在,提示用户
break;
default:
// 其他错误,提示用户
}
} else {
// 网络错误,提示用户
}
return Promise.reject(error);
}
);

// 发送请求
axios.get('/api/user')
.then(response => {
// 处理响应数据
})
.catch(error => {
// 处理错误
});

在上面的代码中,我们通过 axios.interceptors.request 添加了一个请求拦截器,用于在请求头中添加 Authorization 字段,这个字段的值是从 localStorage 中获取的 token 值。同时,我们通过 axios.interceptors.response 添加了一个响应拦截器,用于统一处理错误。在错误处理中,我们根据不同的错误状态码,进行了不同的处理,如未登录跳转到登录页面、没有权限提示用户、请求资源不存在提示用户等。这样,我们就可以在整个应用中统一处理请求和响应中的错误,提高代码的可维护性和可读性。

Pinia 笔记

Webstorm 关于 Pinia 的代码补全

不能用 computed,因为在 computed 函数中,对 useCounterStore() 的调用是异步的,而 Counter 在模板中使用时是同步的。

let Counter
watchEffect(() => {
Counter = useCounterStore()
})
</script>

<template>
<button type="button" @click="Counter.increment()">count is {{ Counter.count }}</button>
<button type="button" @click="Counter.increment()">count is {{ Counter.testCount }}</button>
<button type="button" @click="">count is {{ Counter.double }}</button>
</template>

在路由文件中使用报错解决

官方文档:Pinia Outside Component Usage

import { createRouter } from 'vue-router'
const router = createRouter({})
// ❌ 由于引入顺序的问题,这将失败
const store = useStore()
router.beforeEach((to, from, next) => {
// 我们想要在这里使用 store
if (store.isLoggedIn) next()
else next('/login')
})
router.beforeEach((to) => {
// ✅ 这样做是可行的,因为路由器是在其被安装之后开始导航的,
```typescript
// 而此时 Pinia 也已经被安装。
const store = useStore()
if (to.meta.requiresAuth && !store.isLoggedIn) return '/login'
})

原因 + 解决方法

因为在 main.js 中,注册 router 总比 Pinia 先,所以不能使用到 store/index.js 文件中 createPinia 方法,只能在 router 文件中再 createPinia 一次,才能使用到 Pinia

// stores/store.ts
import { createPinia } from "pinia";
const pinia = createPinia();
export default pinia;

// 替代传统的在 main.ts 中直接导入的形式
import pinia from "./stores/store"
app.use(pinia)

// router.ts
import pinia from '../stores/store'
import { useCounterStore } from "../stores/counter"
const store = useCounterStore(pinia)

相当于原本在 main.ts 引入的改为一个单独的文件再引入,并且在路由文件中也引入一遍,防止未安装的报错出现。

// main.ts
import { createPinia } from 'pinia'
const pinia = createPinia()
app.use(pinia)

引入

npm install pinia
// main.ts
import { createPinia } from 'pinia'
const pinia = createPinia()
app.use(pinia)

使用

import { useCounterStore } from "@/stores/counter";
import { storeToRefs } from "pinia";
let counter = useCounterStore()
// `name` 和 `doubleCount` 是响应式的 ref
// 同时通过插件添加的属性也会被提取为 ref
// 并且会跳过所有的 action 或非响应式(不是 ref 或 reactive)的属性
const { count, doubleCount } = storeToRefs(counter)
// 作为 action 的 increment 可以直接解构
const { increment } = counter
increment()
console.log(count.value, doubleCount.value)

组合式

// stores/index.ts
import { defineStore } from 'pinia'
import { computed, ref } from "vue";

export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const double = computed(() => count.value * 2)

function increment() {
count.value++
}

return { count, double, increment }
})

ref() 就是 state 属性,computed() 就是 gettersfunction() 就是 actions

选项式

// stores/index.ts
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
},
})
import { useCounterStore } from '../stores'

export default {
setup() {
const counter = useCounterStore()
counter.count++
// 带有自动补全 ✨
counter.$patch({ count: counter.count + 1 })
// 或者使用 action 代替
counter.increment()
},
}

Vue-Router 笔记

路由传参获取

官方不推荐使用 params,如果需要隐藏传参内容请使用 Pinia

设置

{
path: '/backend',
name: 'Backend',
component: Backend,
meta: {
title: 'Backend',
keepAlive: true,
requireAuth: true,
identityAuth: 2
}
},
next({
name: 'Login',
query: {
identity: to.meta.identityAuth
}
});

获取

router.currentRoute.value.query.identity

引入

npm install vue-router@4
import router from './router'
createApp(App).use(router).mount('#app')
// router/index.ts
import * as VueRouter from 'vue-router'
import About from '../components/HelloWorld.vue'
import Home from '../App.vue'

const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
component: About
// 动态加载
// component: () => import(/* webpackChunkName: "about" */ '../views/about.vue')
}
]

const router = VueRouter.createRouter({
history: VueRouter.createWebHashHistory(),
routes,
})

export default router
<!-- 因为 / 是回到 app 组件,所以会显示两次路由名称 -->
<router-link to="/">首页</router-link>
|
<router-link to="/about">我的</router-link>
<router-view/>

Sass 笔记

嵌套、混入、变量、函数

npm install sass

混合 mixin

@mixin sexy-border($color, $width: 1in) {
border: {
color: $color;
width: $width;
style: dashed;
}
}
p { @include sexy-border(blue); }
h1 { @include sexy-border(blue, 2in); }
h1 { @include sexy-border($color: blue, $width: 2in); }

插值

如果需要使用变量,同时又要确保 / 不做除法运算而是完整地编译到 CSS 文件中,只需要用 #{} 插值语句将变量包裹。

p {
$font-size: 12px;
$line-height: 30px;
font: #{$font-size}/#{$line-height};
}

@extend

.error {
border: 1px #f00;
background-color: #fdd;
}
.seriousError {
@extend .error;
border-width: 3px;
}

控制指令

@if 1 + 1 == 2 { border: 1px solid; }
@else if 5 < 3 { border: 2px dotted; }
@else { border: 3px double; }

@for $i from 1 through 3 {
.item-#{$i} { width: 2em * $i; }
}

@each $animal in puma, sea-slug, egret, salamander {
.#{$animal}-icon {
background-image: url('/images/#{$animal}.png');
}
}

$i: 6;
@while $i > 0 {
.item-#{$i} { width: 2em * $i; }
$i: $i - 2;
}

函数指令

@function grid-width($n) {
@return $n * $grid-width + ($n - 1) * $gutter-width;
}

#sidebar { width: grid-width(5); }

Nuxt 目录结构

# .nuxt
Nuxt 使用 `.nuxt/` 目录在开发中生成您的 Vue 应用程序。整个目录将在运行 `nuxt dev` 时重新创建。

# .output
Nuxt 在为生产构建应用程序时创建 `.output` 目录。整个目录将在运行 `nuxt build` 时重新创建。

# assets
`assets/` 目录用于添加构建工具(webpack 或 Vite)将处理的所有网站资产。该目录通常包含以下类型的文件:
- Stylesheets (CSS, SASS, etc.)
- Fonts
- Images

# components
`components/` 目录是您放置所有 Vue 组件的地方,然后可以在您的页面或其他组件中导入这些组件(自动导入)。

# composables
Nuxt 3 使用 `composables/` 目录使用 auto-imports 自动将 Vue 组合导入到应用中!

# content
Method 1: Using named export
composables/useFoo.ts
export const useFoo = () => {
return useState('foo', () => 'bar')
}
Method 2: Using default export
composables/use-foo.ts or composables/useFoo.ts
// It will be available as useFoo() (camelCase of file name without extension)
export default function () {
return useState('foo', () => 'bar')
}

# layouts
# pages
Nuxt 提供了一个基于文件的路由,在您的 web 应用程序中使用 Vue Router 在底层创建路由。

# plugins
# server
`public/` 目录直接服务于服务器根目录,包含必须保留其名称的公共文件(例如:`robots.txt`)或可能不会更改(例如:`favicon.ico`)。

Git 笔记

Git 项目分支协作

  1. 创建仓库:首先,需要有一个中央仓库。可以在 GitHub 上创建一个新的仓库。
  2. 克隆仓库:开发者通过 git clone 命令将远程仓库(通常称之为 origin)克隆到本地。
  3. 创建并切换到新分支:当开始一项新的任务时,开发者应该从主分支(main 分支或 master 分支)创建一个新分支。这可以使用命令 git checkout -b branch-name 来完成。
  4. 进行开发:在新分支上进行开发工作,并且定期使用 git add .git commit -m "message" 将更改添加到新分支。
  5. 与主分支同步:如果主分支有更新,你应该定期地将这些更新合并到你的分支上来保持同步。你可以先通过 git checkout master 切换到主分支,然后通过 git pull origin master 命令拉取最新的主分支。然后再切回你的分支(git checkout <branch-name>),然后通过 git merge master 命令来合并主分支的更改。
  6. 拉取最新代码:在准备将分支合并回主分支之前,应该首先用 git pull origin main 命令获取主分支最新的代码,以保证将要合并的代码是基于最新的主分支代码。
  7. 解决冲突:如果在拉取最新代码后有任何冲突,应当立即解决。解决冲突后,再次执行 git add .git commit -m "message"
  8. 推送到远程仓库:使用 git push origin branch-name 将你的分支推送到远程仓库。
  9. 请求合并:在 GitHub UI 上创建一个新的 Pull Request (PR),请求将你的分支合并到主分支。
  10. 审核和合并:其他开发者会在 PR 中对你的代码进行审核。如果代码没有问题,就可以将其合并到主分支。在 GitHub UI 上,可以直接点击“Merge Pull Request”按钮来完成此操作。
  11. 删除分支:合并完毕后,本地和远程的开发分支通常会被删除。可以使用 git branch -d branch-name 在本地删除分支,然后使用 git push origin :branch-name 删除远程分支。

Git Flow 工作流程

![image](67c816a179fe9b0000001bd5/959e4e19a5ad26788f2cf563.png)

分支管理规范

命名

分支命名以 feature/xx-xxfix/xx-xx 的格式命名,中间用短横线 - 连接。

分支管理

项目需要根据环境的不同创建对应的分支:

master(线上环境)
develop(开发环境)
test(测试环境)
feature/xxx(功能分支)
fix/xxx(修复分支)
• 其他…

Git Commit Message 规范

Git 在每次提交时,都需要填写 commit message

git commit -m 'this is a test'

commit message 就是对你这次的代码提交进行一个简单的说明,好的提交说明可以让人一眼就明白这次代码提交做了什么。

格式

<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>

type

commit 的类型:

feat: 新功能、新特性
fix: 修改 bug
perf: 更改代码,以提高性能(在不影响代码内部行为的前提下,对程序性能进行优化)
refactor: 代码重构(重构,在不影响代码内部行为、功能下的代码修改)
docs: 文档修改
style: 代码格式修改,注意不是 CSS 修改(例如分号修改)
test: 测试用例新增、修改
build: 影响项目构建或依赖项修改
revert: 恢复上一次提交
ci: 持续集成相关文件修改
chore: 其他修改(不在上述类型中的修改)
release: 发布新版本

scope

commit message 影响的功能或文件范围,比如:routecomponentutilsbuild

subject

commit message 的概述

body

具体修改内容,可以分为多行。

一些备注,通常是 BREAKING CHANGE 或修复的 bug 的链接。

示例

fix(global): 修复 checkbox 不能复选的问题
feat: 添加网站主页静态页面

这是一个示例,假设对任务静态页面进行了一些描述。

这里是备注,可以是放 BUG 链接或者一些重要性的东西。
chore: 将表格中的查看详情改为详情

ES6 笔记

for..of

for..of 适用遍历数/数组对象/字符串/map/set,但不能遍历对象。

for (const item of array) {
console.log(item);
}

默认导出与命名导出

// 默认导出
export default function() { ... }

// 命名导出
export const foo = 'bar';

?? 空值合并运算符

const value = null ?? 'default';
console.log(value); // 'default'

其他笔记

• 函数表达式与函数声明语句有什么区别
• 为什么避免在块级作用域内声明函数
• 如果确实需要,应该采用函数表达式而非函数声明语句
• 数组和对象都是通过指针来保存的,所以 const 可以修改
• 可接受误差:Number.EPSILON 这是一个极小值,用于判断浮点数的误差是否可接受
• 无论是解构赋值还是函数传参,都可以添加默认值
• 尾递归优化

CSS BEM 架构

.block {
&__element {
// some styles here

&--modifier {
// some styles here
}
}
}

@extend

.nav {
background-color: steelblue;
&__container {
display: flex;
justify-content: space-between;
}
&__item {
color: white;
&--active {
@extend .nav__item;
border-bottom: 1px solid red;
}
}
}

BEM 命名规范

block 代表了更高级别的抽象或组件(相当于大盒子)
block__element 代表 .block 的后代,用于形成一个完整的 .block 的整体(相当于块中的元素)
block--modifier 代表 .block 的不同状态或不同版本。

V8 引擎原理

V8 引擎原理

  1. JavaScript 源代码
  2. Parse 解析器
  3. AST 抽象语法树(可被序列化为 JSON 格式)
  4. Ignition 解释器
  5. ByteCode 字节码(二进制文件,有可移植性,能进行性能优化)

RESTful API 规范

RESTful API 规范

RESTful API 请求设计

请求 = 动词 + 宾语

GET /zoos/:id 获取某个指定动物园的信息

宾语 URL 应该全部使用名词复数,可以有例外,比如搜索可以使用更加直观的 search

过滤信息(Filtering)如果记录数量很多,API 应该提供参数,过滤返回结果。

?limit=10 指定返回记录的数量
?offset=10 指定返回记录的开始位置

RESTful API 响应设计

客户端的每一次请求,服务器都必须给出回应。回应包括 HTTP 状态码和数据两部分。

API 不需要 1xx 状态码。

服务器回应数据

客户端请求时,要明确告诉服务器,接受 JSON 格式,请求的 HTTP 头的 ACCEPT 属性要设成 application/json

服务端返回的数据,不应该是纯文本,而应该是一个 JSON 对象。服务器回应的 HTTP 头的 Content-Type 属性要设为 application/json

错误处理如果状态码是 4xx,就应该向用户返回出错信息。一般来说,返回的信息中将 error 作为键名,出错信息作为键值即可。

{
"error": "Invalid API key"
}

认证 RESTful API 应该是无状态,每个请求应该带有一些认证凭证。推荐使用 JWT 认证,并且使用 SSL。

Hypermedia 即返回结果中提供链接,连向其他 API 方法,使得用户不查文档,也知道下一步应该做什么。

![](https://article.biliimg.com/bfs/article/019156fbac6e94ced539bdd377a5890456318037.png)
![](https://article.biliimg.com/bfs/article/d1cbff0fe379d9b806aca977adddddfd0373565c.png)

VueUse 常用函数

createGlobalState
将状态保持在全局范围内,以便在 Vue 实例之间复用,实现响应式数据共享。

useAsyncState
响应式获取异步状态,在 Promise 完成后,自动更新状态,这在我们进行异步操作获取数据时很有用

useStorage
响应式操作 LocalStorage/SessionStorage

useDocumentVisibility
响应式跟踪文档可见性 document.visibilityState

useTitle
响应式 document.title

useDraggable
使元素可拖动。

useElementVisibility
跟踪元素在视口中的可见性。

useIntersectionObserver
响应式监听目标元素的可见性。

useMouseInElement
响应式获取鼠标相对于元素的位置

useWindowScroll
响应式获取窗口的滚动位置。

useEventListener
轻松使用 EventListener。在挂载时使用 addEventListener 注册,在卸载时自动使用 removeEventListener。
您还可以传递一个 ref 作为事件目标,useEventListener 当您更改目标时,将注销前一个事件并注册新事件。

onClickOutside
监听元素外部的点击事件,对模态框和下拉菜单等组件很有用。

onKeyStroke
监听键盘事件

useElementHover
响应元素的悬停状态

useMagicKeys
响应式按下状态,支持组合键。

useMouse
响应式获取鼠标位置

useFetch
响应式 Fetch API,提供中止请求、在请求被触发之前拦截请求、在 url 更改时自动重新获取请求以及使用预定义选项创建您自己的请求的能力 useFetch。

useAnimate
使用响应式的 Web Animations API。

useTransition
值之间的转换

useThrottleFn
使用节流函数,限制函数的执行。对于限制处理程序在调整大小和滚动等事件上的执行速度特别有用。

useDebounceFn
使用防抖函数

useDateFormat
根据传入的字符串获取格式化日期

useRefHistory
跟踪 ref 的更改历史,提供撤消和重做功能

useMutationObserver
监听 DOM 的修改.

useWindowSize
响应式获取窗口尺寸

useBroadcastChannel
响应式 BroadcastChannel API,BroadcastChannel 接口代理了一个官博频道,可以让指定 origin 下的任意 browsing context 来订阅它。它允许同源的不同浏览器窗口,Tab 页,frame 或者 iframe 下的不同文档之间相互通信。通过触发一个 message 事件,消息可以广播到所有监听了该频道的 BroadcastChannel 对象。

useDark
具有自动数据持久性的响应式暗色模式。

useFullscreen
响应式全屏 API。它添加了以全屏模式呈现特定元素(及其后代)的方法,并在不再需要时退出全屏模式。这使得可以使用用户的整个屏幕呈现所需的内容(例如在线游戏),从屏幕上删除所有浏览器用户界面元素和其他应用程序,直到关闭全屏模式。

useImage
在浏览器中响应式加载图像,您可以等待结果显示它或回退。

useMediaControls
音频和视频元素的响应式媒体控件。

useMediaQuery
响应式媒体查询。创建 MediaQueryList 对象后,您可以检查查询结果或在结果更改时接收通知。

useScreenOrientation
响应式Screen Orientation API ,它为网络开发人员提供有关用户当前屏幕方向的信息。
如果要锁定方向,您可以将 OrientationLockType 传递给 lockOrientation 函数。

useScreenSafeArea
响应式 env(safe-area-inset-*)

useInfiniteScroll
无限滚动元素。

useParallax
轻松创建视差效果。如果不支持方向,它会使用 useDeviceOrientation 并回退到 useMouse。

useScrollLock
锁定元素的滚动。

useWebSocket
响应式 WebSocket.

useVirtualList
创建虚拟列表。虚拟列表(有时称为虚拟滚动条)允许您高效地呈现大量项目。container 通过使用 wrapper 元素模拟容器元素的全高,它们仅呈现显示元素中的项目所需的最少数量的 DOM 节点。

useVModel
v-model 绑定的简写,props + emit -> ref

whenever
观察值是真实的,然后执行回调。

watchDebounced
防抖 watch

useOffsetPagination
响应式分页器

useToggle
布尔值切换器。

还有一些数学方法:平均值, 求和, 绝对值 等