Skip to content

Z01-02 前端常用-知识点

[TOC]

CSS

初始化样式

css
body,
h1,
h2,
h3,
h4,
h5,
h6,
p,
dl,
dd,
ul,
ol,
li,
form,
input,
textarea,
th,
td,
select,
div,
section,
nav,
span,
i {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
em {
  font-style: normal;
}
li {
  list-style: none;
}
a {
  text-decoration: none;
  color: #333;
}
img {
  border: none;
  vertical-align: top;
}
/* img { font-size: 0; } */
input,
textarea {
  outline: none;
}
textarea {
  resize: none;
  overflow: auto;
}
body {
  font-size: 14px;
}

文字溢出省略号

  • 单行文字溢出

    css
    overflow: hidden; // 溢出隐藏
    text-overflow: ellipsis; // 溢出用省略号显示
    
    white-space: nowrap; // 规定段落中的文本不进行换行

    image-20221104215817385

  • 多行文字溢出

    css
    overflow: hidden; // 溢出隐藏
    text-overflow: ellipsis; // 溢出用省略号显示
    
    display: -webkit-box; // -webkit-, 作为弹性伸缩盒子模型显示
    -webkit-box-orient: vertical; // -webkit-, 设置伸缩盒子的子元素排列方式:从上到下垂直排列
    -webkit-line-clamp: 2; // -webkit-, 显示的行数

    image-20221104215845170

  • 代码:文字溢出省略号

布局

float 布局

flex 布局

认识 Flex 布局

Flex 布局是一种按行或列布局元素的一维布局方法

解决的痛点
  • 垂直居中
  • 等分宽高
  • 多列不等高想占等高的位置
API
  • 容器 container

  • display: flex | inline-flex。弹性盒子

  • flex-direction: row | row-reverse | column | column-reverse | unset。调整主轴方向

  • flex-wrap: wrap | nowrap | wrap-reverse。控制是否换行

  • flex-flow: 简写:flex-direction + flex-wrap

  • justify-content: flex-start | flex-end | center | space-between | space-around | space-evenly。调整主轴对齐

  • align-items: flex-start | flex-end | center | baseline | normal | stretch。调整交叉轴对齐

  • align-content: flex-start | flex-end | center | space-between | space-around | space-evenly | stretch。堆栈(由 flex-wrap 产生的独立行)多行垂直对齐方式

  • 子元素 items

  • flex-grow: <非负数>,默认 0。设置 flex 项主轴尺寸的 flex 增长系数

  • flex-shrink: <非负数>,默认 1。指定了 flex 元素的收缩规则

  • flex-basis: auto | <宽度 px>。指定了 flex 元素在主轴方向上的初始大小

  • flex: 缩写:flex-grow | flex-shrink | flex-basis | none | auto

  • order: number。控制子项目的排列顺序,正序方式排序,从小到大

  • align-self: flex-start | flex-end | center | baseline | stretch | auto 。对齐当前元素,并覆盖已有的 align-items 的值

Flex 布局的模型

image-20221107223208141

概念:

  • main start / main end: 主轴开始 / 结束
  • cross start / cross end: 交叉轴开始 / 结束
  • main size/ cross size: 主轴 / 交叉轴尺寸
  • flex container: flex 容器
  • flex item: flex 元素
item 最终 size 的决定因素

优先级:从高到低

  • 1、max-width / max-height / min-width / min-height
  • 2、flex-basis
  • 3、width / height
  • 4、内容本身的 size
Flex 布局常见问题

问题: Flex 布局中设置 justify-content 后最后一行不对齐,如图:

image-20221107224806555

解决: 在 items 最后加上最少 列数 - 2 个 span,span 设置如下

image-20221107225226027

Grid 布局

圣杯布局

####双飞翼布局

opacity 代替 display 实现动画

  1. display: block 不支持transition动画,可以使用opacity代替。

  2. 当同时使用opacitydisplay: block 时,会受block影响,出现动画不起效果的情况

image-20230106115529353

flex: 0 时文字挤在一起

  1. 图示:

    image-20230106125042962
  2. 分析:flex: 0 表示:flex-grow: 0; flex-basis: 0 ,出现文字挤在一起主要就是因为 flex-basis: 0

  3. 解决: flex: 0 auto ,表示:flex-grow: 0; flex-basis: auto

  4. 图示:

    image-20230106125127239

段落(p)字体大小

段落(p)的字体大小最好设置为 font-size: 14px; line-height: 21px;

预设模块

在使用 pinia,node 这些语言时,总是需要不断的下载,导入第三方模块包,很是繁琐。

可不可以在设计之初就提供一个预设功能,让开发者自己将需要的模块事先添加到预设中,使用的时候就不需要再引入了

隐藏滚动条

image-20230606153959365

css
scrollbar-width: none; /* firefox */

手写icon【

HTML

meta 标签

属性描述示例
charsetUTF-8 等规定 HTML 文档的字符编码<meta charset="UTF-8">
nameviewport<meta name="viewport" content="width=device-width, initial-scale=1.0">
keywords
description
author
generator
application-name
http-equivcontent-type
refresh

JS

offset、client、scroll

offset 系列

  • 获取与当前元素最近的定位父级元素之间的距离
js
offsetLeft;
offsetTop;
  • 内容 + padding + border
js
offsetWidth;
offsetHeight;
  • 获取距离当前元素最近的定位父级元素
js
offsetParent;

client 系列

  • 当前元素 border 的宽度
js
clientLeft;
clientTop;
  • 内容 + padding
js
clientWidth;
clientHeight;

scroll 系列

  • 盒子内容滚动出去的距离(包括边框)
js
scrollLeft;
scrollTop;
  • 整个内容的宽度和高度 可见内容 + 滚动出去的内容
js
scrollWidth;
scrollHeight;

Reflect.construct 返回实例【

问题: 通过Reflect.construct(Student, ['jack', 4], Person) 创建的实例对象为什么是父类 Person 的实例,而不是子类 Student 的实例?

js
/* 3. Reflect中的constructor */
class Person {
  constructor(name) {
    this.name = name;
  }
}

class Student extends Person {
  constructor(name, grade) {
    super(name);
    this.grade = grade;
  }
}
const stu =
  Reflect.construct(Student, ["jack", 4], Person) +
  console.log(stu) + // Person {name: 'tom', age: 20}
  console.log(stu instanceof Student) + // false
  console.log(stu instanceof Person); // true

总结判断数据类型的方法【

typeof

判断各数据类型是否为false

  1. 布尔类型(Boolean)

    • false:明确表示假的布尔值。
  2. 空字符串(String)

    • '':长度为零的字符串被视为假。
  3. 数字类型(Number)

    • 0:数字 0 被视为假。
    • NaN(非数字值):特别注意,NaN 是一个奇特的数值类型,它也被视为假。
  4. null

    • null:表示空值的关键字。
  5. undefined

    • undefined:表示未定义的变量或属性。
  6. 判断空数组[]

    js
    [].length // 0
  7. 判断空对象{}

    js
    Object.keys({}).length // 0

自定义滚动条

视频: https://v.anxia.com/?pickcode=bmcixmiwaicg98476&share_id=0

实现思路:

  • 禁止系统的滚动条:因为各大浏览器的系统滚动条风格不一,有可能影响我们的页面布局。
  • 在body当中添加一个盒子模拟body区域,在这个盒子的右侧定位一个盒子模拟滚动条。
  • 自定义滚动条的万能比例:滚动条高度 / 屏幕的高度 = 屏幕的高度 / 内容的高度 = 滚动条的移动距离 / 内容的滚动距离

▸ 基本实现:

1、HTML布局

image-20241111102323359

2、CSS样式

image-20241111102558680

3、JS功能:拖拽滚动条

image-20241111102711231

Element-Plus

修改input中placeholder样式

image-20240523160405594

css
/* input中的placeholder */
.el-input__inner::placeholder,
.el-select__placeholder {
  font-size: 12px;
}

Vue

设置用户片段

在 VSCode 中设置用户片段

  • 1、模板
vue
<template>
  <div class="${1:home}">${1:home}</div>
</template>

<script setup></script>

<style lang="less" scoped></style>
  • 2、将模板在 https://snippet-generator.app 网站上转化成 json 格式,设置 trigger:vuesetup,和简介
  • 3、在 VSCode - 文件 - 首选项 - 配置用户代码片段 - [输入 vue.json] - 复制转化后的模板到 vue.json 中
  • 4、使用时输入:vuesetup 即可

调用函数

疑惑: 不了解Vue文档这里的意思

image-20230913095204371

指令

  • 显示文本
  • v-text返回:,更新元素的文本内容textContent。等同于
  • v-html返回:,更新元素的innerHTML
  • v-pre返回:,所有 Vue 模板语法都会被保留并按原样渲染。如显示原始双大括号标签及内容。
  • 是否显示、条件分支
  • v-show返回:,基于表达式值的真假性,来改变元素的可见性。通过修改display属性值控制是否显示
  • v-if返回:,基于表达式值的真假性,来条件性地渲染元素或者模板片段。
  • v-else返回:,和v-ifv-else-if一起组成条件分支
  • v-else-if返回:,和v-ifv-else一起组成条件分支
  • 循环判断
  • v-for返回:,基于原始数据多次渲染元素或模板块。
  • 绑定
  • v-bind返回:,动态的绑定一个或多个 attribute,也可以是组件的 prop。缩写::
  • v-on返回:,给元素绑定事件监听器。缩写:@
  • v-model返回:,在表单输入元素或组件上创建双向绑定。
  • 插槽
  • v-slot返回:,用于声明具名插槽或是期望接收 props 的作用域插槽。缩写:#
  • 返回:
js
示例:
    <a v-bind:href="url"> ... </a>
	<a v-on:click="doSomething"> ... </a>

指令: v-bind
参数: href, click
参数值: url, doSomething

React

模板命令

基本方法

sh

imn→	import 'module'

imp→	import moduleName from 'module'
imd→	import { destructuredModule } from 'module'

exp→	export default moduleName
exd→	export { destructuredModule } from 'module'

dob→	const {propName} = objectToDescruct
dar→	const [propName] = arrayToDescruct

cp→		const { } = this.props
cs→		const { } = this.state

React片段

1、类组件

rpce

js
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'

export class FileName extends PureComponent {
  static propTypes = {}

  render() {
    return <div>$2</div>
  }
}

export default FileName

rce

js
import React, { Component } from 'react'

export class FileName extends Component {
  render() {
    return <div>$2</div>
  }
}

export default $1

2、函数组件

rmc

js
import React, { memo } from 'react'

export default memo(function $1() {
  return <div>$0</div>
})

rfce

js
import React from 'react'

function $1() {
  return <div>$0</div>
}

export default $1

rfcp

js
import React from 'react'
import PropTypes from 'prop-types'

function $1(props) {
  return <div>$0</div>
}

$1.propTypes = {}

export default $1

全部命令:https://github.com/ults-io/vscode-react-javascript-snippets/blob/HEAD/docs/Snippets.md

Git

GitHub 常用快捷键

  • .

  • 个人登录令牌:ghp_xxx

sh
#
git remote set-url origin
https://ghp_xxx@github.com/PikachuTT/note-web-common.git

image-20221104225724625

Git 提交时提示 Permission denied

git commit 提交时错误:

sh
$ git commit -m "mr-music - 播放 - 背景、导航栏(完成)"
fatal: could not open '.git/COMMIT_EDITMSG': Permission denied

原因:可能某些时候修改了隐藏文件而不再具有对隐藏文件的写入权限

解决:进入.git 文件(隐藏文件)删除“COMMIT_EDITMSG”文件即可

VSCode

Git 提交时 CRLF 冲突

解决:

在 【文件 - 首选项 - 设置 - 搜索 eol】修改 eol 为 \n(LF)

image-20230221105817784

分析:

git 在维护版本库的时候统一使用的是 LF,这样就可以保证文件跨平台的时候保持一致。

在 Linux 下默认的换行符也是 LF,那也就不存在什么问题。

在 Windows 下默认的换行符是 CRLF,那么我们需要保证在文件提交到版本库的时候文件的换行符是 LF

如果你同事中有使用其它系统开发的

你需要先执行上面的操作,再行 以下代码才能解决

sh
git config --global core.autocrlf true

take over

在vscode 的插件中搜索:@builtin typescript,禁用该内置插件

image-20240402151516933

注意: Vue - Official@2.0.8版本已经解决了之前升级为2.x版本时的BUG,无需再使用take over模式了

vscode自动格式化md文件

问题:

vscode做vue的项目开发,需要对代码使用prettier格式化,所以会在settings.json中如下配置:

json
"editor.formatOnSave": true,

但是该配置时全局生效的,这就导致在md文件中保存时也会自动格式化

解决:

可以在settings.json中添加如下配置,为md文件单独设置规则:

json
  "[markdown]": {
    "editor.formatOnSave": false
  }

md

当前可以用作typora高亮的HTML标签有:<q><b>

<del>标签在typora中会使用到,但是在cnblogs中使用的是<s>

webpack【】

115

解决高版本 115 无法重命名问题

高版本: 115br_v8.6.3.5 以上

解决:

91版本以下:

image-20230128124807589

91到94版本:

打开Chrome的快捷方式的属性,在目标后添加:

--flag-switches-begin --disable-features=SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure --flag-switches-end

或者:

--disable-features=SameSiteByDefaultCookies

image-20240327090042230

94版本以后:

2021年9月,已经彻底移除可视化禁用和命令行禁用的方式,详见官方的SameSite Updates

但是Chrome浏览器插件不受跨站跨域的限制,所以对于本地调试的场景,可以通过安装相关cookie透传的插件来解决。

如果需要解决所有用户的这个问题,则需要使用正规的方式解决:前后端地址不跨站,或者使用https+SameSite=None

插件下载地址

安装方式

  1. 打开扩展程序chrome://extensions/
  2. 将下载的zip插件拖拽进去

image-20240327090400615

搜索页面宽度修改【】

image-20240525170500422

跨域

React项目跨域问题

~~描述:~~在React项目中,当请求的API在其他服务器上时,会出现跨域错误:

sh
Access to XMLHttpRequest at 'http://codercba.com:9002/song/detail?ids=408332757' from origin 'http://localhost:3000' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'http://localhost:5173' that is not equal to the supplied origin.

解决方案一: 在webpack配置文件或craco.config.js中添加代理配置

js
  webpack: {
    ...
    // 解决跨域问题
    devServer: {
      proxy: {
        '/api': {
          target: 'http://codercba.com:9002', // 实际API资源存储地址
          changeOrigin: true, // 改变代理时主机头的来源
          pathRewrite: {
            '^/api': '' // 改回请求url前的/api前缀
          }
        }
      }
    }
  }

解决方案二: 在项目根目录添加setupProxy.js文件,并配置代理

js
const proxy = require('http-proxy-middleware')
const proxy = require('http-proxy-middleware')
module.exports = function(app){
    app.use(
        proxy('/api1',{
            target:'http://codercba.com:9002',
            changeOrigin:true,
            pathRewrite:{'/api1':''}
        }),
        proxy('/api2',{
            target:'http://codercba.com:5000',
            changeOrigin:true,
            pathRewrite:{'/api2':''}
        })
    )
}

解决方案三: 安装Chrome插件Allow CORS: Access-Control-Allow-Origin

解决方案四: 修改React项目的端口为5173【无效】

image-20231207151754230

手写

手写 call,aplly,bind

函数对象原型关系

函数 foo 对象的隐式原型 === Function 的显式原型

js
// 函数foo对象的隐式原型 === Function的显式原型
console.log(foo.__proto__ === Function.prototype); // true

console.log(Function.prototype.apply); // f apply()
console.log(Function.prototype.call); // f call()
console.log(Function.prototype.bind); // f bind()

console.log(Function.prototype.apply === foo.apply); // true

结论:

  1. foo 对象中的某些属性和方法是来自 Function.prototype 的
  2. 在 Function.prototype 中添加的属性和方法,可以被所有的函数获取

image-20230224205957711

image-20230224210004633

在 Function 的原型中添加方法 bar

image-20230224211742738

手写 apply 方法

image-20230224211954891

给函数对象添加方法

image-20230224212210173

js
function foo() {
  console.log("foo", this);
}

Function.prototype.mrapply = function (mrthis) {
  // 相当于 mrthis.fn = this
  Object.defineProperty(mrthis, "fn", {
    configurable: true,
    value: this,
  });
  // 隐式调用fn,可以让fn函数的this指向 mrthis
  mrthis.fn();
  // 删除多出来的临时函数fn
  delete mrthis.fn;
};

foo.mrapply({ name: "Tom" });

如果传入的参数是一个 String 或者 Number 的类型,需要将其包裹成对象类型,才能在它上面添加属性

image-20230224214610343

image-20230224214547854

调用 mrapply 时,传递参数

image-20230224214829699

js
    function foo (age, height) {
      console.log('foo', this, age, height)
    }

+    Function.prototype.mrapply = function(mrthis, args) {
      // 当this不是对象时,需要用Object包裹
      mrthis = (mrthis === null || mrthis === undefined) ? window : Object(mrthis)

      // 相当于 mrthis.fn = this
      Object.defineProperty(mrthis, 'fn', {
        configurable: true,
        value: this
      })

      // 隐式调用fn,可以让fn函数的this指向 mrthis
+      mrthis.fn(...args)

      // 删除多出来的临时函数fn
      delete mrthis.fn
    }

+    foo.mrapply({name: "Tom"}, [18, 1.88])
    foo.mrapply(null, [18, 1.88])
    foo.mrapply(undefined, [18, 1.88])
    foo.mrapply(true, [18, 1.88])
    foo.mrapply(123, [18, 1.88])
    foo.mrapply('aaaa', [18, 1.88])

手写 call 方法

js
    function foo(age, height) {
      console.log('foo', this, age, height)
    }

+    Function.prototype.mrcall = function(mrthis, ...args) {
      mrthis = (mrthis === null || mrthis === undefined) ? window : Object(mrthis)

      Object.defineProperty(mrthis, 'fn', {
        configurable: true,
        value: this
      })

+      mrthis.fn(...args)

      delete mrthis.fn
    }

+    foo.mrcall({ name: "张飞" }, 20, 1.77)

抽取封装公共函数

js
    /* 抽取封装的函数 */
+    Function.prototype.mrexec = function(mrthis, args) {
      mrthis = (mrthis === null || mrthis === undefined) ? window : Object(mrthis)
      // mrthis.fn = this
      Object.defineProperty(mrthis, 'fn', {
        configurable: true,
        value: this
      })
      mrthis.fn(...args)
      delete mrthis.fn
    }

    /* 手写apply */
    Function.prototype.mrapply = function(mrthis, args) {
      this.mrexec(mrthis, args)
    }

    /* 手写call */
    Function.prototype.mrcall = function(mrthis, ...args) {
      this.mrexec(mrthis, args)
    }

    // 测试
    function foo(age, height) {
      console.log('foo', this, age, height)
    }
    foo.mrapply({name: "Tom"}, [19, 1.66])
    foo.mrcall({name: "Jack"}, 22, 1.99)

手写 bind 方法

和 apply, call 不同,bind 执行后是返回一个新的函数 newFoo

image-20230225114537180

基础实现

思路:想办法实现如下:

js
// 伪代码
{ name: "why" }.foo(name, age)
js
    /* 手写bind */
    Function.prototype.mrbind = function(mrthis, ...args) {
+      return (...moreArgs) => {
        mrthis = (mrthis === null || mrthis === undefined) ? window : Object(mrthis)
        Object.defineProperty(mrthis, 'fn', {
          configurable: true,
          value: this
        })
+        const allArgs = [...args, ...moreArgs]
+        mrthis.fn(...allArgs)
+        delete mrthis.fn // 可以删除fn,因为每次调用newFoo,都会重新生成一个mrthis.fn
      }
    }

    // 测试
    function foo(name, age, height, address) {
      console.log('foo', this, name, age, height, address)
    }
    const newFoo = foo.mrbind({name: "Jerry"}, '张飞', 45)
    console.log(newFoo)
+    newFoo(1.88, '成都')
+ 	 newFoo(1.88, '成都')

浅拷贝,深拷贝

引用赋值

image-20230228131039985

浅拷贝

方式:

  • 解构赋值:const info = {...obj}

image-20230228131318516

浅拷贝修改 info2.name 后,obj 的 name 依然是"why",被修改的只是 info2

image-20230228131406936

浅拷贝的内存图

image-20230228131822253

如果 obj 对象中有**其他对象(或数组)**时的内存图

image-20230228132402319

深拷贝

方式:

  • 1、借助第三方库:underscore
  • 2、利用现有 JS 机制:JSON
  • 3、自己实现:

2、利用现有 JS 机制:JSON

语法:

js
const info3 = JSON.parse(JSON.stringify(obj));

缺点: 该方法不能实现方法的深拷贝,会忽略 obj 对象中的方法

js
    const obj = {
      name: 'Tom',
      age: 18,
      friend: {
        name: 'Jack'
      },
      run: function() {
        console.log(this.name + '在跑步~');
      }
    }

    // 利用JSON机制实现深拷贝
+    const info = JSON.parse(JSON.stringify(obj))

    // 测试
    console.log(info)
 	// 修改info的深度属性,obj的深度属性保持不变
+    info.friend.name = '张飞'
+    console.log('obj', obj.friend.name); // obj Jack
+    console.log('info', info.friend.name); // obj 张飞

	// 不能实现方法的深拷贝,会忽略obj对象中的方法
+    info.run() // ncaught TypeError: info.run is not a function

Promise【】

任务: 手写Promise

轮播图【】

任务: 分别使用JS原生、Vue、React手写一个轮播图

遍历目录下所有文件

  • fs.readdirSync()(path, options?),用于读取指定目录中的所有文件和子目录,并返回一个包含文件名的数组。

    • pathstring | Buffer | URL,想要读取的目录的路径。

    • options?'utf8' | Object默认:Buffer,它有两种不同的形式:

      • 'utf8':以 UTF-8 编码返回文件名,
      • Object{encoding?, withFileTypes?, recursive?},包含以下选项的对象
        • encoding?string默认:'utf8',如果是utf8,返回字符串名字;如果不设或其他编码格式,返回Buffer格式的名字。
        • withFileTypes?boolean默认:false,是否返回包含fs.Dirent对象的名字。
        • recursive?boolean,是否递归遍历目录及子目录。
    • 返回:

    • filesstring[] | Dirent[],返回包含文件的数组。

      • 如果withFileTypes:false返回string[]包含文件名的数组

        如果withFileTypes:false返回Dirent[]对象数组,每个 Dirent 对象代表目录中的一个文件或子目录。

    • js
      const fs = require('fs');
      
      try {
        const files = fs.readdirSync('/path/to/directory');
        console.log(files);  // 输出文件和子目录的名字(数组)
      } catch (err) {
        console.error('读取目录失败:', err);
      }
  • dirent.isDirectory()(),判断是否是一个目录。需要设置withFileTypes: true返回一个Dirent对象才能判断。

    • 返回: boolean,如果为true,表示是一个目录;如果为false,表示不是一个目录。

    • js
      const fs = require('fs');
      
      // 读取目录,使用 withFileTypes: true 来获取 Dirent 对象
      const files = fs.readdirSync('/path/to/directory', { withFileTypes: true });
      
      files.forEach(file => {
        if (file.isDirectory()) {
          console.log(`${file.name} 是一个目录`);
        } else {
          console.log(`${file.name} 不是一个目录`);
        }
      });

代码实现:

js
// 环境:node
// 运行:node ./index.js

const path = require('node:path')
const fs = require('node:fs')

/**
 * 遍历目录下所有文件
 * @param {string} dir 需要遍历的目录
 * @param {object} [options] 遍历选项
 * @param {boolean} [options.recursive = true] 是否遍历子目录,默认值true
 * @param {boolean} [options.withDirectory] 是否包含目录,默认值true。
 * @returns {{type, filename}[]} allFiles 返回所有文件,包括目录和子目录下的文件
 * @example
 * // 基本使用:递归遍历目录,并包含目录
 * scanFolder('path/to/dir')
 *
 * @example
 * // 其他用法:不遍历子目录,不包含目录
 * scanFolder('path/to/dir', { recursive: false, withDirectory: false })
 */
function scanFolder(dir, { recursive = true, withDirectory = true } = {}) {
  const allFiles = []
  const rootFolder = dir
  function scan(folder) {
    // 核心API:fs.readdirSync()
    const files = fs.readdirSync(folder, { withFileTypes: true })
    files.forEach((file) => {
      const name = path.resolve(file.path, file.name).replace(rootFolder + '\\', '')
      if (file.isDirectory()) {
        if (withDirectory) {
          allFiles.push({ type: 'dir', filename: name })
        }
        if (recursive) {
          scan(path.resolve(folder, file.name))
        }
      } else {
        allFiles.push({ type: 'file', filename: name })
      }
    })
  }
  scan(dir)
  return allFiles
}

测试:

js
// 返回结果:
// [
//   { type: 'dir', filename: 'd01-scan-folder' },
//   { type: 'file', filename: 'd01-scan-folder\\index.js' },
//   { type: 'dir', filename: 'demo' },
//   { type: 'file', filename: 'demo\\web.js' },
//   { type: 'file', filename: 'test.js' }
// ]
const dir = path.resolve(__dirname, '../') // D:\2024\Learn\Web\S21-Demo
console.log(scanFolder(dir))

// console.log(scanFolder(dir, { recursive: false, withDirectory: false }))

image-20241114170132392

Vitepress

问题: 打包时报错:ReferenceError: document is not defined

image-20240919083929291

分析: 这是因为在 .vitepress/theme/index.js 文件中在node环境中调用了document

image-20240919084107144

解决: 将自定义代码放入 enhanceApp 函数中并使用 app.mixin() 来混入生命周期钩子mounted来确保在浏览器加载后执行。

js
// 最终代码

// https://vitepress.dev/guide/custom-theme
import { h } from 'vue'
import DefaultTheme from 'vitepress/theme'
import './style.css'

/** @type {import('vitepress').Theme} */
export default {
  extends: DefaultTheme,
  Layout: () => {
    return h(DefaultTheme.Layout, null, {
      // https://vitepress.dev/guide/extending-default-theme#layout-slots
    })
  },
  enhanceApp({ app, router, siteData }) {
    // 自定义代码
    app.mixin({
      mounted() {
        const els = document.querySelectorAll('.content a')
        console.log(els)

        Array.from(els).forEach((el) => {
          console.log(el.innerHTML, /^/.test(el.innerHTML))
          if (/^/.test(el.innerHTML)) {
            el.style.color = 'blue'
          }
        })
      }
    })
  }
}

猴油

使用插件

jquery

引入jquery

1、通过@require 引入jquery插件的CDN地址

js
// @require      https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js

2、在代码区添加/* globals jQuery, $, waitForKeyElements */

否则会报以下错误:

image-20240417181448437

3、完整代码:

js
// ==UserScript==
// @name         New Userscript
// @namespace    http://tampermonkey.net/
// @version      2024-04-17
// @description  try to take over the world!
// @author       You
// @match        https://www.ysjsxcs.com/histsory
// @icon         https://www.google.com/s2/favicons?sz=64&domain=ysjsxcs.com

+ // @require      https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js

// @grant        none
// ==/UserScript==

// 在页面加载完成后执行代码
(function() {
+ /* globals jQuery, $, waitForKeyElements */

// 在页面加载完成后执行代码
$(document).ready(function() {
    // 使用 jQuery 选择器选择元素,并添加点击事件
    $('#showbook').click(function() {
        // 弹出消息框
        alert('Button clicked!');
    });
});
    
})()
问题集

问题: Uncaught TypeError: Cannot read property 'msie' of undefined

image-20240918165611988

  • 使用jquery修改115搜索页面样式后,无法显示工具栏

    image-20240918170308497

  • 点击感叹号小图标,无法弹出属性

    image-20240918170246205

分析: 这是因为从jquery@1.9开始废除了$.browser这个API,推测在猴油中使用jquery时调用了这个API,所以就会报错。

解决:

  • 方法一:使用1.9之前的版本:https://cdn.bootcdn.net/ajax/libs/jquery/1.8.3/jquery.min.js(OK)
  • 方法二:安装另一个插件:jquery-migrate(测试无效)

axios

1、通过@require 引入axios插件的CDN地址

js
// @require      https://cdn.bootcdn.net/ajax/libs/axios/1.5.0/axios.min.js

2、在代码区添加/* globals axios */

否则会报以下错误:

image-20240417181448437

3、代码:

js
  /* 请求bannedActress */
  getBannedActress()
  function getBannedActress() {
    axios.get('http://120.26.226.124:8000/moment/26').then((res) => {
      console.log(res)
    })
  }

问题: 上面的代码报了以下的错误:

sh
userscript.html?name=98%25E5%25A0%2582.user.js&id=b8080489-58aa-4fe3-8893-a06cad3b8401:2 Mixed Content: The page at 'https://wk8v3.app/forum-37-369.html' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://120.26.226.124:8000/moment/26'. This request has been blocked; the content must be served over HTTPS.

这是由于访问的API地址,不是https协议。需要升级为https协议

免费图床

TIP

技术栈:

  • github + jsdelivr + picgo + typora
  • 七牛云 + picgo + typora

picgo文档: https://picgo.github.io/PicGo-Doc/zh/guide/

github + jsdelivr + picgo + typora

注意:

  • github图床仓库内容不能超过1GB,因为github原则上是反对仓库图床化的,超过1GB之后会由人工审核仓库内容,如果仓库被发现用来做图床,轻则删库重则封号
  • jsdelivr加速的单文件大小为50MB,这就限制了单张图片大小上限

github

1、首先创建一个图床仓库(image)用来放图片,名称随意。

2、访问Github->头像(右上角)->Settings->Developer Settings->Personal access tokens->generate new token,创建的Token名称随意,但必须勾选repo项。

注意: Token只会显示一次,必须记牢,如果忘了就只能重新配置

image-20240705111034987

picgo

1、安装picgo:https://github.com/Molunerfinn/PicGo

2、设置picgo:

  • 设置github图床

    注意: jsdelivr的地址格式为:https://cdn.jsdelivr.net/gh/【github号】/image@main

    image-20240705111408290

  • 设置server端口(默认36677即可)

    image-20240705111800167

  • 只选中github

    image-20240705111900848

typora

1、选择文件 - 偏好设置 -图像,设置picgo为贴图时的默认插件

image-20240705112240620

2、设置完毕

七牛云 + picgo + typora

七牛云

TIP

七牛云会给实名注册用户分配每个月10G的免费空间

1、注册和实名认证(略)

2、新建图床空间:

对象存储 Kodo -> 空间管理 -> 新建空间

image-20240722110523373

新建空间选项:

image-20240722110725261

3、绑定域名

注意: 新建空间成功后会提供一个临时的域名(30天),所以一定要配置一个自己的域名(需要备案)

HTTP / HTTPS费用:https://www.qiniu.com/prices/qcdn

image-20240722114628161

4、解析域名,配置CNAME

在七牛云赋值绑定域名的CNAME

image-20240722114937788

去阿里云(域名厂商)解析绑定的域名,并添加CNAME

image-20240722115424177

5、配置成功

picgo

1、安装picgo: https://github.com/Molunerfinn/PicGo

2、设置picgo:

设置七牛云图床

  • AccessKey/SecretKey:在头像 -> 密钥管理
  • 存储区域:在 picgo文档 中可以查看存储区域代号z0对应的存储区域

image-20240722120115274

设置server端口(默认36677即可)

image-20240705111800167

typora

见:typora