diff --git "a/2023/11/20/C\350\257\255\350\250\200\345\207\275\346\225\260/index.html" "b/2023/11/20/C\350\257\255\350\250\200\345\207\275\346\225\260/index.html" new file mode 100644 index 0000000..fd05bc8 --- /dev/null +++ "b/2023/11/20/C\350\257\255\350\250\200\345\207\275\346\225\260/index.html" @@ -0,0 +1,665 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + my new post - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ + + +
+
+
+ + +
+ +
+
+
+
+

my new post

+ + +
+ +

ctype.h

+
1.isascii()
+

判断是不是在0-127

+

是ASCII 是1

+

不是ASCII 是0

+
2.isalpha
+

小写2 大写1 不是字母0

+
3.isdigit
+

是不是十进制

+

0-9 返回非0

+

不然返回0

+
4.isalnum()
+

是不是字母或者数字

+

是返回非0 不是返回0

+
5.isblank()
+

是不是空白字符

+

是 输出非0

+

不是 输出0

+
6.isspace()
+

是不是空白字符

+

‘ ’ or ‘\t’ or ‘ \r’ or ‘ \n’ or ‘ \v ’or ‘\f ’

+
7.islower()
+

是不是小写字母

+

是为 非零 不是为0

+
8.isupper()
+

是不是大写字母

+

是 非零 不是零

+
9.toupper()
+

转换为大写字母

+

touppper(‘a’)

+
10.tolower()
+

转换为小写字母

+

tolower(‘A’)

+
+ + +
+ +
+
+ + + + + + +
+
+
my new post
+
http://example.com/2023/11/20/C语言函数/
+
+
+ +
+
Author
+
Shanyujia
+
+ + +
+
Posted on
+
November 20, 2023
+
+ + + +
+
Licensed under
+
+ + + + + + + + + + +
+
+ +
+
+
+ + + + +
+ +
+ + +
+
+ +
+ + +
+
+
+
+ +
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/2023/11/27/javascript\347\237\245\350\257\206\347\202\271/index.html" "b/2023/11/27/javascript\347\237\245\350\257\206\347\202\271/index.html" new file mode 100644 index 0000000..75a0346 --- /dev/null +++ "b/2023/11/27/javascript\347\237\245\350\257\206\347\202\271/index.html" @@ -0,0 +1,672 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + javascript1 - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ + + +
+
+
+ + +
+ +
+
+
+
+

javascript1

+ + +
+ +

javascript第一章

+
一元运算符
+
+
1
2
3
4
console.log(typeof 4.5)
//输出 number
console.log(typeof "x")
//输出 string
+

typeof生成一个字符串值 得出你给它的值的类型名

+
布尔值
+

有两个值 truefalse

+

生成布尔值的方法

+
1
2
3
4
console.log (3>2)
// true
console.log (3<2)
// false
+

符号 > 和 < 表示 “大于”和“小于” 二元运算符

+

返回的结果是一个布尔值,表示其运算是否为真。

+

也可以用这种方法比较字符串

+
1
2
console.log("Aardvark" < "Zoroaster")
// → true
+

在 JavaScript 中,只有一个值不等于其自身,那就是NaN(非数值)

+
1
2
console.log (NaN == NaN)
// → false
+

NaN用于表示非法运算的结果

+

逻辑运算符

+

&& 运算符表示逻辑与,该运算符是二元运算符,只有当赋给它的两个值均为true时其结果才是真

+
1
2
3
4
console.log(true && false)
// → false
console.log(true && true)
// → true
+

||运算符表示逻辑或。当两个值中任意一个为true时,结果就为真

+
1
2
3
4
console.log(false || true)
// → true
console.log(false || false)
// → false
+

优先级问题

+

||优先级最低,其次是&&,然后是比较运算符(>,==,<)

+

最后是其他运算符

+

三元预算符

+

A?B:C

+
1
2
3
4
console.log(true ? 1 : 2);
// → 1
console.log(false ? 1 : 2);
// → 2
+

被叫做条件运算符

+

问号左面的值挑选一个值

+

它为 true 是 选择中间的值

+

它为false 是选择右面的值

+
空值
+

null undefined

+

表示不存在有意义的值

+

两者差别不大

+ + +
+ +
+
+ + + + + + +
+
+
javascript1
+
http://example.com/2023/11/27/javascript知识点/
+
+
+ +
+
Author
+
Shanyujia
+
+ + +
+
Posted on
+
November 27, 2023
+
+ + + +
+
Licensed under
+
+ + + + + + + + + + +
+
+ +
+
+
+ + + + +
+ + +
+ +
+ + +
+
+
+
+ +
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/2023/12/09/Javascript-\345\207\275\346\225\260/index.html" "b/2023/12/09/Javascript-\345\207\275\346\225\260/index.html" new file mode 100644 index 0000000..1d7945f --- /dev/null +++ "b/2023/12/09/Javascript-\345\207\275\346\225\260/index.html" @@ -0,0 +1,714 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Javascript 函数 - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ + + +
+
+
+ + +
+ +
+
+
+
+

Javascript 函数

+ + +
+ +

JavaScript 函数

+

1.typeof

+

会产生一个字符串的值,内容是给定值的具体类型

+
1
2
3
4
console.log(typeof 4.5)
// → number
console.log(typeof "x")
// → string
+

2.prompt

+

包含一函数,个显示一个小对话框,请求用户输入

+
1
prompt("Enter passcode");
+

3.cosole.log

+

输出值

+
1
2
3
let x = 30;
console.log("the value of x is", x);
// → the value of x is 30
+

4.Math.max

+

接受任意数量的参数并返回最大

+
1
2
console.log(Math.max(2, 4));
// → 4
+

5.Math.min

+

跟Math.max相反

+
1
2
console.log(Math.min(2, 4) + 100);
// → 102
+

6.Number.isNaN

+

仅当它给出的参数是NaN时才返回true

+

当你给它一个不代表有效数字的字符串时

+

Number函数恰好返回NaN

+

7.函数定义

+
1
2
3
4
function square(x)
{
return x*x;
}
+

以关键字function起始的表达式创建

+

函数有一组参数(例子中只有x)和一个主体

+

它包含调用该函数时要执行的语句

+

return 决定 函数返回值

+

没有return的函数 返回undefined

+

8.箭头函数

+

=>

+
1
2
const square1 = (x) => { return x * x; };
const square2 = x => x * x;
+

箭头出现在参数列表之后,然后是函数主体

+

表达的意思类似于 这个输入(参数)产生这个结果(主体)

+

9.push

+

将值添加到数组的末尾

+
1
2
3
4
5
6
7
8
9
let sequence = [1, 2, 3];
sequence.push(4);
sequence.push(5);
console.log(sequence);
// → [1, 2, 3, 4, 5]
console.log(sequence.pop());
// → 5
console.log(sequence);
// → [1, 2, 3, 4]
+

10.pop

+

与push相反 删除数组中最后的一个值并将其返回

+

11.delete

+

一元运算符

+

当应用于对象属性时,将从对象中删除指定的属性

+
1
2
3
4
5
6
7
8
9
10
let anObject = {left: 1, right: 2};
console.log(anObject.left);
// → 1
delete anObject.left;
console.log(anObject.left);
// → undefined
console.log("left" in anObject);
// → false
console.log("right" in anObject);
// → true
+

12.in

+

二元运算符 会告诉你该对象是否具有名称为它的属性

+

将属性设置为 undefined 和实际删除它的区别在于

+

在设置为undefined的时候 对象仍然具有属性 只是没有意义

+

删除它时 属性不再存在 in 会返回 false

+

13.Object.keys

+

给它一个对象 它返回一个字符串数组 -对象的属性名称

+
1
2
console.log(Object.keys({x: 0, y: 0, z: 2}));
// → ["x", "y", "z"]
+

14.Object.assgin

+

可以将一个对象的所有属性复制到另一个对象中

+
1
2
3
4
let objectA = {a: 1, b: 2};
Object.assign(objectA, {b: 3, c: 4});
console.log(objectA);
// → {a: 1, b: 3, c: 4}
+

15.Math.sqrt

+

平方根函数

+

16.unshift

+

在数组开头添加元素

+

17.shift

+

在数组的开头删除元素

+

remember("groceries")将任务添加到队列的末尾

+

getTask()从队列中获取(并删除)第一个项目

+

rememberUrgently函数也添加任务 .

+

但将其添加到队列的前面而不是队列的后面

+
1
2
3
4
5
6
7
8
9
10
let todoList = [];
function remember(task) {
todoList.push(task);
}
function getTask() {
return todoList.shift();
}
function rememberUrgently(task) {
todoList.unshift(task);
}
+

18.lastIndexOf

+

indexof 类似 indexof从数组第一个元素开始搜索

+

lastIndexOf 从最后一个元素向前搜索

+
1
2
3
4
console.log([1, 2, 3, 2, 1].indexOf(2));
// → 1
console.log([1, 2, 3, 2, 1].lastIndexOf(2));
// → 3
+

19.Math

+

Math.max最大值

+

Math.min最小值

+

Math.sqrt平方根

+

Math.random生成一个随机数 范围在 0(包括)到 1(不包括)之间

+

Math.PI表示数字π

+

Math.floor向下取整到与当前数字最接近的整数

+

Math.ceil向上取整

+

Math.abs取数字的绝对值

+

Math.round四舍五入

+ + +
+ +
+
+ + + + + + +
+
+
Javascript 函数
+
http://example.com/2023/12/09/Javascript-函数/
+
+
+ +
+
Author
+
Shanyujia
+
+ + +
+
Posted on
+
December 9, 2023
+
+ + + +
+
Licensed under
+
+ + + + + + + + + + +
+
+ +
+
+
+ + + + +
+ + +
+ +
+ + +
+
+
+
+ +
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/2023/12/16/Javascript-class\347\261\273/index.html" "b/2023/12/16/Javascript-class\347\261\273/index.html" new file mode 100644 index 0000000..15df7e4 --- /dev/null +++ "b/2023/12/16/Javascript-class\347\261\273/index.html" @@ -0,0 +1,653 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Javascript-class类 - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ + + +
+
+
+ + +
+ +
+
+
+
+

Javascript-class类

+ + +
+ +

Javascript-class类

+

类的定义

+

有两个组成部分

+

类表达式和类声明

+

每个类中包含了一个特殊的方法 constructor(),它是类的构造函数,这种方法用于创建和初始化一个由 class 创建的对象

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//类声明
class A {
constructor(name,age)
{
this.name=name;
this.age=age;
}
}
//类表达式
const B=class{
constructor(name,age)
{
this.name=name
this.age=age
}
}
+

以上创造了一个类 叫A

+

初始化了两个参数 name , age

+

使用类

+
1
2
3
const a1=new A ('shanyujia',18)
console.log(a1)
// =>{ name: 'shanyujia', age: 18 }
+

基于这个类去创造这个类的实例

+

基于这个对象生成 结构相同 内部数据不同的对象形式

+

自定义方法

+
1
2
3
4
5
6
7
8
9
10
11
12
class A {
constructor(name,age)
{
this.name=name;
this.age=age;
}
introduce(){
console.log(`我的名字是${this.name},我的年龄是${this.age} `)
}
}
a1.introduce()
// => 我的名字是shanyujia,我的年龄是18
+

类的继承

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class B extends A{
constructor(name,age,sex){
//如果希望继承A的属性 写一个super的调用
//调用父类的constructor 将属性也在B类生成
super(name,age)
this.sex=sex
}
sayHello(){
console.log('你好我是'+this.name)
}
}
const b1=new B('张三',20,'男')
console.log(b1)
b1.sayHello()
b1.introduce()
/*{ name: '张三', age: 20, sex: '男' }
我的名字是张三,我的年龄是20
你好我是张三*/
+ + +
+ +
+
+ + + + + + +
+
+
Javascript-class类
+
http://example.com/2023/12/16/Javascript-class类/
+
+
+ +
+
Author
+
Shanyujia
+
+ + +
+
Posted on
+
December 16, 2023
+
+ + + +
+
Licensed under
+
+ + + + + + + + + + +
+
+ +
+
+
+ + + + +
+ + +
+ +
+ + +
+
+
+
+ +
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2024/02/17/React hook/index.html b/2024/02/17/React hook/index.html new file mode 100644 index 0000000..cb40d88 --- /dev/null +++ b/2024/02/17/React hook/index.html @@ -0,0 +1,738 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + React hook - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ + + +
+
+
+ + +
+ +
+
+
+
+

React hook

+ + +
+ +

React hook

+

Hook 的优势

+
    +
  • Hook 使你在无需改变组件结构的情况下复用状态逻辑(自定义 Hook)
  • +
  • Hook 将组件中互相关联的部分拆分成更小的函数(比如设置订阅或请求数据)
  • +
  • Hook 使你在非 class 的情况下可以使用更多的 React 特性
  • +
+

Hook 使用规则

+

Hook 就是 Javascript 函数,使用它们时有两个额外的规则:

+
    +
  • 只能在函数外层调用 Hook,不要在循环、条件判断或者子函数中调用
  • +
  • 只能在 React 的函数组件自定义 Hook 中调用 Hook。不要在其他 JavaScript 函数中调用
  • +
+

在组件中 React 是通过判断 Hook 调用的顺序来判断某个 state 对应的 useState的,所以必须保证 Hook 的调用顺序在多次渲染之间保持一致,React 才能正确地将内部 state 和对应的 Hook 进行关联

+

useState

+

useState 可以使函数组件像类组件一样拥有 state,函数组件通过 useState 可以让组件重新渲染,更新视图

+
1
const [ ①state , ②dispatch ] = useState(③initData)
+

state,目的提供给 UI ,作为渲染视图的数据源

+

dispatchAction(setState) 改变 state 的函数,可以理解为推动函数组件渲染的渲染函数

+

initData 有两种情况,第一种情况是非函数,将作为 state 初始化的值。 第二种情况是函数,函数的返回值作为 useState 初始化的值

+

基础用法

+
1
2
3
4
5
6
7
8
9
10
11
const DemoState = (props) => {
/* number为此时state读取值 ,setNumber为派发更新的函数 */
let [number, setNumber] = useState(0) /* 0为初始值 */
return (<div>
<span>{ number }</span>
<button onClick={ ()=> {
setNumber(number+1)
console.log(number) /* 这里的number是不能够即使改变的 */
} } ></button>
</div>)
}
+

``useState 注意事项`:

+

① 在函数组件一次执行上下文中,state 的值是固定不变的

+

② 如果两次 dispatchAction 传入相同的 state 值,那么组件就不会更新

+

③ 当触发 dispatchAction 在当前执行上下文中获取不到最新的 state, 只有在下一次组件 rerender 中才能获取到。

+

useReducer

+

组件中可能有多个位置包括了对某个状态的修改操作

+

useReducer用于统一管理状态的操作方式

+

使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为父组件可以向子组件传递 dispatch 而不是回调函数

+
1
const[state,dispatch]= useReducer (countReducer,0)
+

① 更新之后的 state 值。

+

② 派发更新的 dispatch 函数, 本质上和 useState 的 dispatch是一样的。

+

③ 一个函数countReducer 常规reducer里面的 state 和action

+

准备一个用来进行状态功能管理的函数

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function countReducer (state,action)
//state reducer管理状态的是哪个
//action 对这个状态进行哪些操作
{
switch(action.type)
{
case"increment" :return state+1
case "decrement":return state-1
default:
throw new Error()
}
}
export default function App ()
{
//state 状态当前值
//dispatch 用来进行状态修改的触发器 (函数值)
const [state,dispath]=useReducer(countReducer,0)
const handleIncrement=()=>dispath({type:"increment"})
const handleDecrement=()=>dispath({type:"decrement"})
return (
<div>
<button onClick={handleIncrement}>-</button>
<span>{state}</span>
<button onClick={handleDecrement}>+</button>
</div>
)
}
+

useSyncExternalStore

+

不懂以后再说

+

useTransition

+

别急

+

useDeferredValue

+

你也别急

+

useEffect

+
1
2
3
useEffect(()=>{
return destory
},dep)
+

第一个参数callback返回的 destroy 作为下一次callback执行之前调用 用于清楚上一次callback产生的副作用

+

第二个参数作为依赖项,是一个数组,可以有多个依赖项,依赖项改变,执行上一次callback 返回的 destory ,和执行新的 effect 第一个参数 callback

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/* 模拟数据交互 */
function getUserInfo(a){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve({
name:a,
age:16,
})
},500)
})
}
const Demo = ({ a }) => {
const [ userMessage , setUserMessage ] :any= useState({})
const div= useRef()
const [number, setNumber] = useState(0)
/* 模拟事件监听处理函数 */
const handleResize =()=>{}
/* useEffect使用 ,这里如果不加限制 ,会是函数重复执行,陷入死循环*/
useEffect(()=>{
/* 请求数据 */
getUserInfo(a).then(res=>{
setUserMessage(res)
})
/* 定时器 延时器等 */
const timer = setInterval(()=>console.log(666),1000)
/* 操作dom */
console.log(div.current) /* div */
/* 事件监听等 */
window.addEventListener('resize', handleResize)
/* 此函数用于清除副作用 */
return function(){
clearInterval(timer)
window.removeEventListener('resize', handleResize)
}
/* 只有当props->a和state->number改变的时候 ,useEffect副作用函数重新执行 ,如果此时数组为空[],证明函数只有在初始化的时候执行一次相当于componentDidMount */
},[ a ,number ])
return (<div ref={div} >
<span>{ userMessage.name }</span>
<span>{ userMessage.age }</span>
<div onClick={ ()=> setNumber(1) } >{ number }</div>
</div>)
}

+

如上在 useEffect 中做的功能如下:

+
    +
  • ① 请求数据。
  • +
  • ② 设置定时器,延时器等。
  • +
  • ③ 操作 Dom
  • +
  • ④ 注册事件监听器, 事件绑定
  • +
  • ⑤ 还可以清除定时器,延时器,解绑事件监听器等。
  • +
+

useLayoutEffect

+

别急

+

useInsertionEffect

+

你也别急

+

useContext

+

可以用useContext 来获取父级组件传递过来的context的值

+

这个当前值就是最近的父级组件Provider 设置的value值 ,

+

useContext 参数一般是由createContext方式创建的,也可以父级上下文context传递的(参数是context )

+
1
const contextValue = useContext(context)
+

useContext 接受一个参数 一般都是 context 对象,返回值为 context 对象内部保存的 value 值

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* 用useContext方式 */
const DemoContext = ()=> {
const value= useContext(Context)
/* my name is alien */
return <div> my name is { value.name }</div>
}

/* 用Context.Consumer 方式 */
const DemoContext1 = ()=>{
return <Context.Consumer>
{/* my name is alien */}
{ (value)=> <div> my name is { value.name }</div> }
</Context.Consumer>
}

export default ()=>{
return <div>
<Context.Provider value={{ name:'alien' , age:18 }} >
<DemoContext />
<DemoContext1 />
</Context.Provider>
</div>
}

+

useRef

+

useRef 可以用来获取元素,缓存状态,接受一个状态 initState 作为初始值,返回一个 ref 对象 cur, cur 上有一个 current 属性就是 ref 对象需要获取的内容

+
1
2
const cur = React.useRef(initState)
console.log(cur.current)
+

useRef 创建的 ref 对象就是一个普通的对象,而 useRef() 和自建一个 {current: ...} 对象的唯一区别是,useRef 会在每次渲染时返回同一个 ref 对象

+

useRef 基础用法:

+

useRef来获取DOM节点

+
1
2
3
4
5
6
7
8
9
10
11
12
13
const DemoUseRef = ()=>{
const dom= useRef(null)
const handerSubmit = ()=>{
/* <div >表单组件</div> dom 节点 */
console.log(dom.current)
}
return <div>
{/* ref 标记当前dom节点 */}
<div ref={dom} >表单组件</div>
<button onClick={()=>handerSubmit()} >提交</button>
</div>
}

+

useRef 保存状态

+

可以利用 useRef 返回的 ref 对象来保存状态,只要当前组件不被销毁,那么状态就会一直存在

+
1
2
3
4
5
const status = useRef(false)
/* 改变状态 */
const handleChangeStatus = () => {
status.current = true
}
+

useImperativeHandle

+

这什么玩意

+

useMemo

+

useMemo 可以在函数组件 render 上下文中同步执行一个函数逻辑,这个函数的返回值可以作为一个新的状态缓存起来。那么这个 hooks 的作用就显而易见了

+

场景一:在一些场景下,需要在函数组件中进行大量的逻辑计算,那么我们不期望每一次函数组件渲染都执行这些复杂的计算逻辑,所以就需要在 useMemo 的回调函数中执行这些逻辑,然后把得到的产物(计算结果)缓存起来就可以了。

+

场景二:React 在整个更新流程中,diff 起到了决定性的作用,比如 Context 中的 provider 通过 diff value 来判断是否更新

+

useMemo 基础介绍:

+
1
const cacheSomething = useMemo(create,deps)
+

① create:第一个参数为一个函数,函数的返回值作为缓存值,如上 demo 中把 Children 对应的 element 对象,缓存起来。

+

② deps: 第二个参数为一个数组,存放当前 useMemo 的依赖项,在函数组件下一次执行的时候,会对比 deps 依赖项里面的状态,是否有改变,如果有改变重新执行 create ,得到新的缓存值。

+

cacheSomething:返回值,执行 create 的返回值。如果 deps 中有依赖项改变,返回的重新执行 create 产生的值,否则取上一次缓存值。

+

useMemo 基础用法:

+

派生新状态:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Scope() {
const keeper = useKeep()
const { cacheDispatch, cacheList, hasAliveStatus } = keeper

/* 通过 useMemo 得到派生出来的新状态 contextValue */
const contextValue = useMemo(() => {
return {
cacheDispatch: cacheDispatch.bind(keeper),
hasAliveStatus: hasAliveStatus.bind(keeper),
cacheDestory: (payload) => cacheDispatch.call(keeper, { type: ACTION_DESTORY, payload })
}

}, [keeper])
return <KeepaliveContext.Provider value={contextValue}>
</KeepaliveContext.Provider>
}

+

如上通过 useMemo 得到派生出来的新状态 contextValue ,只有 keeper 变化的时候,才改变 Provider 的 value

+

useCallback

+

这个好像有点急 但是先别急

+

useDebugValue

+

这什么玩意

+

useId

+

看不懂思密达

+

自定义hook

+

别急

+ + +
+ +
+
+ + + + + + +
+
+
React hook
+
http://example.com/2024/02/17/React hook/
+
+
+ +
+
Author
+
Shanyujia
+
+ + +
+
Posted on
+
February 17, 2024
+
+ + + +
+
Licensed under
+
+ + + + + + + + + + +
+
+ +
+
+
+ + + + +
+ + +
+ +
+ + +
+
+
+
+ +
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/2024/02/17/\347\273\204\344\273\266\351\200\232\344\277\241/index.html" "b/2024/02/17/\347\273\204\344\273\266\351\200\232\344\277\241/index.html" new file mode 100644 index 0000000..293ab96 --- /dev/null +++ "b/2024/02/17/\347\273\204\344\273\266\351\200\232\344\277\241/index.html" @@ -0,0 +1,678 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 组件通信 - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ + + +
+
+
+ + +
+ +
+
+
+
+

组件通信

+ + +
+ +

React组件通信

+

组件

+

dom组件 react支持的htmlsvg标签

+

props

+

什么是props html属性的功能

+

dom属性

+

类名 className

+

样式

+
1
2
3
4
5
6
7
8
style={
{
width:'100vh',
height:100,
backgroundColor:'grey'
//带-的写成驼峰
}
}
+

也可以书写变量

+
1
2
3
4
5
6
7
8
9
const imgStyle={
width:'100vh',
height:100,
backgroundColor:'grey'
}
return(
<div>
<img style={imgStyle}/>
</div>)
+

jsx展开语法

+

把所有属性书写为一个变量

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import image from './logo.svg'
const imgData={
className='small',
style:{
width:'100vh',
height:100,
backgroundColor:'grey'
} }
return(
<div>
<img
//src标签不能提出
src={image}
{...imgData}
/>
</div>
)
+

自定义组件react的props

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Artcile()
{
return(
<div>
<h2>111</h2>
<p>222</p>
</div>
)
}
export default function App()
{
return(
<>
<Article/>
<Article/>
<Article/>
<Article/>
</>


)
}
+

如果对组件的结构逻辑 样式复用

+

内容不复用的话 可以写成

+

对组件内容定制

+

步骤

+

1.对父元素(上面的App)进行请求 请求功能所需要的数据

+

2.创建组件

+

3.把数据传给组件

+

父组件向子组件传普通值

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
function Detail()
{
return(
<>
<p>{content}</p>
<p>状态:{active?'显示':'隐藏'}</p>
</>
)
}
function Artcile({acticleData})
//采用解构
{
return(
<div>
<h2>{title}</h2>
<Detail {...articleData}
></Detail>
</div>
)
//此时Article没用到content和active
//但是Detail用到了 可以在Article先不传 直接传给Detail
}
export default function App()
{const articleData={
title:'1',
detailData:{
content:'1',active:true
}
}
return(
<>
<Article
{...acticleData}
/>
<Article
title="2"
content="2"
active
/>
<Article
title="3"
content="3"
active
/>
</>


)
}
+

父组件向子组件传jsx

+

插槽 jsx作为props传给子组件

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
function List ({children,title,footer=       
<div>默认底部</div>})
//此时footer需要一个默认值
{
return(
<>
<h2>{title}</h2>
<ul>
{children}
</ul>
{footer}
</>
)
}
//如果需要在列表加元素
export default function App ()
{
return (
<>
//之前是通过props传递 现在通过children
//自动接受组件开始和接受的内容
<List
title="列表1"
footer={
<p>这是底部内容1</p>
}>
<li>内容1</li>
<li>内容2</li>
<li>内容3</li>
</List>

<List
title="列表2"
footer={<p>这是底部内容2</p>}>
<li>内容A</li>
<li>内容B</li>
<li>内容C</li>
</List>
<List
title="列表3"
//此时没设置footer会显示默认值
>
<li>内容X</li>
<li>内容Y</li>
<li>内容Z</li>
</List>
</>
)
}
+

子组件向父组件传值

+

父组件给子组件一个自定义事件的设置 再通过事件触发后向父组件传递参数来设置

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function Detail ({onActive}){
//希望告诉父组件内部状态 在handleClick函数使用的变更 所以在handleClick加入onACtive事件并传入status
const [status,setStatus]=useState(false)
function handleClick()
{
setStatus(!status)
onActive(status)
//每次状态变更 传递一个新值 传递到function App()
}
return (
<div>
<button onClick={handleClick}>按钮</button>
<p
style={{display;staus?
'block':'none'}}>
Detail的内容</p>

</div>
)
}
export default function App(){
function handleActive(status){
console.log(status)
}
return(
<>
//如果希望父组件能接受子组件的状态的话需要给子组件设置一个自定义属性(onActive)可以理解为事件 用代码决定在何时触发

<Detail
onActive={handleActive}></Detail>
</>

)
}
+

同级组件传值

+

在父组件进行中转

+

多层级传值

+

提供了一个多级属性穿透的hook

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import {useState} from "react"
export function Section ({children})
{
//获取上一级level
const level=useContext(LevelContext)
return (

<section className="section">
<LevelContect.Provider
value={
level+1
}>
{children}</LevelContect.Provider>

</section>
)
}
export function Heading({level,children})
{
const level=useContext(LevelContext)
//设置之后此时全为h1(若默认值是1)
//但是h1和section嵌套会使h1显示出从大到小的样式
switch(level){
case 1:
return <h1>{children}</h1>
case 2:
return <h2>{children}</h2>
case 3:
return <h3>{children}</h3>
case 4:
return <h4>{children}</h4>
case 4:
return <h4>{children}</h4>
case 5:
return <h5>{children}</h5>
case 6:
return <h6>{children}</h6>
}
}
const LevelContext=createContext(0)
//不能直接用 通过useContext
export default function App (){
return(
<div>
<section>
<Heading >主标题</Heading>
<section>
<Heading >副标题</Heading>
<Heading >副标题</Heading>
<Heading >副标题 </Heading>
<section>
<Heading>子标</Heading>
<Heading>子标</Heading>
<Heading >子标</Heading>
<section>
<Heading >子子标题</Heading>
<Heading>子子标题</Heading>
<Heading >子子标题</Heading>
<section>
<Heading >子子子标题</Heading>
<Heading >子子子标题</Heading>
<Heading >子子子标题</Heading>
<section>>
<Heading >子子子子标题 </Heading>
<Heading >子子子子标题 </Heading>
<Heading >子子子子标题 </Heading>
</section>
</section>
</section>
</section>
</section>
</section>
</div>)
}
+

Ps:可能会出现都是h1标签但是大小却不同的情况

+

sectionh1嵌套时候自然出现的状态

+

如何更改useContext能够获取到的level信息

+

useContext创建的对象LevelContext提供一个Provider

+

context提供值的一个方式

+ + +
+ +
+
+ + + + + + +
+
+
组件通信
+
http://example.com/2024/02/17/组件通信/
+
+
+ +
+
Author
+
Shanyujia
+
+ + +
+
Posted on
+
February 17, 2024
+
+ + + +
+
Licensed under
+
+ + + + + + + + + + +
+
+ +
+
+
+ + + + +
+ + +
+ +
+ + +
+
+
+
+ +
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/2024/03/08/\350\207\252\345\256\232\344\271\211React Hooks/index.html" "b/2024/03/08/\350\207\252\345\256\232\344\271\211React Hooks/index.html" new file mode 100644 index 0000000..94bfa07 --- /dev/null +++ "b/2024/03/08/\350\207\252\345\256\232\344\271\211React Hooks/index.html" @@ -0,0 +1,690 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 自定义React hooks - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ + + +
+
+
+ + +
+ +
+
+
+
+

自定义React hooks

+ + +
+ +

自定义React Hooks

+

1.为什么会出现hooks这个东西捏 ❓

+

刚看hooks 就出现了这个问题 hooks出现的意义在哪里呢

+

hooks能解决什么问题

+

React中组件有两种写法 一种类组件 一种函数组件

+

但是函数组件相对于类组件来说有一个小小滴缺点 就是没有state 😔

+

所以hooks就应运而生勒😀

+

hooks是一类特殊的函数 允许在React的函数式组件中"钩入"状态,生命周期等其他React的特性

+

提供了一种无需类组件的方式,使得现在在函数式组件中可以使用像 this.state,this.props的概念勒🌵

+

+

2.那hooks都这么厉害了 为什么还要有自定义的hooks捏❔

+

正常的useState接受两个参数

+
1
const [state,setState]=useState('')
+

正常在类组件中setState会支持两个参数: 一个是更新后的state或者回调式更新的state 另一个参数是更新后的回调函数

+

tips:什么是回调函数😓

+

回调 (callback) 是作为参数传递给另一个函数的函数 ,并在被调用函数执行完毕后被调用

+

个人的小理解: 回调函数就是先定义了functionA然后再定义了functionB

+

但是在使用时候先用了functionB 并且把functionA当成了参数给了functionB

+

useState hook并不直接支持像类组件中的setState方法那样可以接收第二个参数作为回调函数。useState hook返回的更新函数只能用于更新状态,并且不会提供回调函数的选项

+

所以自定义hooks就出现啦

+

3.来自定义useState吧🍶

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const useStatePro =(initState)=>{
const [state,setState]=useState(initState);
//存储一手回调函数
const isUpdate=useRef()
//定义一个新函数喽 (接受一个新状态和一个回调函数)
const setStatePro =(newState,cb)=>{
//使用setState更新状态 把回调函数储存在current里
//如果newState是个函数的情况下 就计算新状态
setState(
prev=>{
isUpdate.current=cb
return typeof newState='function' ? newState(prev):newState
}
)
}
//检查一下current有无回调函数 有就直接执行
useEffect(()=>{
if(isUpdate.current)
{
return isUpdate.current()
}
})
return [state,useStatePro]
}
+

这样就实现了useState的功能 但是多了一个在状态更新后执行回调函数的功能

+

4.自定义一个更新函数useUpdate

+

如果正常使用hooks想让组件重新渲染 一般是要更新state的

+

但是有的时候可能一个state掌握着好几个组件的生死大权😈

+

不能就为了一个小小的组件就让state作出无意义的更新

+

这时候可以想想能不能定义一个更新的hooks来优雅一些实现组件的强制更新

+
1
2
3
4
5
6
7
8
const Update=()=>{
const [,setFlag]=useState()
const update=()=>{
//更新一手时间
setFlag(Date.now())
}
return update
}
+

发现这个函数返回了一个函数 这个函数就是用来强制更新的

+

咋使用他捏💅

+
1
2
3
4
5
6
7
const Time=()=>{
const update=useUpdate();
return(
{Date.now()}
<div><button onCLick={update}>更新喽</button></div>
)
}
+

5.自定义hooks实现redux

+

Redux目前来说还是常用的管理状态的工具 但是Redux需要遵守的规则和步骤有点小多😡

+

所以来制作一个属于自己的Redux

+

1.首先先把应用接口做好

+

在顶部引入Provider组件为所有的儿孙组件提供所有数据源store

+
1
2
3
4
5
6
7
8
9
10
11
import React from 'react';
import ReactDOM, {render} from 'react-dom';
import App from './components/App'
import Provider from './store/provider'
// 挂载节点
render((
<Provider>
<App/>
</Provider>
), document.getElementById('app')
)
+

2.然后就可以开始设计store啦:happy:

+

首先就是数据项

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//初始化数据
const initState={
count:0;
}
//reducer 处理器
const reducer =(state,action)=>{
const{type,payload}=action
switch(type){
case'ADD_COUNT':return{...state ,count:state.count+1}
default : return state;
}
}// 创建上下文
const Context = React.createContext()
const Provider = (props) => {
const [state, dispatch] = useReducer(reducer, initState)
return (
<Context.Provider value={{state, dispatch}}>
{props.children}
</Context.Provider>
)
}
export default { Context, Provider }

+

在这个数据项中可以看出 initState reducer的定义和使用redux`是一模一样的

+

重点看下面的创建的上下文 首先通过React.createContext()创建一个空的上下文

+

然后定义Provider这个组件 在内部用useReducerreducer和初始化的initState传入进去

+

返回的statedispatch提供到Provider作为数据源

+

数据项聚合一下

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 聚合count、todo这些数据项
const providers = [
Count.Provider,
Todo.Provider
];
// 递归包裹Provider
const ProvidersComposer = (props) => (
props.providers.reduceRight((children, Parent) => (
return Parent({children})
), props.children)
)
const Provider = (props) => {
return (
<ProvidersComposer providers={providers}>
{props.children}
</ProvidersComposer>
)
}
export default Provider
+

最后出来的组件结构: Provider > Context.Provider > Context.Provider > App 我们通过ProviderComposer进行递归包裹,把每个Provider进行一层一层的包裹 这里使用了parent({children})替代了<Parent>{children}</Parent>,这样的做法可以减少一层嵌套结构。

+

如何使用捏💩

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React, { useContext, useEffect } from 'react';
// 引入count数据源
import CountStore from '@/store/modules/count'

const App = (props) => {
// 通过useContext使用Count这个store的上下文
const {state, dispatch} = useContext(CountStore.Context)
// 每秒更新count
useEffect(() => {
setInterval(() => {
dispatch({ type: 'ADD_COUNT' })
}, 1000);
}, [])

return (
<div className='app'>
{JSON.stringify(state)}
</div>
)
}

export default App

+

这样就实现啦一个小型redux 感觉比正常的redux会好用一些捏

+ + +
+ +
+
+ + + + + + +
+
+
自定义React hooks
+
http://example.com/2024/03/08/自定义React Hooks/
+
+
+ +
+
Author
+
Shanyujia
+
+ + +
+
Posted on
+
March 8, 2024
+
+ + + +
+
Licensed under
+
+ + + + + + + + + + +
+
+ +
+
+
+ + + + +
+ + +
+ +
+ + +
+
+
+
+ +
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/2024/03/23/\307\263\321\247webpack/index.html" "b/2024/03/23/\307\263\321\247webpack/index.html" new file mode 100644 index 0000000..4708fe0 --- /dev/null +++ "b/2024/03/23/\307\263\321\247webpack/index.html" @@ -0,0 +1,791 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 浅学webpack - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ + + +
+
+
+ + +
+ +
+
+
+
+

浅学webpack

+ + +
+ +

webpack原理(一)

+

1.啥是webpack捏❔

+

本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容

+

2.webpack主要流程

+

感觉webpack可以分成三部分

+
    +
  • 构建的核心流程
  • +
  • loader 的作用
  • +
  • plugin 架构与常用套路
  • +
+

3.核心流程解析 😆

+

首先得知道这玩意最核心的功能是什么的吧

+

就像他官网说的一样

+
1
At its core, webpack is a static module bundler for modern JavaScript applications.
+

也就是将各种类型的资源,包括图片、css、js等,转译、组合、拼接、生成 JS 格式的 bundler 文件

+

屏幕截图 2024-03-23 012454.png

+

这个过程核心完成了 [内容转换+资源合并] 两种功能

+

实际上包含三个阶段

+
    +
  • +

    初始化阶段:

    +
  • +
+
    +
  1. 初始化参数:从配置文件、 配置对象、Shell 参数中读取,与默认配置结合得出最终的参数
  2. +
  3. 创建编译器对象:用上一步得到的参数创建 Compiler 对象
  4. +
  5. 初始化编译环境:包括注入内置插件、注册各种模块工厂、初始化 RuleSet 集合、加载配置的插件等
  6. +
  7. 开始编译:执行 compiler 对象的 run 方法
  8. +
  9. 确定入口:根据配置中的 entry 找出所有的入口文件,调用 compilition.addEntry 将入口文件转换为 dependence 对象
  10. +
+
    +
  • +

    构建阶段:

    +
  • +
+
    +
  1. 编译模块(make):根据 entry 对应的 dependence 创建 module 对象,调用 loader 将模块转译为标准 JS 内容,调用 JS 解释器将内容转换为 AST 对象,从中找出该模块依赖的模块,再 递归 本步骤直到所有入口依赖的文件都经过了本步骤的处理
  2. +
  3. 完成模块编译:上一步递归处理所有能触达到的模块后,得到了每个模块被翻译后的内容以及它们之间的 依赖关系图
  4. +
+
    +
  • +

    生成阶段:

    +
  • +
+
    +
  1. 输出资源(seal):根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
  2. +
  3. 写入文件系统(emitAssets):在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
  4. +
+

屏幕截图 2024-03-23 012530.png

+

接下来说说细节部分吧 对上面一些听起来很高级的名词解释一手 :happy:

+
    +
  • +

    Entry:编译入口,webpack 编译的起点

    +
  • +
  • +

    Compiler:编译管理器,webpack 启动后会创建 compiler 对象,该对象一直存活知道结束退出

    +

    这玩意记录了webpack运行环境的所有信息 插件可以通过它获取到webpack配置信息,像entry output module等配置

    +
  • +
  • +

    Compilation:单次编辑过程的管理器,比如 watch = true 时,运行过程中只有一个 compiler 但每次文件变更触发重新编译时,都会创建一个新的 compilation 对象

    +

    它储存了当前的模块资源、编译生成的资源、变化的文件、以及被跟踪依赖的状态信息

    +
  • +
  • +

    Dependence:依赖对象,webpack 基于该类型记录模块间依赖关系

    +
  • +
  • +

    Module:webpack 内部所有资源都会以“module”对象形式存在,所有关于资源的操作、转译、合并都是以 “module” 为基本单位进行的

    +
  • +
  • +

    Chunk:编译完成准备输出时,webpack 会将 module 按特定的规则组织成一个一个的 chunk,这些 chunk 某种程度上跟最终输出一一对应

    +
  • +
  • +

    Loader:资源内容转换器,其实就是实现从内容 A 转换 B 的转换器

    +
  • +
  • +

    Plugin:webpack构建过程中,会在特定的时机广播对应的事件,插件监听这些事件,在特定时间点介入编译过程

    +
  • +
+

4.Plugin解析

+

看插件之前 首先想三个问题

+

什么是插件

+

什么时间点会有什么钩子被触发

+

在钩子回调中,如何影响编译状态

+

What: 什么是插件🚘

+

先从形态上来看 插件是个带有apply函数的类

+
1
2
3
4
class SomePlugin {
apply(compiler){
}
}
+

apply 函数运行时会得到参数 compiler ,以此为起点可以调用 hook 对象注册各种钩子回调,例如: compiler.hooks.make.tapAsync ,这里面 make 是钩子名称,tapAsync 定义了钩子的调用方式,webpack 的插件架构基于这种模式构建而成,插件开发者可以使用这种模式在钩子回调中,插入特定代码。webpack 各种内置对象都带有 hooks 属性,比如 compilation 对象:

+
1
2
3
4
5
6
7
class SomePlugin {
apply(compiler) {
compiler.hooks.thisCompilation.tap('SomePlugin', (compilation) => {
compilation.hooks.optimizeChunkAssets.tapAsync('SomePlugin', ()=>{});
})
}
}
+

When: 什么时候会触发钩子 📦

+

官网对钩子的说明都比较简短

+

直接看几个例子吧

+

compiler.hooks.compilation

+
    +
  • 时机:启动编译创建出 compilation 对象后触发
  • +
  • 参数:当前编译的 compilation 对象
  • +
  • 示例:很多插件基于此事件获取 compilation 实例
  • +
+

compiler.hooks.make

+
    +
  • 时机:正式开始编译时触发
  • +
  • 参数:同样是当前编译的 compilation 对象
  • +
  • 示例:webpack 内置的 EntryPlugin 基于此钩子实现 entry 模块的初始化
  • +
+

compilation.hooks.optimizeChunks

+
    +
  • 时机: seal 函数中,chunk 集合构建完毕后触发
  • +
  • 参数:chunks 集合与 chunkGroups 集合
  • +
  • 示例: SplitChunksPlugin 插件基于此钩子实现 chunk 拆分优化
  • +
+

compiler.hooks.done

+
    +
  • 时机:编译完成后触发
  • +
  • 参数: stats 对象,包含编译过程中的各类统计信息
  • +
  • 示例: webpack-bundle-analyzer 插件基于此钩子实现打包分析
  • +
+

看这个钩子时候我直接看 触发时机 传递参数 示例代码

+

首先看触发时机勒

+

触发时机是与webpack工作过程紧密相关的,大体上从启动到结束,compiler对象逐次触发以下钩子

+

屏幕截图 2024-03-23 012707.png

+

compilation对象逐次触发:

+

image (1).png

+

就都是嘟嘟嘟嘟嘟嘟长串 😓

+

我自己的理解是 这个时机就是几个情况

+
    +
  • 创建出这个对象触发
  • +
  • 正式开始编译就触发
  • +
  • 函数构建完毕后触发
  • +
+

传递参数

+

我在他的配置里没怎么找到 感觉无非传点modules之类的东西

+

示例代码

+

webpack这钩子实话实说

+

抽象程度是小大的 我感觉最好的学习办法 就是查询其他插件中如何使用这些钩子 不然你就看吧 一看一个不吱声😭

+

How 如何影响编译状态捏

+

webpack的插件体系跟平常看到的不太一样 hooks回调由webpack决定何时 以何种方式 而在hooks回调内部可以直接修改状态 调用上下文api等方式webpack产生 一些影响

+

举个🏮子

+

EntryPlugin插件

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class EntryPlugin {
apply(compiler) {
compiler.hooks.compilation.tap(
"EntryPlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(
EntryDependency,
normalModuleFactory
);
}
);

compiler.hooks.make.tapAsync("EntryPlugin", (compilation, callback) => {
const { entry, options, context } = this;

const dep = EntryPlugin.createDependency(entry, options);
compilation.addEntry(context, dep, options, (err) => {
callback(err);
});
});
}
}
+

在这里面 webpack会将上下文信息以参数或this形式传递给钩子回调用

+

在回调中可以调用上下文对象的方法或者直接修改上下文对象属性的方式,对原定的流程产生 side effect。所以想纯熟地编写插件,除了要理解调用时机,还需要了解我们可以用哪一些api,例如:

+
    +
  • compilation.addModule:添加模块,可以在原有的 module 构建规则之外,添加自定义模块
  • +
  • compilation.emitAsset:直译是“提交资产”,功能可以理解将内容写入到一个特定路径
  • +
  • compilation.addEntry:添加入口,功能上与直接定义 entry 配置相同
  • +
  • module.addError:添加编译错误信息
  • +
+

5.Loader介绍

+

看一下loader在编译流程中的生效位置

+

屏幕截图 2024-03-23 012734.png

+

在这个流程图里,runloaders会调用用户所配置的loader集合读取,转移资源,前面的内容可以千奇百怪👽

+

但是转译之后一般都是输出Javascript文本,webpack才可以继续处理模块依赖

+

理解了这个基本逻辑,loader职责基本上也知道了 就是把内容A =>内容B

+ + +
+ +
+
+ + + + + + +
+
+
浅学webpack
+
http://example.com/2024/03/23/dzѧwebpack/
+
+
+ +
+
Author
+
Shanyujia
+
+ + +
+
Posted on
+
March 23, 2024
+
+ + + +
+
Licensed under
+
+ + + + + + + + + + +
+
+ +
+
+
+ + + + +
+ + +
+ +
+ + +
+
+
+
+ +
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/2024/04/11/Git\345\256\235\345\205\270/index.html" "b/2024/04/11/Git\345\256\235\345\205\270/index.html" new file mode 100644 index 0000000..cc801cf --- /dev/null +++ "b/2024/04/11/Git\345\256\235\345\205\270/index.html" @@ -0,0 +1,768 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Git宝典 - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ + + +
+
+
+ + +
+ +
+
+
+
+

Git宝典

+ + +
+ +

Git宝典

+

其实这次mini对我自身来说 学到了很多 技术这里就先不说了 终于意识到了git在多人协作写一个大项目的重要性 😆

+

啥是Git

+

我对Git这个东西的理解就是 在进行大型项目的开发时候 你不可能单兵作战 一般是有好多好多人一起的 这个时候怎么进行每个人之间的汇总勒

+

这个时候 就要用到Git

+

说点官方的话术就是😒

+

Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理项目

+

Git是怎么工作的呢❓

+

如图

+

img

+

具体都是什么意思勒

+
    +
  • Workspace:工作区 (就是你电脑能看到的)
  • +
  • Index / Stage:暂存区 (一般是在.git目录下的index文件下)
  • +
  • Repository:仓库区(或本地仓库)
  • +
  • Remote:远程仓库
  • +
+

Git咋使用呢

+

1.第一步肯定是新建Git库喽 🆕

+

有两种方法

+

1️⃣直接新建!

+
1
git init
+

2️⃣克隆下来一个已经存在的库

+
1
git clone "url"
+

2.基本操作

+

该说不说这玩意真多啊😡

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
git add添加文件到暂存区
git status查看仓库当前的状态 显示有变更的文件
git commit提交暂存区到本地仓库
git mv移动或重命名工作区文件
git log查看历史提交记录
git blame以列表形式查看指定文件的历史修改记录
git remote远程仓库操作
git fetch从远程获取代码库
git pull下载远程代码并合并
git push上传远程代码并合并 添加文件到本地暂存区
+
    +
  • +

    添加文件到暂存区

    +
    1
    2
    git add 文件名 //这样是添加某个问价
    git add . //添加该目录下所有未被忽略的文件
    +

    Tips:一般都忽略什么呢?🤔

    +

    一般都会忽略你的node_modules文件夹 因为依赖太多多多多了

    +

    +
  • +
  • +

    提交更改到本地仓库

    +
    1
    git commit -m "说明"
    +

    这个说明也是有规范滴

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    feat - 新功能 feature
    fix - 修复 bug
    docs - 文档注释
    style - 代码格式(不影响代码运行的变动)
    refactor - 重构、优化(既不增加新功能,也不是修复bug)
    perf - 性能优化
    test - 增加测试
    chore - 构建过程或辅助工具的变动
    revert - 回退
    build - 打包
    +

    就像

    +

    git commit规范.png

    +
  • +
+

这样写commit的话 就能非常清楚的看到你每次提交了一些什么

+
    +
  • +

    分支管理

    +

    创建分支

    +
    1
    git branch (分支名)
    +

    切换分支

    +
    1
    git checkout (分支名)
    +

    创建新的分支并且立即切换到该分支

    +
    1
    git checkout -b (分支名)
    +

    合并分支

    +
    1
    git merge
    +

    列出分支

    +
    1
    2
    3
    4
    //列出本地分支
    git branch
    //列出全部分支(包括远程分支)
    git branch -a
    +

    删除分支

    +
    1
    git branch -d (分支名)
    +
  • +
+

多人协作

+

在进行一个大型项目的时候 该怎么使用Git呢

+

就是刘总经常说的提PR了 😧

+

怎么提呢

+

首先你需要找到你用的大型项目仓库

+

然后fork!!!

+

fork.png

+

然后回到你自己仓库

+

就能看到你的本地仓库了

+
    +
  • 本地仓库.png
  • +
+

怎么提PR勒 🤔

+

点击你本地Contribute按钮 然后Open Pull Request

+

pr.png

+

就可以跟本地一样写说明喽 标准也是跟commit的标准差不多的

+

小小小Tips:

+

在执行git push之前,为了防止远程仓库已有其他人提交的更改与你的更改产生冲突,要先执行git pull拉取远程仓库的代码,如果显示确有冲突,就要在本地手动更改冲突,可利用ide解决。 如果没有冲突,就可以直接推送。如果有冲突,解决冲突后重新执行git add和git commit ,再推送到远程仓库

+

就像

+

git 手动操作.png

+

基本上Git常用的就这些勒 好像博客也可以用Github Actions自动部署 以后有时间试试

+

😸

+ + +
+ +
+
+ + + + + + +
+
+
Git宝典
+
http://example.com/2024/04/11/Git宝典/
+
+
+ +
+
Author
+
Shanyujia
+
+ + +
+
Posted on
+
April 11, 2024
+
+ + + +
+
Licensed under
+
+ + + + + + + + + + +
+
+ +
+
+
+ + + + +
+ + +
+ +
+ + +
+
+
+
+ +
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/2024/04/21/Javascript\345\277\215\350\200\205\347\247\230\347\261\215blog-1/index.html" "b/2024/04/21/Javascript\345\277\215\350\200\205\347\247\230\347\261\215blog-1/index.html" new file mode 100644 index 0000000..860e887 --- /dev/null +++ "b/2024/04/21/Javascript\345\277\215\350\200\205\347\247\230\347\261\215blog-1/index.html" @@ -0,0 +1,718 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Javascript忍者秘籍blog-1 - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ + + +
+
+
+ + +
+ +
+
+
+
+

Javascript忍者秘籍blog-1

+ + +
+ +

Javascript忍者秘籍(一)

+

最近一直都在看这本书 感觉之前的js底子太差了 看这本书回顾一下吧(感觉有些概念都是没听过的)🌵

+

这篇主要写第四章理解函数调用的东西吧

+

调用函数时,隐式的函数参数 this 和 arguments 会被静默的传递给函数,可以像函数体内显式声明的参数一样使用

+

this 表示调用函数的上下文对象

+

arguments 表示函数调用过程中传递的所有参数。通过 arguments 参数可以访问 函数调用过程中传递的实际参数。

+

函数调用的方式 对 函数的隐式参数有很大的影响🍶

+

1.隐式函数参数

+

arguments参数

+

arguments参数表示传入函数的所有参数的集合。

+

使用arguments.length属性来获取传递给函数的实际参数个数。

+

通过数组下标的方式访问到arguments参数中的每个参数值,如arguments[2]将获取第三个参数。

+

arguments是一个类数组对象,不可以对arguments对象使用数组的方法。

+

在非严格模式下,arguments对象是函数参数的别名,修改arguments对象会影响对应的函数实参,反之亦然。

+

arguments作为参数别名使用时,会影响代码可读性,应该避免使用参数别名修改参数。在严格模式下通过arguments修改参数是不起作用的

+

this参数

+

this表示函数上下文,即与函数调用相关联的对象。(还是有很多抽象小题的 经典this指向问题)💢

+

但是,在javascript中,将一个函数作为方法调用仅仅是函数的一种调用方式。this参数是由函数的定义方式调用方式决定

+

2.函数调用方式

+

基本上是4种

+
    +
  1. 作为函数直接被调用;myfunc()
  2. +
  3. 作为方法关联在一个对象上,实现面向对象编程;obj.myfunc()
  4. +
  5. 作为构造函数调用,实例化一个对象;new Myfunc()
  6. +
  7. 通过函数的apply和call方法
  8. +
+

作为函数被直接调用

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*函数定义作为函数被调用*/
function aa(){
console.log(this)
}
aa(); //=>object.window
/*函数表达式作为函数被调用*/
let bb = function(){
console.log(this)
}
bb(); //=>object.window
/*立即调用函数表达式作为函数被调用*/
(function(){console.log(this)})();
//=>object.window

+

作为对象方法被调用

+

当一个函数被赋值一个对象的属性,并且通过对象属性引用的方式调用函数时,函数会作为对象的方法被调用。 作为对象方法调用的函数this值与对象关联,通过this可以访问所关联对象的其他方法和属性

+
1
2
3
4
5
6
7
8
9
10
11
function aa(){return this}
console.log(aa()==window);
//=>true
var obj1 = {}
obj1.aa = aa;
console.log(obj1.aa()==obj1);
//=>true
var obj2 = {}
obj2.bb = aa;
console.log(obj2.bb()==obj2);
//=>true
+

作为构造函数调用

+

在函数调用之前加上关键字new,即为构造函数调用。

+

构造函数目的是用来创建和初始化一个新对象,然后将这个对象作为构造函数的返回值

+

使用关键字new调用函数会触发以下几个动作:🍔

+
    +
  1. 创建一个新的空对象;
  2. +
  3. 该对象作为this参数传递给构造函数,成为构造函数的上下文;
  4. +
  5. 新构造的对象作为new运算符的返回值。
  6. +
  7. 如果构造函数返回一个对象,则该对象将作为整个表达式的返回值,而传入构造函数的this将被丢弃。
  8. +
  9. 如果构建函数返回的是非对象类型,则忽略返回值,返回新创建的对象。
  10. +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Ninja(){
//这里的this表示Ninja函数的上下文
this.skulk = function(){
//这里的this表示该匿名函数的上下文
return this;
}
}
//skulk以对象的方式调用是,返回值是其关联的对象
var ninja1 = new Ninja();
var ninja2 = new Ninja();
console.log(ninja1.skulk() == ninja1);
console.log(ninja2.skulk() == ninja2);
// skulk复制给一个变量后,直接调用函数时,非严格模式下skulk返回的值是window
var skulk = ninja2.skulk;
console.log(skulk() == window);

+

通过apply与call方法调用

+

javascript提供了可以显示指定任何对象作为函数的上下文的函数调用方式。每个函数都存在apply和call方法。通过apply与call方法来设置函数的上下文。

+
call函数
+
1
function.call(context, arg1, arg2, ...)
+

context 是指定函数中的 this 关键字指向的对象,arg1, arg2, … 是传递给函数的参数

+
apply函数
+
1
function.apply(context, [argsArray])
+

context 是指定函数中的 this 关键字指向的对象,argsArray 是一个数组,其中包含要传递给函数的参数

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function juggle(){
var result = 0;
for(var n=0; n<arguments.length; n++){
result+=arguments[n]
}
this.result = result;
}

var ninja1 = {};
var ninja2 = {};

juggle.apply(ninja1, [1,2,3,4,5])
juggle.call(ninja2, 1,2,3,4,5, 6)

console.log(ninja1.result == 15)
console.log(ninja2.result == 21)
+

apply和call功能类似,唯一的不同在于如何传递参数。apply和call第一个参数作为函数的上下文,apply第二个参数是一个包含参数值的数组。call可以传入任意数量参数,作为函数的参数。

+

总结!!!

+
总结四种函数的调用方式对this取值的影响
+
    +
  • 如果作为函数调用,在非严格模式下,this指向全局window对象;在严格模式下,this指向undefined。
  • +
  • 作为方法调用,this通常指向调用的对象
  • +
  • 作为构造函数调用,this指向新创建的对象。
  • +
  • 通过call或apply调用,this指向call或apply的第一个参数。
  • +
+

3.this指向问题! (天天被拷打版)

+

在全局作用域

+

this->window

+

在普通函数中

+

谁调用我 this指向谁

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj = {
fn1:function() {
console.log(this);
},
fn2:function(){
fn3()
}
}
function fn3() {
console.log(this);
}
fn3();//this->window
obj.fn1();//this->obj
obj.fn2();//this->window

+

箭头函数的this

+

箭头函数没有自己的this,箭头函数的this就是上下文中定义的this,因为箭头函数没有自己的this所以不能用做构造函数。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var div = document.querySelector('div'); 
var o={
a:function(){
var arr=[1];
//就是定义所在对象中的this
//这里的this—>o
arr.forEach(item=>{
//所以this -> o
console.log(this);
})
},
//这里的this指向window o是定义在window中的对象
b:()=>{
console.log(this);
},
c:function() {
console.log(this);
}
}
div.addEventListener('click',item=>{
console.log(this);//this->window 这里的this就是定义上文window环境中的this
});
o.a(); //this->o
o.b();//this->window
o.c();//this->o 普通函数谁调用就指向谁

+

事件绑定中的this

+

基本上都是指向this->事件源的😆

+
1
2
3
4
5
6
7
8
9
var div = document.querySelector('div'); 
div.addEventListener('click',function() {
console.log(this); //this->div
});

div.onclick = function() {
console.log(this) //this->div
}

+

定时器的this

+

定时器中采用回调函数作为处理函数 回调函数的this->window

+
1
2
3
4
5
6
7
8
  setInterval(function() {
console.log(this); //this->window
},500)

setTimeout(function() {
console.log(this); //this->window
},500)
442
+

构造函数的this

+

构造函数配合new使用, 而new关键字会将构造函数中的this指向实例化对象,所以构造函数中的this->实例化对象

+
1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name,age) {
this.name = name;
this.age = age;
}
var person1 = new Person();
person1.name = 'ggb';
person1.age = 21;
console.log(person1);//Person {name: "ggb", age: 21}
var person2 = new Person();
person2.name = 'syj';
person2.age = 19;
console.log(person2);// Person {name: "syj", age: 19}

+ + +
+ +
+
+ + + + + + +
+
+
Javascript忍者秘籍blog-1
+
http://example.com/2024/04/21/Javascript忍者秘籍blog-1/
+
+
+ +
+
Author
+
Shanyujia
+
+ + +
+
Posted on
+
April 21, 2024
+
+ + + +
+
Licensed under
+
+ + + + + + + + + + +
+
+ +
+
+
+ + + + +
+ + +
+ +
+ + +
+
+
+
+ +
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2024/05/12/Typescript-declare/index.html b/2024/05/12/Typescript-declare/index.html new file mode 100644 index 0000000..ce6486f --- /dev/null +++ b/2024/05/12/Typescript-declare/index.html @@ -0,0 +1,689 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Typescript-declare - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ + + +
+
+
+ + +
+ +
+
+
+
+

Typescript-declare

+ + +
+ +

declare 关键字

+

最近在做那个开源之夏 然后要用到这个declare 进行类型检查就正好写一篇啦 (其实还有很多不会的)👼

+

本来想写JSX的自定义接口来着 但是没准备看懂 所以写了这个

+

简介

+

declare 关键字用来告诉编译器,某个类型是存在的,可以在当前文件中使用🛩️

+

它的主要作用,就是让当前文件可以使用其他文件声明的类型。举例来说,自己的脚本使用外部库定义的函数,编译器会因为不知道外部函数的类型定义而报错,这时就可以在自己的脚本里面使用declare关键字,告诉编译器外部函数的类型。这样的话,编译单个脚本就不会因为使用了外部类型而报错。

+

declare 关键字可以描述以下类型。

+
    +
  • 变量(const、let、var 命令声明)
  • +
  • type 或者 interface 命令声明的类型
  • +
  • class
  • +
  • enum
  • +
  • 函数(function)
  • +
  • 模块(module)
  • +
  • 命名空间(namespace)
  • +
+

declare 关键字的重要特点是,它只是通知编译器某个类型是存在的,不用给出具体实现。比如,只描述函数的类型,不给出函数的实现,如果不使用declare,这是做不到的。

+

declare 只能用来描述已经存在的变量和数据结构,不能用来声明新的变量和数据结构。另外,所有 declare 语句都不会出现在编译后的文件里面。

+

declare variable

+

declare 关键字可以给出外部变量的类型描述。

+

举例来说,当前脚本使用了其他脚本定义的全局变量x

+
1
x = 123; // 报错
+

上面示例中,变量x是其他脚本定义的,当前脚本不知道它的类型,编译器就会报错。

+

这时使用 declare 命令给出它的类型,就不会报错了。

+
1
declare let x:number;x = 1;
+

如果 declare 关键字没有给出变量的具体类型,那么变量类型就是any

+
1
declare let x;x = 1;
+

上面示例中,变量x的类型为any

+

下面的例子是脚本使用浏览器全局对象document

+
1
declare var document;document.title = 'Hello';
+

上面示例中,declare 告诉编译器,变量document的类型是外部定义的(具体定义在 TypeScript 内置文件lib.d.ts)。

+

如果 TypeScript 没有找到document的外部定义,这里就会假定它的类型是any

+

注意,declare 关键字只用来给出类型描述,是纯的类型代码,不允许设置变量的初始值,即不能涉及值。

+
1
2
// 报错 
declare let x:number = 1;
+

上面示例中,declare 设置了变量的初始值,结果就报错了。

+

declare function

+

declare 关键字可以给出外部函数的类型描述。

+
1
2
declare function sayHello(name:string):void;
sayHello('zzx');
+

上面示例中,declare 命令给出了sayHello()的类型描述,因此可以直接使用它。

+

注意,这种单独的函数类型声明语句,只能用于declare命令后面。一方面,TypeScript 不支持单独的函数类型声明语句;另一方面,declare 关键字后面也不能带有函数的具体实现。

+
1
2
// 报错 
function sayHello( name:string):void;function sayHello(name) { return '你好,' + name;}
+

上面示例中,单独写函数的类型声明就会报错。

+

declare class

+

declare 给出 class 的描述描述写法如下。

+
1
declare class Animal {  constructor(name:string);  eat():void;  sleep():void;}
+

下面是一个复杂一点的例子。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
declare class C {  
// 静态成员
public static s0():string;
private static s1:string;
// 属性
public a:number;
private b:number;
// 构造函数
constructor(arg:number);
// 方法
m(x:number, y:number):number;
// 存取器
get c():number; set c(value:number);
// 索引签名
[index:string]:any;
}
+

同样的,declare 后面不能给出 Class 的具体实现或初始值。

+

declare module,declare namespace

+

如果想把变量、函数、类组织在一起,可以将 declare 与 module 或 namespace 一起使用。

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
declare namespace AnimalLib {  
class Animal {
constructor(name:string);
eat():void;
sleep():void; }
type Animals = 'Fish' | 'Dog';}
// 或者
declare module AnimalLib
{ class Animal
{ constructor(name:string);
eat(): void;
sleep(): void;
}
type Animals = 'Fish' | 'Dog';}
+

这个栗子中 declare 关键字给出了 module 或 namespace 的类型描述。

+

declare module 和 declare namespace 里面,加不加 export 关键字都可以。

+
1
declare namespace Foo {  export var a: boolean;}declare module 'io' { export function readFile(filename:string):string;}
+

上面示例中,namespace 和 module 里面使用了 export 关键字。

+

declare 关键字的另一个用途,是为外部模块添加属性和方法时,给出新增部分的类型描述。

+
1
import { Foo as Bar } from 'moduleA';declare module 'moduleA' {  interface Bar extends Foo {    custom: {      prop1:string;    }  }}
+

上面示例中,从模块moduleA导入了Foo接口,将其重命名为Bar,并用 declare 关键字为Bar增加一个属性custom

+

下面是另一个例子。一个项目有多个模块,可以在一个模型中,对另一个模块的接口进行类型扩展。

+
1
2
3
4
5
6
7
// a.ts
export interface A { x: number;}
// b.ts
import { A } from './a';
declare module './a' {
interface A { y: number; }}
const a:A = { x: 0, y: 0 };
+

上面示例中,脚本a.ts定义了一个接口A,脚本b.ts为这个接口添加了属性ydeclare module './a' {}表示对a.ts里面的模块,进行类型声明,而同名 interface 会自动合并,所以等同于扩展类型。

+

使用这种语法进行模块的类型扩展时,有两点需要注意:

+

(1)declare module NAME语法里面的模块名NAME,跟 import 和 export 的模块名规则是一样的,且必须跟当前文件加载该模块的语句写法(上例import { A } from './a')保持一致。

+

(2)不能创建新的顶层类型。也就是说,只能对a.ts模块中已经存在的类型进行扩展,不允许增加新的顶层类型,比如新定义一个接口B

+

(3)不能对默认的default接口进行扩展,只能对 export 命令输出的命名接口进行扩充。这是因为在进行类型扩展时,需要依赖输出的接口名。

+

某些第三方模块,原始作者没有提供接口类型,这时可以在自己的脚本顶部加上下面一行命令。

+
1
2
declare module "模块名";
// 例子declare module "hot-new-module";
+

加上上面的命令以后,外部模块即使没有类型,也可以通过编译。但是,从该模块输入的所有接口都将为any类型。

+

declare module 描述的模块名可以使用通配符。

+
1
2
3
4
5
6
7
8
declare module 'my-plugin-*' {  
interface PluginOptions {
enabled: boolean;
priority: number;
}
function initialize(options: PluginOptions): void;
export = initialize;
}
+

上面示例中,模块名my-plugin-*表示适配所有以my-plugin-开头的模块名(比如my-plugin-logger)。

+ + +
+ +
+
+ + + + + + +
+
+
Typescript-declare
+
http://example.com/2024/05/12/Typescript-declare/
+
+
+ +
+
Author
+
Shanyujia
+
+ + +
+
Posted on
+
May 12, 2024
+
+ + + +
+
Licensed under
+
+ + + + + + + + + + +
+
+ +
+
+
+ + + + +
+ + +
+ +
+ + +
+
+
+
+ +
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2024/05/12/blog/index.html b/2024/05/12/blog/index.html new file mode 100644 index 0000000..e7b3094 --- /dev/null +++ b/2024/05/12/blog/index.html @@ -0,0 +1,614 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + blog - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ + + +
+
+
+ + +
+ +
+
+
+ +
+
+
+ +
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/2024/06/02/\346\265\217\350\247\210\345\231\250\346\217\222\344\273\266\345\205\245\351\227\250/index.html" "b/2024/06/02/\346\265\217\350\247\210\345\231\250\346\217\222\344\273\266\345\205\245\351\227\250/index.html" new file mode 100644 index 0000000..05d8acd --- /dev/null +++ "b/2024/06/02/\346\265\217\350\247\210\345\231\250\346\217\222\344\273\266\345\205\245\351\227\250/index.html" @@ -0,0 +1,695 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 浏览器插件入门 - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ + + +
+
+
+ + +
+ +
+
+
+
+

浏览器插件入门

+ + +
+ +

浏览器插件

+

开源之夏有点被导师pua到了 所以准备换了个项目 还是一个社区的项目

+

这个项目主要是对浏览器插件优化

+

浏览器插件基本项目结构

+

先看看基本结构吧 基本上都会有一个mainfest.jsonpackage.json基本上我感觉是一样的作用

+
1
2
3
4
5
6
7
8
9
10
11
{
// 插件名称
"name": "Hello Extensions",
// 插件的描述
"description" : "Base Level Extension",
// 插件的版本
"version": "1.0",
// 配置插件程序的版本号,主流版本是2,最新是3
"manifest_version": 2
}

+

我们经常会点击右上角插件图标时弹出一个小窗口的页面,焦点离开时就关闭了,一般做一些临时性的交互操作;在配置文件中新增browser_action字段,配置popup弹框:

+
1
2
3
4
5
6
7
8
9
10
11
{
"name": "Hello Extensions",
"description" : "Base Level Extension",
"version": "1.0",
"manifest_version": 2,
// 新增popup弹框
"browser_action": {
"default_popup": "popup.html",
"default_icon": "popup.png"
}
}
+

然后创建我们的弹框页面panel.html:

+
1
2
3
4
5
html复制代码<html>
<body>
<h1>Hello Extensions</h1>
</body>
</html>
+

点击图标后,插件显示panel.html

+

后台background

+

们也发现了,popup页面只能做临时性的交互操作,用完就关了,不能存储信息或者和其他标签页进行交互等等;这时就需要用到background(后台),它是一个常驻的页面,它的生命周期是插件中所有类型页面中最长的;它随着浏览器的打开而打开,随着浏览器的关闭而关闭,所以通常把需要一直运行的、启动就运行的、全局的代码放在background里面。

+

mainfest.json添加路径

+
1
2
3
4
5
6
"background": {
"script": [
"background.js"
],
"persistent": true
},
+

这样基本上结构就差不多了

+

实现查看组件属性和查看状态管理状态的功能

+

这个就是开源之夏的第一个任务喽

+

感觉就是React的那个插件的功能

+

我去看了看react的源码

+

感觉操作就是需要先获取到组件的状态 然后看状态管理器的状态

+

然后监听后台管理器的消息 然后在显示的html中加上显示的部分

+

在content-Script中新增⼀个stateManager.ts⽂件 以便捕获和发送组件状态和状态管理器信息

+

哦这里补充一下 一般会有一个content-script文件夹

+

官方定义是

+

content-scripts(内容脚本)是在网页上下文中运行的文件。通过使用标准的文档对象模型(DOM),它能够读取浏览器访问的网页的详细信息,对其进行更改,并将信息传递给其父级插件。内容脚本相对于background还是有一些访问API上的限制,它可以直接访问以下chrome的API:

+
    +
  • i18n
  • +
  • storage
  • +
  • runtime: +
      +
    • connect
    • +
    • getManifest
    • +
    • getURL
    • +
    • id
    • +
    • onConnect
    • +
    • onMessage
    • +
    • sendMessage
    • +
    +
  • +
+

我的理解就是监听监听监听 传信号信号信号 💬

+

基本上用的都是chrome的api

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let mockComponentState = { exampleComponent: { state: 'active' } };
let mockStateManagerInfo = { exampleStateManager: { state: 'initialized' } };
chrome.runtime.sendMessage({ type: 'updateComponentState', state:
mockComponentState });
chrome.runtime.sendMessage({ type: 'updateStateManagerInfo', stateManager:
mockStateManagerInfo });
//钩子到框架特定的 API 获取真实的组件状态和状态管理器信息
(function () {
const originalSetState = YourFramework.Component.prototype.setState;
YourFramework.Component.prototype.setState = function (newState, callback) {
chrome.runtime.sendMessage({ type: 'updateComponentState', state:
this.state });
return originalSetState.apply(this, arguments);
};
})();
(function () {
const originalDispatch = YourFramework.store.dispatch;
YourFramework.store.dispatch = function (action) {
chrome.runtime.sendMessage({ type: 'updateStateManagerInfo', stateManager:
YourFramework.store.getState() });
return originalDispatch.apply(this, arguments);
};
})();
+

chrome.runtime.sendMessage 是 Chrome 扩展程序中的一种方法,用于从扩展程序中的一个部分向另一个部分发送消息,例如从内容脚本向后台脚本发送消息。

+

用法

+
1
chrome.runtime.sendMessage(extensionId, message, options, responseCallback);
+
    +
  • extensionId(可选):字符串类型。指定接收消息的扩展程序的ID。如果消息是发送到本扩展程序,可以省略此参数。
  • +
  • message:任意类型。发送的消息内容,可以是任何JSON序列化类型的数据。
  • +
  • options(可选):对象类型。包含额外的选项,比如 includeTlsChannelId(布尔值)。
  • +
  • responseCallback(可选):函数类型。一个函数,当收到响应消息时调用,传入一个参数 response,该参数是响应的消息。
  • +
+

然后在content-Script的index.ts中引入使用

+
1
2
3
4
5
6
7
8
9
10
11
 //监听来自 background 的消息
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
//该方法可以监听页面 contentScript 和插件的消息
// 没有 tab 信息说明消息来自插件
if (!sender.tab checkMessage(message, DevToolBackground)) {
changeSource(message, DevToolContentScript);
// 传递消息给页面
window.postMessage(message, '*');
}
sendResponse({ status: 'ok' });
});
+

他自己写的源码中有个什么window对象不能直接通过 contentScript 代码修改,只能通过添加 js 代码往页面 window 注入 hook 这玩意还没看懂 等要是选上慢慢来吧

+ + +
+ +
+
+ + + + + + +
+
+
浏览器插件入门
+
http://example.com/2024/06/02/浏览器插件入门/
+
+
+ +
+
Author
+
Shanyujia
+
+ + +
+
Posted on
+
June 2, 2024
+
+ + + +
+
Licensed under
+
+ + + + + + + + + + +
+
+ +
+
+
+ + + + +
+ + +
+ +
+ + +
+
+
+
+ +
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2024/07/12/test/index.html b/2024/07/12/test/index.html new file mode 100644 index 0000000..ce43ae2 --- /dev/null +++ b/2024/07/12/test/index.html @@ -0,0 +1,608 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + test - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ + + +
+
+
+ + +
+ +
+
+
+
+

test

+ + +
+ + + +
+ +
+
+ + + + + + +
+
+
test
+
http://example.com/2024/07/12/test/
+
+
+ +
+
Author
+
Shanyujia
+
+ + +
+
Posted on
+
July 12, 2024
+
+ + + +
+
Licensed under
+
+ + + + + + + + + + +
+
+ +
+
+
+ + + + +
+
+ + +
+ +
+ +
+ + +
+
+
+
+ +
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/404.html b/404.html new file mode 100644 index 0000000..511eb33 --- /dev/null +++ b/404.html @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Page not found - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/about/index.html b/about/index.html new file mode 100644 index 0000000..fc2970b --- /dev/null +++ b/about/index.html @@ -0,0 +1,479 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + About - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+ avatar +
+ +
+
+
+ + +
+
+
Shanyujia
+
A CCNU Student Learning Front End
+
+ + + + + + + + + + + + + + + + + + + + + + + + qrcode + + + +
+
+
+ +
+
+ +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2023/11/index.html b/archives/2023/11/index.html new file mode 100644 index 0000000..0609d78 --- /dev/null +++ b/archives/2023/11/index.html @@ -0,0 +1,358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Archives - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + +
+

14 posts in total

+
+ + + + +

2023

+ + + +
javascript1
+
+ + + + +
my new post
+
+ +
+ + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2023/12/index.html b/archives/2023/12/index.html new file mode 100644 index 0000000..33a193c --- /dev/null +++ b/archives/2023/12/index.html @@ -0,0 +1,358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Archives - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + +
+

14 posts in total

+
+ + + + +

2023

+ + + +
Javascript-class类
+
+ + + + +
Javascript 函数
+
+ +
+ + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2023/index.html b/archives/2023/index.html new file mode 100644 index 0000000..4c75a8a --- /dev/null +++ b/archives/2023/index.html @@ -0,0 +1,370 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Archives - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + + + + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2024/02/index.html b/archives/2024/02/index.html new file mode 100644 index 0000000..e187f79 --- /dev/null +++ b/archives/2024/02/index.html @@ -0,0 +1,358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Archives - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + +
+

14 posts in total

+
+ + + + +

2024

+ + + +
React hook
+
+ + + + +
组件通信
+
+ +
+ + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2024/03/index.html b/archives/2024/03/index.html new file mode 100644 index 0000000..117c324 --- /dev/null +++ b/archives/2024/03/index.html @@ -0,0 +1,358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Archives - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + +
+

14 posts in total

+
+ + + + +

2024

+ + + +
浅学webpack
+
+ + + + +
自定义React hooks
+
+ +
+ + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2024/04/index.html b/archives/2024/04/index.html new file mode 100644 index 0000000..1d85f9b --- /dev/null +++ b/archives/2024/04/index.html @@ -0,0 +1,358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Archives - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + +
+

14 posts in total

+
+ + + + +

2024

+ + + +
Javascript忍者秘籍blog-1
+
+ + + + +
Git宝典
+
+ +
+ + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2024/05/index.html b/archives/2024/05/index.html new file mode 100644 index 0000000..ee49ffc --- /dev/null +++ b/archives/2024/05/index.html @@ -0,0 +1,358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Archives - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + +
+

14 posts in total

+
+ + + + +

2024

+ + + +
blog
+
+ + + + +
Typescript-declare
+
+ +
+ + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2024/06/index.html b/archives/2024/06/index.html new file mode 100644 index 0000000..b9a1683 --- /dev/null +++ b/archives/2024/06/index.html @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Archives - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + +
+

14 posts in total

+
+ + + + +

2024

+ + + +
浏览器插件入门
+
+ +
+ + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2024/07/index.html b/archives/2024/07/index.html new file mode 100644 index 0000000..24737ab --- /dev/null +++ b/archives/2024/07/index.html @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Archives - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + +
+

14 posts in total

+
+ + + + +

2024

+ + + +
test
+
+ +
+ + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2024/index.html b/archives/2024/index.html new file mode 100644 index 0000000..9c93f3c --- /dev/null +++ b/archives/2024/index.html @@ -0,0 +1,406 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Archives - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+ +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/index.html b/archives/index.html new file mode 100644 index 0000000..b03981e --- /dev/null +++ b/archives/index.html @@ -0,0 +1,412 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Archives - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+ +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/2/index.html b/archives/page/2/index.html new file mode 100644 index 0000000..76464a0 --- /dev/null +++ b/archives/page/2/index.html @@ -0,0 +1,376 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Archives - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + + + + + + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/categories/C/index.html b/categories/C/index.html new file mode 100644 index 0000000..69c324c --- /dev/null +++ b/categories/C/index.html @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Categories - C++ - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + +
+

1 posts in total

+
+ + + + +

2023

+ + + +
my new post
+
+ +
+ + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/categories/Javascript/index.html b/categories/Javascript/index.html new file mode 100644 index 0000000..0654507 --- /dev/null +++ b/categories/Javascript/index.html @@ -0,0 +1,378 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Categories - javascript - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + +
+

1 posts in total

+
+ + + + +

2024

+ + + +
Javascript忍者秘籍blog-1
+
+ +
+ + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/categories/React/index.html b/categories/React/index.html new file mode 100644 index 0000000..9529364 --- /dev/null +++ b/categories/React/index.html @@ -0,0 +1,364 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Categories - React - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + + + + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/categories/index.html b/categories/index.html new file mode 100644 index 0000000..0b3baae --- /dev/null +++ b/categories/index.html @@ -0,0 +1,672 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Categories - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + + + + + +
+ + + + + + + + + + +
+ + + + + 3 + + +
+ + + +
+ + + + + + 自定义React hooks + + + + + + + React hook + + + + + + + 组件通信 + + + +
+ + +
+
+ + + + +
+ + + + + 1 + + +
+ + + +
+ + + + + + Git宝典 + + + +
+ + +
+
+ + + + +
+ + + + + 1 + + +
+ + + +
+ + + + + + my new post + + + +
+ + +
+
+ + + + + + + + + +
+ + + + + 1 + + +
+ + + +
+ + + + + + 浅学webpack + + + +
+ + +
+
+ + + + + + +
+ + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/categories/webpack/index.html b/categories/webpack/index.html new file mode 100644 index 0000000..d320b4f --- /dev/null +++ b/categories/webpack/index.html @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Categories - webpack - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + +
+

1 posts in total

+
+ + + + +

2024

+ + + +
浅学webpack
+
+ +
+ + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/categories/\345\274\200\345\217\221\345\237\272\347\241\200/index.html" "b/categories/\345\274\200\345\217\221\345\237\272\347\241\200/index.html" new file mode 100644 index 0000000..5322e6f --- /dev/null +++ "b/categories/\345\274\200\345\217\221\345\237\272\347\241\200/index.html" @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Categories - 开发基础 - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + +
+

1 posts in total

+
+ + + + +

2024

+ + + +
Git宝典
+
+ +
+ + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/categories/\346\265\217\350\247\210\345\231\250\346\217\222\344\273\266/index.html" "b/categories/\346\265\217\350\247\210\345\231\250\346\217\222\344\273\266/index.html" new file mode 100644 index 0000000..b3f4eb9 --- /dev/null +++ "b/categories/\346\265\217\350\247\210\345\231\250\346\217\222\344\273\266/index.html" @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Categories - 浏览器插件 - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + +
+

1 posts in total

+
+ + + + +

2024

+ + + +
浏览器插件入门
+
+ +
+ + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/css/gitalk.css b/css/gitalk.css new file mode 100644 index 0000000..a268f1d --- /dev/null +++ b/css/gitalk.css @@ -0,0 +1,546 @@ +@font-face { + font-family: octicons-link; + src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format('woff'); +} +/* variables */ +/* functions & mixins */ +/* variables - calculated */ +/* styles */ +.gt-container { + -webkit-box-sizing: border-box; + box-sizing: border-box; + font-size: 16px; + /* loader */ + /* error */ + /* initing */ + /* no int */ + /* link */ + /* meta */ + /* popup */ + /* header */ + /* comments */ + /* comment */ +} +.gt-container * { + -webkit-box-sizing: border-box; + box-sizing: border-box; +} +.gt-container a { + color: #6190e8; +} +.gt-container a:hover { + color: #81a6ed; + border-color: #81a6ed; +} +.gt-container a.is--active { + color: #333; + cursor: default !important; +} +.gt-container a.is--active:hover { + color: #333; +} +.gt-container .hide { + display: none !important; +} +.gt-container .gt-svg { + display: inline-block; + width: 1em; + height: 1em; + vertical-align: sub; +} +.gt-container .gt-svg svg { + width: 100%; + height: 100%; + fill: #6190e8; +} +.gt-container .gt-ico { + display: inline-block; +} +.gt-container .gt-ico-text { + margin-left: 0.3125em; +} +.gt-container .gt-ico-github { + width: 100%; + height: 100%; +} +.gt-container .gt-ico-github .gt-svg { + width: 100%; + height: 100%; +} +.gt-container .gt-ico-github svg { + fill: inherit; +} +.gt-container .gt-spinner { + position: relative; +} +.gt-container .gt-spinner::before { + content: ''; + -webkit-box-sizing: border-box; + box-sizing: border-box; + position: absolute; + top: 3px; + width: 0.75em; + height: 0.75em; + margin-top: -0.1875em; + margin-left: -0.375em; + border-radius: 50%; + border: 1px solid #fff; + border-top-color: #6190e8; + -webkit-animation: gt-kf-rotate 0.6s linear infinite; + animation: gt-kf-rotate 0.6s linear infinite; +} +.gt-container .gt-loader { + position: relative; + border: 1px solid #999; + -webkit-animation: ease gt-kf-rotate 1.5s infinite; + animation: ease gt-kf-rotate 1.5s infinite; + display: inline-block; + font-style: normal; + width: 1.75em; + height: 1.75em; + line-height: 1.75em; + border-radius: 50%; +} +.gt-container .gt-loader:before { + content: ''; + position: absolute; + display: block; + top: 0; + left: 50%; + margin-top: -0.1875em; + margin-left: -0.1875em; + width: 0.375em; + height: 0.375em; + background-color: #999; + border-radius: 50%; +} +.gt-container .gt-avatar { + display: inline-block; + width: 3.125em; + height: 3.125em; +} +@media (max-width: 479px) { + .gt-container .gt-avatar { + width: 2em; + height: 2em; + } +} +.gt-container .gt-avatar img { + width: 100%; + height: auto; + border-radius: 3px; +} +.gt-container .gt-avatar-github { + width: 3em; + height: 3em; +} +@media (max-width: 479px) { + .gt-container .gt-avatar-github { + width: 1.875em; + height: 1.875em; + } +} +.gt-container .gt-btn { + padding: 0.75em 1.25em; + display: inline-block; + line-height: 1; + text-decoration: none; + white-space: nowrap; + cursor: pointer; + border: 1px solid #6190e8; + border-radius: 5px; + background-color: #6190e8; + color: #fff; + outline: none; + font-size: 0.75em; +} +.gt-container .gt-btn-text { + font-weight: 400; +} +.gt-container .gt-btn-loading { + position: relative; + margin-left: 0.5em; + display: inline-block; + width: 0.75em; + height: 1em; + vertical-align: top; +} +.gt-container .gt-btn.is--disable { + cursor: not-allowed; + opacity: 0.5; +} +.gt-container .gt-btn-login { + margin-right: 0; +} +.gt-container .gt-btn-preview { + background-color: #fff; + color: #6190e8; +} +.gt-container .gt-btn-preview:hover { + background-color: #f2f2f2; + border-color: #81a6ed; +} +.gt-container .gt-btn-public:hover { + background-color: #81a6ed; + border-color: #81a6ed; +} +.gt-container .gt-error { + text-align: center; + margin: 0.625em; + color: #ff3860; +} +.gt-container .gt-initing { + padding: 1.25em 0; + text-align: center; +} +.gt-container .gt-initing-text { + margin: 0.625em auto; + font-size: 92%; +} +.gt-container .gt-no-init { + padding: 1.25em 0; + text-align: center; +} +.gt-container .gt-link { + border-bottom: 1px dotted #6190e8; +} +.gt-container .gt-link-counts, +.gt-container .gt-link-project { + text-decoration: none; +} +.gt-container .gt-meta { + margin: 1.25em 0; + padding: 1em 0; + position: relative; + border-bottom: 1px solid #e9e9e9; + font-size: 1em; + position: relative; + z-index: 10; +} +.gt-container .gt-meta:before, +.gt-container .gt-meta:after { + content: " "; + display: table; +} +.gt-container .gt-meta:after { + clear: both; +} +.gt-container .gt-counts { + margin: 0 0.625em 0 0; +} +.gt-container .gt-user { + float: right; + margin: 0; + font-size: 92%; +} +.gt-container .gt-user-pic { + width: 16px; + height: 16px; + vertical-align: top; + margin-right: 0.5em; +} +.gt-container .gt-user-inner { + display: inline-block; + cursor: pointer; +} +.gt-container .gt-user .gt-ico { + margin: 0 0 0 0.3125em; +} +.gt-container .gt-user .gt-ico svg { + fill: inherit; +} +.gt-container .gt-user .is--poping .gt-ico svg { + fill: #6190e8; +} +.gt-container .gt-version { + color: #a1a1a1; + margin-left: 0.375em; +} +.gt-container .gt-copyright { + margin: 0 0.9375em 0.5em; + border-top: 1px solid #e9e9e9; + padding-top: 0.5em; +} +.gt-container .gt-popup { + position: absolute; + right: 0; + top: 2.375em; + background: #fff; + display: inline-block; + border: 1px solid #e9e9e9; + padding: 0.625em 0; + font-size: 0.875em; + letter-spacing: 0.5px; +} +.gt-container .gt-popup .gt-action { + cursor: pointer; + display: block; + margin: 0.5em 0; + padding: 0 1.125em; + position: relative; + text-decoration: none; +} +.gt-container .gt-popup .gt-action.is--active:before { + content: ''; + width: 0.25em; + height: 0.25em; + background: #6190e8; + position: absolute; + left: 0.5em; + top: 0.4375em; +} +.gt-container .gt-header { + position: relative; + display: -webkit-box; + display: -ms-flexbox; + display: flex; +} +.gt-container .gt-header-comment { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + margin-left: 1.25em; +} +@media (max-width: 479px) { + .gt-container .gt-header-comment { + margin-left: 0.875em; + } +} +.gt-container .gt-header-textarea { + padding: 0.75em; + display: block; + -webkit-box-sizing: border-box; + box-sizing: border-box; + width: 100%; + min-height: 5.125em; + max-height: 15em; + border-radius: 5px; + border: 1px solid rgba(0,0,0,0.1); + font-size: 0.875em; + word-wrap: break-word; + resize: vertical; + background-color: #f6f6f6; + outline: none; + -webkit-transition: all 0.25s ease; + transition: all 0.25s ease; +} +.gt-container .gt-header-textarea:hover { + background-color: #fbfbfb; +} +.gt-container .gt-header-preview { + padding: 0.75em; + border-radius: 5px; + border: 1px solid rgba(0,0,0,0.1); + background-color: #f6f6f6; +} +.gt-container .gt-header-controls { + position: relative; + margin: 0.75em 0 0; +} +.gt-container .gt-header-controls:before, +.gt-container .gt-header-controls:after { + content: " "; + display: table; +} +.gt-container .gt-header-controls:after { + clear: both; +} +@media (max-width: 479px) { + .gt-container .gt-header-controls { + margin: 0; + } +} +.gt-container .gt-header-controls-tip { + font-size: 0.875em; + color: #6190e8; + text-decoration: none; + vertical-align: sub; +} +@media (max-width: 479px) { + .gt-container .gt-header-controls-tip { + display: none; + } +} +.gt-container .gt-header-controls .gt-btn { + float: right; + margin-left: 1.25em; +} +@media (max-width: 479px) { + .gt-container .gt-header-controls .gt-btn { + float: none; + width: 100%; + margin: 0.75em 0 0; + } +} +.gt-container:after { + content: ''; + position: fixed; + bottom: 100%; + left: 0; + right: 0; + top: 0; + opacity: 0; +} +.gt-container.gt-input-focused { + position: relative; +} +.gt-container.gt-input-focused:after { + content: ''; + position: fixed; + bottom: 0%; + left: 0; + right: 0; + top: 0; + background: #000; + opacity: 0.6; + -webkit-transition: opacity 0.3s, bottom 0s; + transition: opacity 0.3s, bottom 0s; + z-index: 9999; +} +.gt-container.gt-input-focused .gt-header-comment { + z-index: 10000; +} +.gt-container .gt-comments { + padding-top: 1.25em; +} +.gt-container .gt-comments-null { + text-align: center; +} +.gt-container .gt-comments-controls { + margin: 1.25em 0; + text-align: center; +} +.gt-container .gt-comment { + position: relative; + padding: 0.625em 0; + display: -webkit-box; + display: -ms-flexbox; + display: flex; +} +.gt-container .gt-comment-content { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + margin-left: 1.25em; + padding: 0.75em 1em; + background-color: #f9f9f9; + overflow: auto; + -webkit-transition: all ease 0.25s; + transition: all ease 0.25s; +} +.gt-container .gt-comment-content:hover { + -webkit-box-shadow: 0 0.625em 3.75em 0 #f4f4f4; + box-shadow: 0 0.625em 3.75em 0 #f4f4f4; +} +@media (max-width: 479px) { + .gt-container .gt-comment-content { + margin-left: 0.875em; + padding: 0.625em 0.75em; + } +} +.gt-container .gt-comment-header { + margin-bottom: 0.5em; + font-size: 0.875em; + position: relative; +} +.gt-container .gt-comment-block-1 { + float: right; + height: 1.375em; + width: 2em; +} +.gt-container .gt-comment-block-2 { + float: right; + height: 1.375em; + width: 4em; +} +.gt-container .gt-comment-username { + font-weight: 500; + color: #6190e8; + text-decoration: none; +} +.gt-container .gt-comment-username:hover { + text-decoration: underline; +} +.gt-container .gt-comment-text { + margin-left: 0.5em; + color: #a1a1a1; +} +.gt-container .gt-comment-date { + margin-left: 0.5em; + color: #a1a1a1; +} +.gt-container .gt-comment-like, +.gt-container .gt-comment-edit, +.gt-container .gt-comment-reply { + position: absolute; + height: 1.375em; +} +.gt-container .gt-comment-like:hover, +.gt-container .gt-comment-edit:hover, +.gt-container .gt-comment-reply:hover { + cursor: pointer; +} +.gt-container .gt-comment-like { + top: 0; + right: 2em; +} +.gt-container .gt-comment-edit, +.gt-container .gt-comment-reply { + top: 0; + right: 0; +} +.gt-container .gt-comment-body { + color: #333 !important; +} +.gt-container .gt-comment-body .email-hidden-toggle a { + display: inline-block; + height: 12px; + padding: 0 9px; + font-size: 12px; + font-weight: 600; + line-height: 6px; + color: #444d56; + text-decoration: none; + vertical-align: middle; + background: #dfe2e5; + border-radius: 1px; +} +.gt-container .gt-comment-body .email-hidden-toggle a:hover { + background-color: #c6cbd1; +} +.gt-container .gt-comment-body .email-hidden-reply { + display: none; + white-space: pre-wrap; +} +.gt-container .gt-comment-body .email-hidden-reply .email-signature-reply { + padding: 0 15px; + margin: 15px 0; + color: #586069; + border-left: 4px solid #dfe2e5; +} +.gt-container .gt-comment-body .email-hidden-reply.expanded { + display: block; +} +.gt-container .gt-comment-admin .gt-comment-content { + background-color: #f6f9fe; +} +@-webkit-keyframes gt-kf-rotate { + 0% { + -webkit-transform: rotate(0); + transform: rotate(0); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes gt-kf-rotate { + 0% { + -webkit-transform: rotate(0); + transform: rotate(0); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} diff --git a/css/highlight-dark.css b/css/highlight-dark.css new file mode 100644 index 0000000..13d6ac8 --- /dev/null +++ b/css/highlight-dark.css @@ -0,0 +1,62 @@ +pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em +} +code.hljs { + padding: 3px 5px +} +/* + +Dark style from softwaremaniacs.org (c) Ivan Sagalaev + +*/ +.hljs { + color: #ddd; + background: #303030 +} +.hljs-keyword, +.hljs-selector-tag, +.hljs-literal, +.hljs-section, +.hljs-link { + color: white +} +.hljs-subst { + /* default */ + +} +.hljs-string, +.hljs-title, +.hljs-name, +.hljs-type, +.hljs-attribute, +.hljs-symbol, +.hljs-bullet, +.hljs-built_in, +.hljs-addition, +.hljs-variable, +.hljs-template-tag, +.hljs-template-variable { + color: #d88 +} +.hljs-comment, +.hljs-quote, +.hljs-deletion, +.hljs-meta { + color: #979797 +} +.hljs-keyword, +.hljs-selector-tag, +.hljs-literal, +.hljs-title, +.hljs-section, +.hljs-doctag, +.hljs-type, +.hljs-name, +.hljs-strong { + font-weight: bold +} +.hljs-emphasis { + font-style: italic +} diff --git a/css/highlight.css b/css/highlight.css new file mode 100644 index 0000000..acd5468 --- /dev/null +++ b/css/highlight.css @@ -0,0 +1,118 @@ +pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em +} +code.hljs { + padding: 3px 5px +} +/*! + Theme: GitHub + Description: Light theme as seen on github.com + Author: github.com + Maintainer: @Hirse + Updated: 2021-05-15 + + Outdated base version: https://github.com/primer/github-syntax-light + Current colors taken from GitHub's CSS +*/ +.hljs { + color: #24292e; + background: #ffffff +} +.hljs-doctag, +.hljs-keyword, +.hljs-meta .hljs-keyword, +.hljs-template-tag, +.hljs-template-variable, +.hljs-type, +.hljs-variable.language_ { + /* prettylights-syntax-keyword */ + color: #d73a49 +} +.hljs-title, +.hljs-title.class_, +.hljs-title.class_.inherited__, +.hljs-title.function_ { + /* prettylights-syntax-entity */ + color: #6f42c1 +} +.hljs-attr, +.hljs-attribute, +.hljs-literal, +.hljs-meta, +.hljs-number, +.hljs-operator, +.hljs-variable, +.hljs-selector-attr, +.hljs-selector-class, +.hljs-selector-id { + /* prettylights-syntax-constant */ + color: #005cc5 +} +.hljs-regexp, +.hljs-string, +.hljs-meta .hljs-string { + /* prettylights-syntax-string */ + color: #032f62 +} +.hljs-built_in, +.hljs-symbol { + /* prettylights-syntax-variable */ + color: #e36209 +} +.hljs-comment, +.hljs-code, +.hljs-formula { + /* prettylights-syntax-comment */ + color: #6a737d +} +.hljs-name, +.hljs-quote, +.hljs-selector-tag, +.hljs-selector-pseudo { + /* prettylights-syntax-entity-tag */ + color: #22863a +} +.hljs-subst { + /* prettylights-syntax-storage-modifier-import */ + color: #24292e +} +.hljs-section { + /* prettylights-syntax-markup-heading */ + color: #005cc5; + font-weight: bold +} +.hljs-bullet { + /* prettylights-syntax-markup-list */ + color: #735c0f +} +.hljs-emphasis { + /* prettylights-syntax-markup-italic */ + color: #24292e; + font-style: italic +} +.hljs-strong { + /* prettylights-syntax-markup-bold */ + color: #24292e; + font-weight: bold +} +.hljs-addition { + /* prettylights-syntax-markup-inserted */ + color: #22863a; + background-color: #f0fff4 +} +.hljs-deletion { + /* prettylights-syntax-markup-deleted */ + color: #b31d28; + background-color: #ffeef0 +} +.hljs-char.escape_, +.hljs-link, +.hljs-params, +.hljs-property, +.hljs-punctuation, +.hljs-tag { + /* purposely ignored */ + +} diff --git a/css/main.css b/css/main.css new file mode 100644 index 0000000..8204318 --- /dev/null +++ b/css/main.css @@ -0,0 +1,2238 @@ +.anchorjs-link { + text-decoration: none !important; + transition: opacity 0.2s ease-in-out; +} +.markdown-body h1:hover > .anchorjs-link, +h2:hover > .anchorjs-link, +h3:hover > .anchorjs-link, +h4:hover > .anchorjs-link, +h5:hover > .anchorjs-link, +h6:hover > .anchorjs-link { + opacity: 1; +} +.banner { + height: 100%; + position: relative; + overflow: hidden; + cursor: default; +} +.banner .mask { + position: absolute; + width: 100%; + height: 100%; + background-color: rgba(0,0,0,0.3); +} +.banner[parallax="true"] { + will-change: transform; + -webkit-transform-style: preserve-3d; + -webkit-backface-visibility: hidden; + transition: transform 0.05s ease-out; +} +@media (max-width: 100vh) { + .header-inner { + max-height: 100vw; + } + #board { + margin-top: -1rem !important; + } +} +@media (max-width: 79.99vh) { + .scroll-down-bar { + display: none; + } +} +#board { + position: relative; + margin-top: -2rem; + padding: 3rem 0; + background-color: var(--board-bg-color); + transition: background-color 0.2s ease-in-out; + border-radius: 0.5rem; + z-index: 3; + -webkit-box-shadow: 0 12px 15px 0 rgba(0,0,0,0.24), 0 17px 50px 0 rgba(0,0,0,0.19); + box-shadow: 0 12px 15px 0 rgba(0,0,0,0.24), 0 17px 50px 0 rgba(0,0,0,0.19); +} +.code-widget { + display: inline-block; + background-color: transparent; + font-size: 0.75rem; + line-height: 1; + font-weight: bold; + padding: 0.3rem 0.1rem 0.1rem 0.1rem; + position: absolute; + right: 0.45rem; + top: 0.15rem; + z-index: 1; +} +.code-widget-light { + color: #999; +} +.code-widget-dark { + color: #bababa; +} +.copy-btn { + cursor: pointer; + user-select: none; + -webkit-appearance: none; + outline: none; +} +.copy-btn > i { + font-size: 0.75rem !important; + font-weight: 400; + margin-right: 0.15rem; + opacity: 0; + transition: opacity 0.2s ease-in-out; +} +.markdown-body pre:hover > .copy-btn > i { + opacity: 0.9; +} +.markdown-body pre:hover > .copy-btn, +.markdown-body pre:not(:hover) > .copy-btn { + outline: none; +} +.license-box { + background-color: rgba(27,31,35,0.05); + transition: background-color 0.2s ease-in-out; + border-radius: 4px; + font-size: 0.9rem; + overflow: hidden; + padding: 1.25rem; + position: relative; + z-index: 1; +} +.license-box .license-icon { + position: absolute; + top: 50%; + left: 100%; +} +.license-box .license-icon::after { + content: "\e8e4"; + font-size: 12.5rem; + line-height: 1; + opacity: 0.1; + position: relative; + left: -0.85em; + bottom: 0.5em; + z-index: -1; +} +.license-box .license-title { + margin-bottom: 1rem; +} +.license-box .license-title div:nth-child(1) { + line-height: 1.2; + margin-bottom: 0.25rem; +} +.license-box .license-title div:nth-child(2) { + color: var(--sec-text-color); + font-size: 0.8rem; +} +.license-box .license-meta { + align-items: center; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; +} +.license-box .license-meta .license-meta-item { + align-items: center; + justify-content: center; + margin-right: 1.5rem; +} +.license-box .license-meta .license-meta-item div:nth-child(1) { + color: var(--sec-text-color); + font-size: 0.8rem; + font-weight: normal; +} +.license-box .license-meta .license-meta-item i.iconfont { + font-size: 1rem; +} +@media (max-width: 575px) and (min-width: 425px) { + .license-box .license-meta .license-meta-item { + display: flex; + justify-content: flex-start; + flex-wrap: wrap; + font-size: 0.8rem; + flex: 0 0 50%; + max-width: 50%; + margin-right: 0; + } + .license-box .license-meta .license-meta-item div:nth-child(1) { + margin-right: 0.5rem; + } + .license-box .license-meta .license-meta-date { + order: -1; + } +} +@media (max-width: 424px) { + .license-box::after { + top: -65px; + } + .license-box .license-meta { + flex-direction: column; + align-items: flex-start; + } + .license-box .license-meta .license-meta-item { + display: flex; + flex-wrap: wrap; + font-size: 0.8rem; + } + .license-box .license-meta .license-meta-item div:nth-child(1) { + margin-right: 0.5rem; + } +} +.footer-inner { + padding: 3rem 0 1rem 0; + text-align: center; +} +.footer-inner > div:not(:first-child) { + margin: 0.25rem 0; + font-size: 0.85rem; +} +.footer-inner .statistics { + display: flex; + flex-direction: row; + justify-content: center; +} +.footer-inner .statistics > span { + flex: 1; + margin: 0 0.25rem; +} +.footer-inner .statistics > *:nth-last-child(2):first-child { + text-align: right; +} +.footer-inner .statistics > *:nth-last-child(2):first-child ~ * { + text-align: left; +} +.footer-inner .beian { + display: flex; + flex-direction: row; + justify-content: center; +} +.footer-inner .beian > * { + margin: 0 0.25rem; +} +.footer-inner .beian-police { + position: relative; + overflow: hidden; + display: inline-flex; + align-items: center; + justify-content: left; +} +.footer-inner .beian-police img { + margin-right: 3px; + width: 1rem; + height: 1rem; + margin-bottom: 0.1rem; +} +@media (max-width: 424px) { + .footer-inner .statistics { + flex-direction: column; + } + .footer-inner .statistics > *:nth-last-child(2):first-child { + text-align: center; + } + .footer-inner .statistics > *:nth-last-child(2):first-child ~ * { + text-align: center; + } + .footer-inner .beian { + flex-direction: column; + } + .footer-inner .beian .beian-police { + justify-content: center; + } + .footer-inner .beian > *:nth-last-child(2):first-child { + text-align: center; + } + .footer-inner .beian > *:nth-last-child(2):first-child ~ * { + text-align: center; + } +} +sup > a::before, +.footnote-text::before { + display: block; + content: ""; + margin-top: -5rem; + height: 5rem; + width: 1px; + visibility: hidden; +} +sup > a::before, +.footnote-text::before { + display: inline-block; +} +.footnote-item::before { + display: block; + content: ""; + margin-top: -5rem; + height: 5rem; + width: 1px; + visibility: hidden; +} +.footnote-list ol { + list-style-type: none; + counter-reset: sectioncounter; + padding-left: 0.5rem; + font-size: 0.95rem; +} +.footnote-list ol li:before { + font-family: "Helvetica Neue", monospace, "Monaco"; + content: "[" counter(sectioncounter) "]"; + counter-increment: sectioncounter; +} +.footnote-list ol li+li { + margin-top: 0.5rem; +} +.footnote-text { + padding-left: 0.5em; +} +.navbar { + background-color: transparent; + font-size: 0.875rem; + box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12); + -webkit-box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12); +} +.navbar .navbar-brand { + color: var(--navbar-text-color); +} +.navbar .navbar-toggler .animated-icon span { + background-color: var(--navbar-text-color); +} +.navbar .nav-item .nav-link { + display: block; + color: var(--navbar-text-color); + transition: color 0.2s ease-in-out, background-color 0.2s ease-in-out; +} +.navbar .nav-item .nav-link:hover { + color: var(--link-hover-color); +} +.navbar .nav-item .nav-link:focus { + color: var(--navbar-text-color); +} +.navbar .nav-item .nav-link i { + font-size: 0.875rem; +} +.navbar .nav-item .nav-link i:only-child { + margin: 0 0.2rem; +} +.navbar .navbar-toggler { + border-width: 0; + outline: 0; +} +.navbar.scrolling-navbar { + will-change: background, padding; + -webkit-transition: background 0.5s ease-in-out, padding 0.5s ease-in-out; + transition: background 0.5s ease-in-out, padding 0.5s ease-in-out; +} +@media (min-width: 600px) { + .navbar.scrolling-navbar { + padding-top: 12px; + padding-bottom: 12px; + } + .navbar.scrolling-navbar .navbar-nav > li { + -webkit-transition-duration: 1s; + transition-duration: 1s; + } +} +.navbar.scrolling-navbar.top-nav-collapse { + padding-top: 5px; + padding-bottom: 5px; +} +.navbar .dropdown-menu { + font-size: 0.875rem; + color: var(--navbar-text-color); + background-color: rgba(0,0,0,0.3); + border: none; + min-width: 8rem; + -webkit-transition: background 0.5s ease-in-out, padding 0.5s ease-in-out; + transition: background 0.5s ease-in-out, padding 0.5s ease-in-out; +} +@media (max-width: 991.98px) { + .navbar .dropdown-menu { + text-align: center; + } +} +.navbar .dropdown-item { + color: var(--navbar-text-color); +} +.navbar .dropdown-item:hover, +.navbar .dropdown-item:focus { + color: var(--link-hover-color); + background-color: rgba(0,0,0,0.1); +} +@media (min-width: 992px) { + .navbar .dropdown:hover > .dropdown-menu { + display: block; + } + .navbar .dropdown > .dropdown-toggle:active { + pointer-events: none; + } + .navbar .dropdown-menu { + top: 95%; + } +} +.navbar .animated-icon { + width: 30px; + height: 20px; + position: relative; + margin: 0; + -webkit-transform: rotate(0deg); + -moz-transform: rotate(0deg); + -o-transform: rotate(0deg); + transform: rotate(0deg); + -webkit-transition: 0.5s ease-in-out; + -moz-transition: 0.5s ease-in-out; + -o-transition: 0.5s ease-in-out; + transition: 0.5s ease-in-out; + cursor: pointer; +} +.navbar .animated-icon span { + display: block; + position: absolute; + height: 3px; + width: 100%; + border-radius: 9px; + opacity: 1; + left: 0; + -webkit-transform: rotate(0deg); + -moz-transform: rotate(0deg); + -o-transform: rotate(0deg); + transform: rotate(0deg); + -webkit-transition: 0.25s ease-in-out; + -moz-transition: 0.25s ease-in-out; + -o-transition: 0.25s ease-in-out; + transition: 0.25s ease-in-out; + background: #fff; +} +.navbar .animated-icon span:nth-child(1) { + top: 0; +} +.navbar .animated-icon span:nth-child(2) { + top: 10px; +} +.navbar .animated-icon span:nth-child(3) { + top: 20px; +} +.navbar .animated-icon.open span:nth-child(1) { + top: 11px; + -webkit-transform: rotate(135deg); + -moz-transform: rotate(135deg); + -o-transform: rotate(135deg); + transform: rotate(135deg); +} +.navbar .animated-icon.open span:nth-child(2) { + opacity: 0; + left: -60px; +} +.navbar .animated-icon.open span:nth-child(3) { + top: 11px; + -webkit-transform: rotate(-135deg); + -moz-transform: rotate(-135deg); + -o-transform: rotate(-135deg); + transform: rotate(-135deg); +} +.navbar .dropdown-collapse, +.top-nav-collapse, +.navbar-col-show { + background-color: var(--navbar-bg-color); +} +@media (max-width: 767px) { + .navbar { + font-size: 1rem; + line-height: 2.5rem; + } +} +.banner-text { + color: var(--subtitle-color); + max-width: calc(960px - 6rem); + width: 80%; + overflow-wrap: break-word; +} +.banner-text .typed-cursor { + margin: 0 0.2rem; +} +@media (max-width: 767px) { + #subtitle, + .typed-cursor { + font-size: 1.5rem; + } +} +@media (max-width: 575px) { + .banner-text { + font-size: 0.9rem; + } + #subtitle, + .typed-cursor { + font-size: 1.35rem; + } +} +.modal-dialog .modal-content { + background-color: var(--board-bg-color); + border: 0; + border-radius: 0.125rem; + -webkit-box-shadow: 0 5px 11px 0 rgba(0,0,0,0.18), 0 4px 15px 0 rgba(0,0,0,0.15); + box-shadow: 0 5px 11px 0 rgba(0,0,0,0.18), 0 4px 15px 0 rgba(0,0,0,0.15); +} +.modal-dialog .modal-content .modal-header { + border-bottom-color: var(--line-color); + transition: border-bottom-color 0.2s ease-in-out; +} +.close { + color: var(--text-color); +} +.close:hover { + color: var(--link-hover-color); +} +.close:focus { + outline: 0; +} +.modal-dialog .modal-content .modal-header { + border-top-left-radius: 0.125rem; + border-top-right-radius: 0.125rem; + border-bottom: 1px solid #dee2e6; +} +.md-form { + position: relative; + margin-top: 1.5rem; + margin-bottom: 1.5rem; +} +.md-form input[type] { + -webkit-box-sizing: content-box; + box-sizing: content-box; + background-color: transparent; + border: none; + border-bottom: 1px solid #ced4da; + border-radius: 0; + outline: none; + -webkit-box-shadow: none; + box-shadow: none; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; +} +.md-form input[type]:focus:not([readonly]) { + border-bottom: 1px solid #4285f4; + -webkit-box-shadow: 0 1px 0 0 #4285f4; + box-shadow: 0 1px 0 0 #4285f4; +} +.md-form input[type]:focus:not([readonly]) + label { + color: #4285f4; +} +.md-form input[type].valid, +.md-form input[type]:focus.valid { + border-bottom: 1px solid #00c851; + -webkit-box-shadow: 0 1px 0 0 #00c851; + box-shadow: 0 1px 0 0 #00c851; +} +.md-form input[type].valid + label, +.md-form input[type]:focus.valid + label { + color: #00c851; +} +.md-form input[type].invalid, +.md-form input[type]:focus.invalid { + border-bottom: 1px solid #f44336; + -webkit-box-shadow: 0 1px 0 0 #f44336; + box-shadow: 0 1px 0 0 #f44336; +} +.md-form input[type].invalid + label, +.md-form input[type]:focus.invalid + label { + color: #f44336; +} +.md-form input[type].validate { + margin-bottom: 2.5rem; +} +.md-form input[type].form-control { + height: auto; + padding: 0.6rem 0 0.4rem 0; + margin: 0 0 0.5rem 0; + color: var(--text-color); + background-color: transparent; + border-radius: 0; +} +.md-form label { + font-size: 0.8rem; + position: absolute; + top: -1rem; + left: 0; + color: #757575; + cursor: text; + transition: color 0.2s ease-out; +} +.modal-open[style] { + padding-right: 0 !important; + overflow: auto; +} +.modal-open[style] #navbar[style] { + padding-right: 1rem !important; +} +#nprogress .bar { + height: 3px !important; + background-color: #29d !important; +} +#nprogress .peg { + box-shadow: 0 0 14px #29d, 0 0 8px #29d !important; +} +@media (max-width: 575px) { + #nprogress .bar { + display: none; + } +} +.noscript-warning { + background-color: #f55; + color: #fff; + font-family: sans-serif; + font-size: 1rem; + font-weight: bold; + position: fixed; + left: 0; + bottom: 0; + text-align: center; + width: 100%; + z-index: 99; +} +.pagination { + margin-top: 3rem; + justify-content: center; +} +.pagination .space { + align-self: flex-end; +} +.pagination .page-number, +.pagination .current, +.pagination .extend { + outline: 0; + border: 0; + background-color: transparent; + font-size: 0.9rem; + padding: 0.5rem 0.75rem; + line-height: 1.25; + border-radius: 0.125rem; +} +.pagination .page-number { + margin: 0 0.05rem; +} +.pagination .page-number:hover, +.pagination .current { + transition: background-color 0.2s ease-in-out; + background-color: var(--link-hover-bg-color); +} +.qr-trigger { + cursor: pointer; + position: relative; +} +.qr-trigger:hover .qr-img { + display: block; + transition: all 0.3s; +} +.qr-img { + max-width: 12rem; + position: absolute; + right: -5.25rem; + z-index: 99; + display: none; + border-radius: 0.2rem; + background-color: transparent; + box-shadow: 0 0 20px -5px rgba(158,158,158,0.2); +} +.scroll-down-bar { + position: absolute; + width: 100%; + height: 6rem; + text-align: center; + cursor: pointer; + bottom: 0; +} +.scroll-down-bar i.iconfont { + font-size: 2rem; + font-weight: bold; + display: inline-block; + position: relative; + padding-top: 2rem; + color: var(--subtitle-color); + transform: translateZ(0); + animation: scroll-down 1.5s infinite; +} +#scroll-top-button { + position: fixed; + z-index: 99; + background: var(--board-bg-color); + transition: background-color 0.2s ease-in-out, bottom 0.3s ease; + border-radius: 4px; + min-width: 40px; + min-height: 40px; + bottom: -60px; + outline: none; + display: flex; + display: -webkit-flex; + align-items: center; + box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12); +} +#scroll-top-button i { + font-size: 32px; + margin: auto; + color: var(--sec-text-color); +} +#scroll-top-button:hover i, +#scroll-top-button:active i { + animation-name: scroll-top; + animation-duration: 1s; + animation-delay: 0.1s; + animation-timing-function: ease-in-out; + animation-iteration-count: infinite; + animation-fill-mode: forwards; + animation-direction: alternate; +} +#local-search-result .search-list-title { + border-left: 3px solid #0d47a1; +} +#local-search-result .search-list-content { + padding: 0 1.25rem; +} +#local-search-result .search-word { + color: #ff4500; +} +#toc { + visibility: hidden; +} +.toc-header { + margin-bottom: 0.5rem; + font-weight: bold; + line-height: 1.2; +} +.toc-header, +.toc-header > i { + font-size: 1.25rem; +} +.toc-body { + max-height: 75vh; + overflow-y: auto; + overflow: -moz-scrollbars-none; + -ms-overflow-style: none; +} +.toc-body ol { + list-style: none; + padding-inline-start: 1rem; +} +.toc-body::-webkit-scrollbar { + display: none; +} +.tocbot-list { + position: relative; +} +.tocbot-list ol { + list-style: none; + padding-left: 1rem; +} +.tocbot-list a { + font-size: 0.95rem; +} +.tocbot-link { + color: var(--text-color); +} +.tocbot-active-link { + font-weight: bold; + color: var(--link-hover-color); +} +.tocbot-is-collapsed { + max-height: 0; +} +.tocbot-is-collapsible { + overflow: hidden; + transition: all 0.3s ease-in-out; +} +.toc-list-item { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.toc-list-item.is-active-li::before { + height: 1rem; + margin: 0.25rem 0; + visibility: visible; +} +.toc-list-item::before { + width: 0.15rem; + height: 0.2rem; + position: absolute; + left: 0.25rem; + content: ""; + border-radius: 2px; + margin: 0.65rem 0; + background: var(--link-hover-color); + visibility: hidden; + transition: height 0.1s ease-in-out, margin 0.1s ease-in-out, visibility 0.1s ease-in-out; +} +.sidebar { + position: -webkit-sticky; + position: sticky; + top: 2rem; + padding: 3rem 0; +} +html { + font-size: 16px; + letter-spacing: 0.02em; +} +html, +body { + height: 100%; + font-family: var(--font-family-sans-serif); + overflow-wrap: break-word; +} +body { + transition: color 0.2s ease-in-out, background-color 0.2s ease-in-out; + background-color: var(--body-bg-color); + color: var(--text-color); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +body a { + color: var(--text-color); + text-decoration: none; + cursor: pointer; + transition: color 0.2s ease-in-out, background-color 0.2s ease-in-out; +} +body a:hover { + color: var(--link-hover-color); + text-decoration: none; + transition: color 0.2s ease-in-out, background-color 0.2s ease-in-out; +} +code { + color: inherit; +} +table { + font-size: inherit; + color: var(--post-text-color); +} +img[lazyload] { + object-fit: cover; +} +*[align="left"] { + text-align: left; +} +*[align="center"] { + text-align: center; +} +*[align="right"] { + text-align: right; +} +::-webkit-scrollbar { + width: 6px; + height: 6px; +} +::-webkit-scrollbar-thumb { + background-color: var(--scrollbar-color); + border-radius: 6px; +} +::-webkit-scrollbar-thumb:hover { + background-color: var(--scrollbar-hover-color); +} +::-webkit-scrollbar-corner { + background-color: transparent; +} +label { + margin-bottom: 0; +} +i.iconfont { + font-size: 1em; + line-height: 1; +} +:root { + --color-mode: "light"; + --body-bg-color: #eee; + --board-bg-color: #fff; + --text-color: #3c4858; + --sec-text-color: #718096; + --post-text-color: #2c3e50; + --post-heading-color: #1a202c; + --post-link-color: #0366d6; + --link-hover-color: #30a9de; + --link-hover-bg-color: #f8f9fa; + --line-color: #eaecef; + --navbar-bg-color: #2f4154; + --navbar-text-color: #fff; + --subtitle-color: #fff; + --scrollbar-color: #c4c6c9; + --scrollbar-hover-color: #a6a6a6; + --button-bg-color: transparent; + --button-hover-bg-color: #f2f3f5; + --highlight-bg-color: #f6f8fa; + --inlinecode-bg-color: rgba(175,184,193,0.2); + --fold-title-color: #3c4858; + --fold-border-color: #eaecef; +} +@media (prefers-color-scheme: dark) { + :root { + --color-mode: "dark"; + } + :root:not([data-user-color-scheme]) { + --body-bg-color: #181c27; + --board-bg-color: #252d38; + --text-color: #c4c6c9; + --sec-text-color: #a7a9ad; + --post-text-color: #c4c6c9; + --post-heading-color: #c4c6c9; + --post-link-color: #1589e9; + --link-hover-color: #30a9de; + --link-hover-bg-color: #364151; + --line-color: #435266; + --navbar-bg-color: #1f3144; + --navbar-text-color: #d0d0d0; + --subtitle-color: #d0d0d0; + --scrollbar-color: #687582; + --scrollbar-hover-color: #9da8b3; + --button-bg-color: transparent; + --button-hover-bg-color: #46647e; + --highlight-bg-color: #303030; + --inlinecode-bg-color: rgba(99,110,123,0.4); + --fold-title-color: #c4c6c9; + --fold-border-color: #435266; + } + :root:not([data-user-color-scheme]) img { + -webkit-filter: brightness(0.9); + filter: brightness(0.9); + transition: filter 0.2s ease-in-out; + } + :root:not([data-user-color-scheme]) .license-box { + background-color: rgba(62,75,94,0.35); + transition: background-color 0.2s ease-in-out; + } + :root:not([data-user-color-scheme]) .gt-comment-admin .gt-comment-content { + background-color: transparent; + transition: background-color 0.2s ease-in-out; + } +} +@media not print { + [data-user-color-scheme="dark"] { + --body-bg-color: #181c27; + --board-bg-color: #252d38; + --text-color: #c4c6c9; + --sec-text-color: #a7a9ad; + --post-text-color: #c4c6c9; + --post-heading-color: #c4c6c9; + --post-link-color: #1589e9; + --link-hover-color: #30a9de; + --link-hover-bg-color: #364151; + --line-color: #435266; + --navbar-bg-color: #1f3144; + --navbar-text-color: #d0d0d0; + --subtitle-color: #d0d0d0; + --scrollbar-color: #687582; + --scrollbar-hover-color: #9da8b3; + --button-bg-color: transparent; + --button-hover-bg-color: #46647e; + --highlight-bg-color: #303030; + --inlinecode-bg-color: rgba(99,110,123,0.4); + --fold-title-color: #c4c6c9; + --fold-border-color: #435266; + } + [data-user-color-scheme="dark"] img { + -webkit-filter: brightness(0.9); + filter: brightness(0.9); + transition: filter 0.2s ease-in-out; + } + [data-user-color-scheme="dark"] .license-box { + background-color: rgba(62,75,94,0.35); + transition: background-color 0.2s ease-in-out; + } + [data-user-color-scheme="dark"] .gt-comment-admin .gt-comment-content { + background-color: transparent; + transition: background-color 0.2s ease-in-out; + } +} +@media print { + :root { + --color-mode: "light"; + } +} +.fade-in-up { + -webkit-animation-name: fade-in-up; + animation-name: fade-in-up; +} +.hidden-mobile { + display: block; +} +.visible-mobile { + display: none; +} +@media (max-width: 575px) { + .hidden-mobile { + display: none; + } + .visible-mobile { + display: block; + } +} +.nomargin-x { + margin-left: 0 !important; + margin-right: 0 !important; +} +.nopadding-x { + padding-left: 0 !important; + padding-right: 0 !important; +} +@media (max-width: 767px) { + .nopadding-x-md { + padding-left: 0 !important; + padding-right: 0 !important; + } +} +.flex-center { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + height: 100%; +} +.hover-with-bg { + display: inline-block; + line-height: 1; +} +.hover-with-bg:hover { + background-color: var(--link-hover-bg-color); + transition-duration: 0.2s; + transition-timing-function: ease-in-out; + border-radius: 0.2rem; +} +@-moz-keyframes fade-in-up { + from { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +@-webkit-keyframes fade-in-up { + from { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +@-o-keyframes fade-in-up { + from { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +@keyframes fade-in-up { + from { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +@-moz-keyframes scroll-down { + 0% { + opacity: 0.8; + top: 0; + } + 50% { + opacity: 0.4; + top: -1em; + } + 100% { + opacity: 0.8; + top: 0; + } +} +@-webkit-keyframes scroll-down { + 0% { + opacity: 0.8; + top: 0; + } + 50% { + opacity: 0.4; + top: -1em; + } + 100% { + opacity: 0.8; + top: 0; + } +} +@-o-keyframes scroll-down { + 0% { + opacity: 0.8; + top: 0; + } + 50% { + opacity: 0.4; + top: -1em; + } + 100% { + opacity: 0.8; + top: 0; + } +} +@keyframes scroll-down { + 0% { + opacity: 0.8; + top: 0; + } + 50% { + opacity: 0.4; + top: -1em; + } + 100% { + opacity: 0.8; + top: 0; + } +} +@-moz-keyframes scroll-top { + 0% { + -webkit-transform: translateY(0); + transform: translateY(0); + } + 50% { + -webkit-transform: translateY(-0.35rem); + transform: translateY(-0.35rem); + } + 100% { + -webkit-transform: translateY(0); + transform: translateY(0); + } +} +@-webkit-keyframes scroll-top { + 0% { + -webkit-transform: translateY(0); + transform: translateY(0); + } + 50% { + -webkit-transform: translateY(-0.35rem); + transform: translateY(-0.35rem); + } + 100% { + -webkit-transform: translateY(0); + transform: translateY(0); + } +} +@-o-keyframes scroll-top { + 0% { + -webkit-transform: translateY(0); + transform: translateY(0); + } + 50% { + -webkit-transform: translateY(-0.35rem); + transform: translateY(-0.35rem); + } + 100% { + -webkit-transform: translateY(0); + transform: translateY(0); + } +} +@keyframes scroll-top { + 0% { + -webkit-transform: translateY(0); + transform: translateY(0); + } + 50% { + -webkit-transform: translateY(-0.35rem); + transform: translateY(-0.35rem); + } + 100% { + -webkit-transform: translateY(0); + transform: translateY(0); + } +} +@media print { + header, + footer, + .side-col, + #scroll-top-button, + .post-prevnext, + #comments { + display: none !important; + } + .markdown-body a:not([href^='#']):not([href^='javascript:']):not(.print-no-link)::after { + content: ' (' attr(href) ')'; + font-size: 0.8rem; + color: var(--post-text-color); + opacity: 0.8; + } + .markdown-body > h1, + .markdown-body h2 { + border-bottom-color: transparent !important; + } + .markdown-body > h1, + .markdown-body h2, + .markdown-body h3, + .markdown-body h4, + .markdown-body h5, + .markdown-body h6 { + margin-top: 1.25em !important; + margin-bottom: 0.25em !important; + } + .markdown-body [data-anchorjs-icon]::after { + display: none; + } + .markdown-body figure.highlight table, + .markdown-body figure.highlight tbody, + .markdown-body figure.highlight tr, + .markdown-body figure.highlight td.code, + .markdown-body figure.highlight td.code pre { + width: 100% !important; + display: block !important; + } + .markdown-body figure.highlight pre > code { + white-space: pre-wrap; + } + .markdown-body figure.highlight .gutter, + .markdown-body figure.highlight .code-widget { + display: none !important; + } + .post-metas a { + text-decoration: none; + } +} +@media not print { + #seo-header { + display: none; + } +} +.index-card { + margin-bottom: 2.5rem; +} +.index-img img { + display: block; + width: 100%; + height: 10rem; + object-fit: cover; + box-shadow: 0 5px 11px 0 rgba(0,0,0,0.18), 0 4px 15px 0 rgba(0,0,0,0.15); + border-radius: 0.25rem; + background-color: transparent; +} +.index-info { + display: flex; + flex-direction: column; + justify-content: space-between; + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} +.index-header { + color: var(--text-color); + font-size: 1.5rem; + font-weight: bold; + line-height: 1.4; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-bottom: 0.25rem; +} +.index-header .index-pin { + color: var(--text-color); + font-size: 1.5rem; + margin-right: 0.15rem; +} +.index-btm { + color: var(--sec-text-color); +} +.index-btm a { + color: var(--sec-text-color); +} +.index-excerpt { + color: var(--sec-text-color); + margin: 0.5rem 0; + height: calc(1.4rem * 3); + overflow: hidden; + display: flex; +} +.index-excerpt > div { + width: 100%; + line-height: 1.4rem; + word-break: break-word; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; +} +.index-excerpt__noimg { + height: auto; + max-height: calc(1.4rem * 3); +} +@media (max-width: 767px) { + .index-info { + padding-top: 1.25rem; + } + .index-header { + font-size: 1.25rem; + white-space: normal; + overflow: hidden; + word-break: break-word; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + } + .index-header .index-pin { + font-size: 1.25rem; + } + .index-excerpt { + height: auto; + max-height: calc(1.4rem * 3); + margin: 0.25rem 0; + } +} +#valine.v[data-class=v] .status-bar, +#valine.v[data-class=v] .veditor, +#valine.v[data-class=v] .vinput, +#valine.v[data-class=v] .vbtn, +#valine.v[data-class=v] p, +#valine.v[data-class=v] pre code { + color: var(--text-color); +} +#valine.v[data-class=v] .vinput::placeholder { + color: var(--sec-text-color); +} +#valine.v[data-class=v] .vicon { + fill: var(--text-color); +} +.gt-container .gt-comment-content:hover { + -webkit-box-shadow: none; + box-shadow: none; +} +.gt-container .gt-comment-body { + color: var(--text-color) !important; + transition: color 0.2s ease-in-out; +} +#remark-km423lmfdslkm34-back { + z-index: 1030; +} +#remark-km423lmfdslkm34-node { + z-index: 1031; +} +.markdown-body .highlight pre, +.markdown-body pre { + padding: 1.45rem 1rem; +} +.markdown-body pre code.hljs { + padding: 0; +} +.markdown-body pre[class*="language-"] { + padding-top: 1.45rem; + padding-bottom: 1.45rem; + padding-right: 1rem; + line-height: 1.5; + margin-bottom: 1rem; +} +.markdown-body .code-wrapper { + position: relative; + border-radius: 4px; + margin-bottom: 1rem; +} +.markdown-body .hljs, +.markdown-body .highlight pre, +.markdown-body .code-wrapper pre, +.markdown-body figure.highlight td.gutter { + transition: color 0.2s ease-in-out, background-color 0.2s ease-in-out; + background-color: var(--highlight-bg-color); +} +pre[class*=language-].line-numbers { + position: initial; +} +figure { + margin: 1rem 0; +} +figure.highlight { + position: relative; +} +figure.highlight table { + border: 0; + margin: 0; + width: auto; + border-radius: 4px; +} +figure.highlight td { + border: 0; + padding: 0; +} +figure.highlight tr { + border: 0; +} +figure.highlight td.code { + width: 100%; +} +figure.highlight td.gutter { + display: table-cell; + position: -webkit-sticky; + position: sticky; + left: 0; + z-index: 1; +} +figure.highlight td.gutter pre { + text-align: right; + padding: 0 0.75rem; + border-radius: initial; + border-right: 1px solid #999; +} +figure.highlight td.gutter pre span.line { + color: #999; +} +figure.highlight td.code > pre { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.markdown-body { + font-size: 1rem; + line-height: 1.6; + font-family: var(--font-family-sans-serif); + margin-bottom: 2rem; + color: var(--post-text-color); +} +.markdown-body > h1, +.markdown-body h2 { + border-bottom-color: var(--line-color); +} +.markdown-body > h1, +.markdown-body h2, +.markdown-body h3, +.markdown-body h4, +.markdown-body h5, +.markdown-body h6 { + color: var(--post-heading-color); + transition: color 0.2s ease-in-out, border-bottom-color 0.2s ease-in-out; + font-weight: bold; + margin-bottom: 0.75em; + margin-top: 2em; +} +.markdown-body > h1::before, +.markdown-body h2::before, +.markdown-body h3::before, +.markdown-body h4::before, +.markdown-body h5::before, +.markdown-body h6::before { + display: block; + content: ""; + margin-top: -5rem; + height: 5rem; + width: 1px; + visibility: hidden; +} +.markdown-body > h1:focus, +.markdown-body h2:focus, +.markdown-body h3:focus, +.markdown-body h4:focus, +.markdown-body h5:focus, +.markdown-body h6:focus { + outline: none; +} +.markdown-body a { + color: var(--post-link-color); +} +.markdown-body strong { + font-weight: bold; +} +.markdown-body code { + tab-size: 4; + background-color: var(--inlinecode-bg-color); + transition: background-color 0.2s ease-in-out; +} +.markdown-body table tr { + background-color: var(--board-bg-color); + transition: background-color 0.2s ease-in-out; +} +.markdown-body table tr:nth-child(2n) { + background-color: var(--board-bg-color); + transition: background-color 0.2s ease-in-out; +} +.markdown-body table th, +.markdown-body table td { + border-color: var(--line-color); + transition: border-color 0.2s ease-in-out; +} +.markdown-body pre { + font-size: 85% !important; +} +.markdown-body pre .mermaid { + text-align: center; +} +.markdown-body pre .mermaid > svg { + min-width: 100%; +} +.markdown-body p > img, +.markdown-body p > a > img, +.markdown-body figure > img, +.markdown-body figure > a > img { + max-width: 90%; + margin: 1.5rem auto; + display: block; + box-shadow: 0 5px 11px 0 rgba(0,0,0,0.18), 0 4px 15px 0 rgba(0,0,0,0.15); + border-radius: 4px; + background-color: transparent; +} +.markdown-body blockquote { + color: var(--sec-text-color); +} +.markdown-body details { + cursor: pointer; +} +.markdown-body details summary { + outline: none; +} +hr, +.markdown-body hr { + background-color: initial; + border-top: 1px solid var(--line-color); + transition: border-top-color 0.2s ease-in-out; +} +.markdown-body hr { + height: 0; + margin: 2rem 0; +} +.markdown-body figcaption.image-caption { + font-size: 0.8rem; + color: var(--post-text-color); + opacity: 0.65; + line-height: 1; + margin: -0.75rem auto 2rem; + text-align: center; +} +.markdown-body figcaption:not(.image-caption) { + display: none; +} +.post-content, +post-custom { + box-sizing: border-box; + padding-left: 10%; + padding-right: 10%; +} +@media (max-width: 767px) { + .post-content, + post-custom { + padding-left: 2rem; + padding-right: 2rem; + } +} +@media (max-width: 424px) { + .post-content, + post-custom { + padding-left: 1rem; + padding-right: 1rem; + } + .anchorjs-link-left { + opacity: 0 !important; + } +} +.page-content strong, +.post-content strong { + font-weight: bold; +} +.page-content > *:nth-child(2), +.post-content > *:nth-child(2) { + margin-top: 0; +} +.page-content img, +.post-content img { + object-fit: cover; + max-width: 100%; +} +@media (max-width: 767px) { + .page-content, + .post-content { + overflow-x: hidden; + } +} +.post-metas { + display: flex; + flex-wrap: wrap; + font-size: 0.9rem; +} +.post-meta > *:not(.hover-with-bg) { + margin-right: 0.2rem; +} +.post-prevnext { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + font-size: 0.9rem; + margin-left: -0.35rem; + margin-right: -0.35rem; +} +.post-prevnext .post-prev, +.post-prevnext .post-next { + display: flex; + padding-left: 0; + padding-right: 0; +} +.post-prevnext .post-prev i, +.post-prevnext .post-next i { + font-size: 1.5rem; +} +.post-prevnext .post-prev a, +.post-prevnext .post-next a { + display: flex; + align-items: center; +} +.post-prevnext .post-prev .hidden-mobile, +.post-prevnext .post-next .hidden-mobile { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + text-overflow: ellipsis; + overflow: hidden; +} +@media (max-width: 575px) { + .post-prevnext .post-prev .hidden-mobile, + .post-prevnext .post-next .hidden-mobile { + display: none; + } +} +.post-prevnext .post-prev:hover i, +.post-prevnext .post-prev:active i, +.post-prevnext .post-next:hover i, +.post-prevnext .post-next:active i { + -webkit-animation-duration: 1s; + animation-duration: 1s; + -webkit-animation-delay: 0.1s; + animation-delay: 0.1s; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; + -webkit-animation-fill-mode: forwards; + animation-fill-mode: forwards; + -webkit-animation-direction: alternate; + animation-direction: alternate; +} +.post-prevnext .post-prev:hover i, +.post-prevnext .post-prev:active i { + -webkit-animation-name: post-prev-anim; + animation-name: post-prev-anim; +} +.post-prevnext .post-next:hover i, +.post-prevnext .post-next:active i { + -webkit-animation-name: post-next-anim; + animation-name: post-next-anim; +} +.post-prevnext .post-next { + justify-content: flex-end; +} +.post-prevnext .fa-chevron-left { + margin-right: 0.5rem; +} +.post-prevnext .fa-chevron-right { + margin-left: 0.5rem; +} +#seo-header { + color: var(--post-heading-color); + font-weight: bold; + margin-top: 0.5em; + margin-bottom: 0.75em; + border-bottom-color: var(--line-color); + border-bottom-style: solid; + border-bottom-width: 2px; + line-height: 1.5; +} +.custom, +#comments { + margin-top: 2rem; +} +#comments noscript { + display: block; + text-align: center; + padding: 2rem 0; +} +.visitors { + font-size: 0.8em; + padding: 0.45rem; + float: right; +} +a.fancybox:hover { + text-decoration: none; +} +mjx-container, +.mjx-container { + overflow-x: auto; + overflow-y: hidden !important; + padding: 0.5em 0; +} +mjx-container:focus, +.mjx-container:focus, +mjx-container svg:focus, +.mjx-container svg:focus { + outline: none; +} +.mjx-char { + line-height: 1; +} +.katex-block { + overflow-x: auto; +} +.katex, +.mjx-mrow { + white-space: pre-wrap !important; +} +.footnote-ref [class*=hint--][aria-label]:after { + max-width: 12rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +@-moz-keyframes post-prev-anim { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + 50% { + -webkit-transform: translateX(-0.35rem); + transform: translateX(-0.35rem); + } + 100% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} +@-webkit-keyframes post-prev-anim { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + 50% { + -webkit-transform: translateX(-0.35rem); + transform: translateX(-0.35rem); + } + 100% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} +@-o-keyframes post-prev-anim { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + 50% { + -webkit-transform: translateX(-0.35rem); + transform: translateX(-0.35rem); + } + 100% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} +@keyframes post-prev-anim { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + 50% { + -webkit-transform: translateX(-0.35rem); + transform: translateX(-0.35rem); + } + 100% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} +@-moz-keyframes post-next-anim { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + 50% { + -webkit-transform: translateX(0.35rem); + transform: translateX(0.35rem); + } + 100% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} +@-webkit-keyframes post-next-anim { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + 50% { + -webkit-transform: translateX(0.35rem); + transform: translateX(0.35rem); + } + 100% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} +@-o-keyframes post-next-anim { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + 50% { + -webkit-transform: translateX(0.35rem); + transform: translateX(0.35rem); + } + 100% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} +@keyframes post-next-anim { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + 50% { + -webkit-transform: translateX(0.35rem); + transform: translateX(0.35rem); + } + 100% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} +.fold { + margin: 1rem 0; + border: 0.5px solid var(--fold-border-color); + position: relative; + clear: both; + border-radius: 0.125rem; +} +.fold .fold-title { + color: var(--fold-title-color); + padding: 0.5rem 0.75rem; + font-size: 0.9rem; + font-weight: bold; + border-radius: 0.125rem; +} +.fold .fold-title:not(.collapsed) > .fold-arrow { + transform: rotate(90deg); + transform-origin: center center; +} +.fold .fold-title .fold-arrow { + display: inline-block; + margin-right: 0.35rem; + transition: transform 0.3s ease-out; +} +.fold .fold-content { + padding: 1rem 1rem; +} +.fold .fold-content > *:last-child { + margin-bottom: 0; +} +.fold-default, +.fold-secondary { + background-color: rgba(187,187,187,0.25); +} +.fold-primary { + background-color: rgba(183,160,224,0.25); +} +.fold-info { + background-color: rgba(160,197,228,0.25); +} +.fold-success { + background-color: rgba(174,220,174,0.25); +} +.fold-warning { + background-color: rgba(248,214,166,0.25); +} +.fold-danger { + background-color: rgba(236,169,167,0.25); +} +.fold-light { + background-color: rgba(254,254,254,0.25); +} +.note { + padding: 0.75rem; + border-left: 0.35rem solid; + border-radius: 0.25rem; + margin: 1.5rem 0; + color: var(--text-color); + transition: color 0.2s ease-in-out; + font-size: 0.9rem; +} +.note a { + color: var(--text-color); + transition: color 0.2s ease-in-out; +} +.note *:last-child { + margin-bottom: 0; +} +.note-default, +.note-secondary { + background-color: rgba(187,187,187,0.25); + border-color: #777; +} +.note-primary { + background-color: rgba(183,160,224,0.25); + border-color: #6f42c1; +} +.note-success { + background-color: rgba(174,220,174,0.25); + border-color: #5cb85c; +} +.note-danger { + background-color: rgba(236,169,167,0.25); + border-color: #d9534f; +} +.note-warning { + background-color: rgba(248,214,166,0.25); + border-color: #f0ad4e; +} +.note-info { + background-color: rgba(160,197,228,0.25); + border-color: #428bca; +} +.note-light { + background-color: rgba(254,254,254,0.25); + border-color: #0f0f0f; +} +.label { + display: inline; + border-radius: 3px; + font-size: 85%; + margin: 0; + padding: 0.2em 0.4em; + color: var(--text-color); + transition: color 0.2s ease-in-out; +} +.label-default, +.label-secondary { + background-color: rgba(187,187,187,0.25); +} +.label-primary { + background-color: rgba(183,160,224,0.25); +} +.label-info { + background-color: rgba(160,197,228,0.25); +} +.label-success { + background-color: rgba(174,220,174,0.25); +} +.label-warning { + background-color: rgba(248,214,166,0.25); +} +.label-danger { + background-color: rgba(236,169,167,0.25); +} +.markdown-body .btn { + border: 1px solid var(--line-color); + background-color: var(--button-bg-color); + color: var(--text-color); + transition: color 0.2s ease-in-out, background 0.2s ease-in-out, border-color 0.2s ease-in-out; + border-radius: 0.25rem; + display: inline-block; + font-size: 0.875em; + line-height: 2; + padding: 0 0.75rem; + margin-bottom: 1rem; +} +.markdown-body .btn:hover { + background-color: var(--button-hover-bg-color); + text-decoration: none; +} +.group-image-container { + margin: 1.5rem auto; +} +.group-image-container img { + margin: 0 auto; + border-radius: 3px; + background-color: transparent; + box-shadow: 0 3px 9px 0 rgba(0,0,0,0.15), 0 3px 9px 0 rgba(0,0,0,0.15); +} +.group-image-row { + margin-bottom: 0.5rem; + display: flex; + justify-content: center; +} +.group-image-wrap { + flex: 1; + display: flex; + justify-content: center; +} +.group-image-wrap:not(:last-child) { + margin-right: 0.25rem; +} +input[type=checkbox] { + margin: 0 0.2em 0.2em 0; + vertical-align: middle; +} +.list-group a ~ p.h5 { + margin-top: 1rem; +} +.list-group-item { + display: flex; + background-color: transparent; + border: 0; +} +.list-group-item time { + flex: 0 0 5rem; +} +.list-group-item .list-group-item-title { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +@media (max-width: 575px) { + .list-group-item { + font-size: 0.95rem; + padding: 0.5rem 0.75rem; + } + .list-group-item time { + flex: 0 0 4rem; + } +} +.list-group-item-action { + color: var(--text-color); +} +.list-group-item-action:focus, +.list-group-item-action:hover { + color: var(--link-hover-color); + background-color: var(--link-hover-bg-color); +} +.about-avatar { + position: relative; + margin: -8rem auto 1rem; + width: 10rem; + height: 10rem; + z-index: 3; +} +.about-avatar img { + width: 100%; + height: 100%; + border-radius: 50%; + background-color: transparent; + object-fit: cover; + box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12); +} +.about-info > div { + margin-bottom: 0.5rem; +} +.about-name { + font-size: 1.75rem; + font-weight: bold; +} +.about-intro { + font-size: 1rem; +} +.about-icons > a:not(:last-child) { + margin-right: 0.5rem; +} +.about-icons > a > i { + font-size: 1.5rem; +} +.category-bar .category-list { + max-height: 85vh; + overflow-y: auto; + overflow-x: hidden; +} +.category-bar .category-list::-webkit-scrollbar { + display: none; +} +.category-bar .category-list > .category-sub > a { + font-weight: bold; + font-size: 1.2rem; +} +.category-bar .category-list .category-item-action i { + margin: 0; +} +.category-bar .category-list .category-subitem.list-group-item { + padding-left: 0.5rem; + padding-right: 0; +} +.category-bar .category-list .category-collapse .category-post-list { + margin-top: 0.25rem; + margin-bottom: 0.5rem; +} +.category-bar .category-list .category-collapse .category-post { + font-size: 0.9rem; + line-height: 1.75; +} +.category-bar .category-list .category-item-action:hover { + background-color: initial; +} +.category-bar .list-group-item { + padding: 0; +} +.category-bar .list-group-item.active { + color: var(--link-hover-color); + background-color: initial; + font-weight: bold; + font-family: "iconfont"; + font-style: normal; + -webkit-font-smoothing: antialiased; +} +.category-bar .list-group-item.active::before { + content: "\e61f"; + font-weight: initial; + margin-right: 0.25rem; +} +.category-bar .list-group-count { + margin-left: 0.2rem; + margin-right: 0.2rem; + font-size: 0.9em; +} +.category-bar .list-group-item-action:focus, +.category-bar .list-group-item-action:hover { + background-color: initial; +} +.category-chains { + display: flex; + flex-wrap: wrap; +} +.category-chains > *:not(:last-child) { + margin-right: 1em; +} +.category:not(:last-child) { + margin-bottom: 1rem; +} +.category .category-item, +.category .category-subitem { + font-weight: bold; + display: flex; + align-items: center; +} +.category .category-item { + font-size: 1.25rem; +} +.category .category-subitem { + font-size: 1.1rem; +} +.category .category-collapse { + padding-left: 1.25rem; + width: 100%; +} +.category .category-count { + font-size: 0.9rem; + font-weight: initial; + min-width: 1.3em; + line-height: 1.3em; + display: flex; + align-items: center; +} +.category .category-count i { + padding-right: 0.25rem; +} +.category .category-count span { + width: 2rem; +} +.category .category-post { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.category .category-item-action:not(.collapsed) > i { + transform: rotate(90deg); + transform-origin: center center; +} +.category .category-item-action i { + transition: transform 0.3s ease-out; + display: inline-block; + margin-left: 0.25rem; +} +.category .category-item-action .category:hover { + z-index: 1; + color: var(--link-hover-color); + text-decoration: none; + background-color: var(--link-hover-bg-color); +} +.category .row { + margin-left: 0; + margin-right: 0; +} +.tagcloud { + padding: 1rem 5%; +} +.tagcloud a { + display: inline-block; + padding: 0.5rem; +} +.tagcloud a:hover { + color: var(--link-hover-color) !important; +} +.links .card { + box-shadow: none; + min-width: 33%; + background-color: transparent; + border: 0; +} +.links .card-body { + margin: 1rem 0; + padding: 1rem; + border-radius: 0.3rem; + display: block; + width: 100%; + height: 100%; +} +.links .card-body:hover .link-avatar { + transform: scale(1.1); +} +.links .card-content { + display: flex; + flex-wrap: nowrap; + width: 100%; + height: 3.5rem; +} +.link-avatar { + flex: none; + width: 3rem; + height: 3rem; + margin-right: 0.75rem; + object-fit: cover; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; +} +.link-avatar img { + width: 100%; + height: 100%; + border-radius: 50%; + background-color: transparent; + object-fit: cover; +} +.link-text { + flex: 1; + display: grid; + flex-direction: column; + line-height: 1.5; +} +.link-title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--text-color); + font-weight: bold; +} +.link-intro { + max-height: 2rem; + font-size: 0.85rem; + line-height: 1.2; + color: var(--sec-text-color); + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + text-overflow: ellipsis; + overflow: hidden; +} +@media (max-width: 767px) { + .links { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + } + .links .card { + padding-left: 2rem; + padding-right: 2rem; + } +} +@media (min-width: 768px) { + .link-text:only-child { + margin-left: 1rem; + } +} diff --git a/img/about.jpg b/img/about.jpg new file mode 100644 index 0000000..5e1b29e Binary files /dev/null and b/img/about.jpg differ diff --git a/img/avatar.png b/img/avatar.png new file mode 100644 index 0000000..ffd1c77 Binary files /dev/null and b/img/avatar.png differ diff --git a/img/banner_img.jpg b/img/banner_img.jpg new file mode 100644 index 0000000..3935759 Binary files /dev/null and b/img/banner_img.jpg differ diff --git a/img/default.png b/img/default.png new file mode 100644 index 0000000..2bc2cd7 Binary files /dev/null and b/img/default.png differ diff --git a/img/fluid.png b/img/fluid.png new file mode 100644 index 0000000..368a58a Binary files /dev/null and b/img/fluid.png differ diff --git a/img/index1_jpg.jpg b/img/index1_jpg.jpg new file mode 100644 index 0000000..9762f18 Binary files /dev/null and b/img/index1_jpg.jpg differ diff --git a/img/loading.gif b/img/loading.gif new file mode 100644 index 0000000..c5126ed Binary files /dev/null and b/img/loading.gif differ diff --git a/img/pages_img.png b/img/pages_img.png new file mode 100644 index 0000000..dd91f2d Binary files /dev/null and b/img/pages_img.png differ diff --git a/img/police_beian.png b/img/police_beian.png new file mode 100644 index 0000000..60190da Binary files /dev/null and b/img/police_beian.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..5d2fdca --- /dev/null +++ b/index.html @@ -0,0 +1,866 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + + +
+ + + +
+ +
+ + +
+ + 浏览器插件入门 + +
+ + +
+ +
+ + + +
+ +
+ + +
+ + Typescript-declare + +
+ + +
+ +
+ + +
+ + Javascript忍者秘籍blog-1 + +
+ + +
+ +
+ + +
+ + Git宝典 + +
+ + +
+ +
+ + +
+ + 浅学webpack + +
+ + +
+ +
+ + +
+ + 自定义React hooks + +
+ + +
+ + + + + + + + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/js/boot.js b/js/boot.js new file mode 100644 index 0000000..26c3a1a --- /dev/null +++ b/js/boot.js @@ -0,0 +1,22 @@ +/* global Fluid */ + +Fluid.boot = {}; + +Fluid.boot.registerEvents = function() { + Fluid.events.billboard(); + Fluid.events.registerNavbarEvent(); + Fluid.events.registerParallaxEvent(); + Fluid.events.registerScrollDownArrowEvent(); + Fluid.events.registerScrollTopArrowEvent(); + Fluid.events.registerImageLoadedEvent(); +}; + +Fluid.boot.refresh = function() { + Fluid.plugins.fancyBox(); + Fluid.plugins.codeWidget(); + Fluid.events.refresh(); +}; + +document.addEventListener('DOMContentLoaded', function() { + Fluid.boot.registerEvents(); +}); diff --git a/js/color-schema.js b/js/color-schema.js new file mode 100644 index 0000000..5b1b75c --- /dev/null +++ b/js/color-schema.js @@ -0,0 +1,286 @@ +/* global Fluid */ + +/** + * Modified from https://blog.skk.moe/post/hello-darkmode-my-old-friend/ + */ +(function(window, document) { + var rootElement = document.documentElement; + var colorSchemaStorageKey = 'Fluid_Color_Scheme'; + var colorSchemaMediaQueryKey = '--color-mode'; + var userColorSchemaAttributeName = 'data-user-color-scheme'; + var defaultColorSchemaAttributeName = 'data-default-color-scheme'; + var colorToggleButtonSelector = '#color-toggle-btn'; + var colorToggleIconSelector = '#color-toggle-icon'; + var iframeSelector = 'iframe'; + + function setLS(k, v) { + try { + localStorage.setItem(k, v); + } catch (e) {} + } + + function removeLS(k) { + try { + localStorage.removeItem(k); + } catch (e) {} + } + + function getLS(k) { + try { + return localStorage.getItem(k); + } catch (e) { + return null; + } + } + + function getSchemaFromHTML() { + var res = rootElement.getAttribute(defaultColorSchemaAttributeName); + if (typeof res === 'string') { + return res.replace(/["'\s]/g, ''); + } + return null; + } + + function getSchemaFromCSSMediaQuery() { + var res = getComputedStyle(rootElement).getPropertyValue( + colorSchemaMediaQueryKey + ); + if (typeof res === 'string') { + return res.replace(/["'\s]/g, ''); + } + return null; + } + + function resetSchemaAttributeAndLS() { + rootElement.setAttribute(userColorSchemaAttributeName, getDefaultColorSchema()); + removeLS(colorSchemaStorageKey); + } + + var validColorSchemaKeys = { + dark : true, + light: true + }; + + function getDefaultColorSchema() { + // 取默认字段的值 + var schema = getSchemaFromHTML(); + // 如果明确指定了 schema 则返回 + if (validColorSchemaKeys[schema]) { + return schema; + } + // 默认优先按 prefers-color-scheme + schema = getSchemaFromCSSMediaQuery(); + if (validColorSchemaKeys[schema]) { + return schema; + } + // 否则按本地时间是否大于 18 点或凌晨 0 ~ 6 点 + var hours = new Date().getHours(); + if (hours >= 18 || (hours >= 0 && hours <= 6)) { + return 'dark'; + } + return 'light'; + } + + function applyCustomColorSchemaSettings(schema) { + // 接受从「开关」处传来的模式,或者从 localStorage 读取,否则按默认设置值 + var current = schema || getLS(colorSchemaStorageKey) || getDefaultColorSchema(); + + if (current === getDefaultColorSchema()) { + // 当用户切换的显示模式和默认模式相同时,则恢复为自动模式 + resetSchemaAttributeAndLS(); + } else if (validColorSchemaKeys[current]) { + rootElement.setAttribute( + userColorSchemaAttributeName, + current + ); + } else { + // 特殊情况重置 + resetSchemaAttributeAndLS(); + return; + } + + // 根据当前模式设置图标 + setButtonIcon(current); + + // 设置代码高亮 + setHighlightCSS(current); + + // 设置其他应用 + setApplications(current); + } + + var invertColorSchemaObj = { + dark : 'light', + light: 'dark' + }; + + function getIconClass(scheme) { + return 'icon-' + scheme; + } + + function toggleCustomColorSchema() { + var currentSetting = getLS(colorSchemaStorageKey); + + if (validColorSchemaKeys[currentSetting]) { + // 从 localStorage 中读取模式,并取相反的模式 + currentSetting = invertColorSchemaObj[currentSetting]; + } else if (currentSetting === null) { + // 当 localStorage 中没有相关值,或者 localStorage 抛了 Error + // 先按照按钮的状态进行切换 + var iconElement = document.querySelector(colorToggleIconSelector); + if (iconElement) { + currentSetting = iconElement.getAttribute('data'); + } + if (!iconElement || !validColorSchemaKeys[currentSetting]) { + // 当 localStorage 中没有相关值,或者 localStorage 抛了 Error,则读取默认值并切换到相反的模式 + currentSetting = invertColorSchemaObj[getSchemaFromCSSMediaQuery()]; + } + } else { + return; + } + // 将相反的模式写入 localStorage + setLS(colorSchemaStorageKey, currentSetting); + + return currentSetting; + } + + function setButtonIcon(schema) { + if (validColorSchemaKeys[schema]) { + // 切换图标 + var icon = getIconClass('dark'); + if (schema) { + icon = getIconClass(schema); + } + var iconElement = document.querySelector(colorToggleIconSelector); + if (iconElement) { + iconElement.setAttribute( + 'class', + 'iconfont ' + icon + ); + iconElement.setAttribute( + 'data', + invertColorSchemaObj[schema] + ); + } else { + // 如果图标不存在则说明图标还没加载出来,等到页面全部加载再尝试切换 + Fluid.utils.waitElementLoaded(colorToggleIconSelector, function() { + var iconElement = document.querySelector(colorToggleIconSelector); + if (iconElement) { + iconElement.setAttribute( + 'class', + 'iconfont ' + icon + ); + iconElement.setAttribute( + 'data', + invertColorSchemaObj[schema] + ); + } + }); + } + if (document.documentElement.getAttribute('data-user-color-scheme')) { + var color = getComputedStyle(document.documentElement).getPropertyValue('--navbar-bg-color').trim() + document.querySelector('meta[name="theme-color"]').setAttribute('content', color) + } + } + } + + function setHighlightCSS(schema) { + // 启用对应的代码高亮的样式 + var lightCss = document.getElementById('highlight-css'); + var darkCss = document.getElementById('highlight-css-dark'); + if (schema === 'dark') { + if (darkCss) { + darkCss.removeAttribute('disabled'); + } + if (lightCss) { + lightCss.setAttribute('disabled', ''); + } + } else { + if (lightCss) { + lightCss.removeAttribute('disabled'); + } + if (darkCss) { + darkCss.setAttribute('disabled', ''); + } + } + + setTimeout(function() { + // 设置代码块组件样式 + document.querySelectorAll('.markdown-body pre').forEach((pre) => { + var cls = Fluid.utils.getBackgroundLightness(pre) >= 0 ? 'code-widget-light' : 'code-widget-dark'; + var widget = pre.querySelector('.code-widget-light, .code-widget-dark'); + if (widget) { + widget.classList.remove('code-widget-light', 'code-widget-dark'); + widget.classList.add(cls); + } + }); + }, 200); + } + + function setApplications(schema) { + // 设置 remark42 评论主题 + if (window.REMARK42) { + window.REMARK42.changeTheme(schema); + } + + // 设置 cusdis 评论主题 + if (window.CUSDIS) { + window.CUSDIS.setTheme(schema); + } + + // 设置 utterances 评论主题 + var utterances = document.querySelector('.utterances-frame'); + if (utterances) { + var utterancesTheme = schema === 'dark' ? window.UtterancesThemeDark : window.UtterancesThemeLight; + const message = { + type : 'set-theme', + theme: utterancesTheme + }; + utterances.contentWindow.postMessage(message, 'https://utteranc.es'); + } + + // 设置 giscus 评论主题 + var giscus = document.querySelector('iframe.giscus-frame'); + if (giscus) { + var giscusTheme = schema === 'dark' ? window.GiscusThemeDark : window.GiscusThemeLight; + const message = { + setConfig: { + theme: giscusTheme, + } + }; + giscus.style.cssText += 'color-scheme: normal;'; + giscus.contentWindow.postMessage({ 'giscus': message }, 'https://giscus.app'); + } + } + + // 当页面加载时,将显示模式设置为 localStorage 中自定义的值(如果有的话) + applyCustomColorSchemaSettings(); + + Fluid.utils.waitElementLoaded(colorToggleIconSelector, function() { + applyCustomColorSchemaSettings(); + var button = document.querySelector(colorToggleButtonSelector); + if (button) { + // 当用户点击切换按钮时,获得新的显示模式、写入 localStorage、并在页面上生效 + button.addEventListener('click', function() { + applyCustomColorSchemaSettings(toggleCustomColorSchema()); + }); + var icon = document.querySelector(colorToggleIconSelector); + if (icon) { + // 光标悬停在按钮上时,切换图标 + button.addEventListener('mouseenter', function() { + var current = icon.getAttribute('data'); + icon.classList.replace(getIconClass(invertColorSchemaObj[current]), getIconClass(current)); + }); + button.addEventListener('mouseleave', function() { + var current = icon.getAttribute('data'); + icon.classList.replace(getIconClass(current), getIconClass(invertColorSchemaObj[current])); + }); + } + } + }); + + Fluid.utils.waitElementLoaded(iframeSelector, function() { + applyCustomColorSchemaSettings(); + }); + +})(window, document); diff --git a/js/events.js b/js/events.js new file mode 100644 index 0000000..bc6b773 --- /dev/null +++ b/js/events.js @@ -0,0 +1,184 @@ +/* global Fluid */ + +HTMLElement.prototype.wrap = function(wrapper) { + this.parentNode.insertBefore(wrapper, this); + this.parentNode.removeChild(this); + wrapper.appendChild(this); +}; + +Fluid.events = { + + registerNavbarEvent: function() { + var navbar = jQuery('#navbar'); + if (navbar.length === 0) { + return; + } + var submenu = jQuery('#navbar .dropdown-menu'); + if (navbar.offset().top > 0) { + navbar.removeClass('navbar-dark'); + submenu.removeClass('navbar-dark'); + } + Fluid.utils.listenScroll(function() { + navbar[navbar.offset().top > 50 ? 'addClass' : 'removeClass']('top-nav-collapse'); + submenu[navbar.offset().top > 50 ? 'addClass' : 'removeClass']('dropdown-collapse'); + if (navbar.offset().top > 0) { + navbar.removeClass('navbar-dark'); + submenu.removeClass('navbar-dark'); + } else { + navbar.addClass('navbar-dark'); + submenu.removeClass('navbar-dark'); + } + }); + jQuery('#navbar-toggler-btn').on('click', function() { + jQuery('.animated-icon').toggleClass('open'); + jQuery('#navbar').toggleClass('navbar-col-show'); + }); + }, + + registerParallaxEvent: function() { + var ph = jQuery('#banner[parallax="true"]'); + if (ph.length === 0) { + return; + } + var board = jQuery('#board'); + if (board.length === 0) { + return; + } + var parallax = function() { + var pxv = jQuery(window).scrollTop() / 5; + var offset = parseInt(board.css('margin-top'), 10); + var max = 96 + offset; + if (pxv > max) { + pxv = max; + } + ph.css({ + transform: 'translate3d(0,' + pxv + 'px,0)' + }); + var sideCol = jQuery('.side-col'); + if (sideCol) { + sideCol.css({ + 'padding-top': pxv + 'px' + }); + } + }; + Fluid.utils.listenScroll(parallax); + }, + + registerScrollDownArrowEvent: function() { + var scrollbar = jQuery('.scroll-down-bar'); + if (scrollbar.length === 0) { + return; + } + scrollbar.on('click', function() { + Fluid.utils.scrollToElement('#board', -jQuery('#navbar').height()); + }); + }, + + registerScrollTopArrowEvent: function() { + var topArrow = jQuery('#scroll-top-button'); + if (topArrow.length === 0) { + return; + } + var board = jQuery('#board'); + if (board.length === 0) { + return; + } + var posDisplay = false; + var scrollDisplay = false; + // Position + var setTopArrowPos = function() { + var boardRight = board[0].getClientRects()[0].right; + var bodyWidth = document.body.offsetWidth; + var right = bodyWidth - boardRight; + posDisplay = right >= 50; + topArrow.css({ + 'bottom': posDisplay && scrollDisplay ? '20px' : '-60px', + 'right' : right - 64 + 'px' + }); + }; + setTopArrowPos(); + jQuery(window).resize(setTopArrowPos); + // Display + var headerHeight = board.offset().top; + Fluid.utils.listenScroll(function() { + var scrollHeight = document.body.scrollTop + document.documentElement.scrollTop; + scrollDisplay = scrollHeight >= headerHeight; + topArrow.css({ + 'bottom': posDisplay && scrollDisplay ? '20px' : '-60px' + }); + }); + // Click + topArrow.on('click', function() { + jQuery('body,html').animate({ + scrollTop: 0, + easing : 'swing' + }); + }); + }, + + registerImageLoadedEvent: function() { + if (!('NProgress' in window)) { return; } + + var bg = document.getElementById('banner'); + if (bg) { + var src = bg.style.backgroundImage; + var url = src.match(/\((.*?)\)/)[1].replace(/(['"])/g, ''); + var img = new Image(); + img.onload = function() { + window.NProgress && window.NProgress.inc(0.2); + }; + img.src = url; + if (img.complete) { img.onload(); } + } + + var notLazyImages = jQuery('main img:not([lazyload])'); + var total = notLazyImages.length; + for (const img of notLazyImages) { + const old = img.onload; + img.onload = function() { + old && old(); + window.NProgress && window.NProgress.inc(0.5 / total); + }; + if (img.complete) { img.onload(); } + } + }, + + registerRefreshCallback: function(callback) { + if (!Array.isArray(Fluid.events._refreshCallbacks)) { + Fluid.events._refreshCallbacks = []; + } + Fluid.events._refreshCallbacks.push(callback); + }, + + refresh: function() { + if (Array.isArray(Fluid.events._refreshCallbacks)) { + for (var callback of Fluid.events._refreshCallbacks) { + if (callback instanceof Function) { + callback(); + } + } + } + }, + + billboard: function() { + if (!('console' in window)) { + return; + } + // eslint-disable-next-line no-console + console.log(` +------------------------------------------------- +| | +| ________ __ _ __ | +| |_ __ |[ | (_) | ] | +| | |_ \\_| | | __ _ __ .--.| | | +| | _| | |[ | | | [ |/ /'\`\\' | | +| _| |_ | | | \\_/ |, | || \\__/ | | +| |_____| [___]'.__.'_/[___]'.__.;__] | +| | +| Powered by Hexo x Fluid | +| https://github.com/fluid-dev/hexo-theme-fluid | +| | +------------------------------------------------- + `); + } +}; diff --git a/js/img-lazyload.js b/js/img-lazyload.js new file mode 100644 index 0000000..c0c8e4e --- /dev/null +++ b/js/img-lazyload.js @@ -0,0 +1,10 @@ +/* global Fluid, CONFIG */ + +(function(window, document) { + for (const each of document.querySelectorAll('img[lazyload]')) { + Fluid.utils.waitElementVisible(each, function() { + each.removeAttribute('srcset'); + each.removeAttribute('lazyload'); + }, CONFIG.lazyload.offset_factor); + } +})(window, document); diff --git a/js/leancloud.js b/js/leancloud.js new file mode 100644 index 0000000..ab901ce --- /dev/null +++ b/js/leancloud.js @@ -0,0 +1,192 @@ +/* global CONFIG */ +// eslint-disable-next-line no-console + +(function(window, document) { + // 查询存储的记录 + function getRecord(Counter, target) { + return new Promise(function(resolve, reject) { + Counter('get', '/classes/Counter?where=' + encodeURIComponent(JSON.stringify({ target }))) + .then(resp => resp.json()) + .then(({ results, code, error }) => { + if (code === 401) { + throw error; + } + if (results && results.length > 0) { + var record = results[0]; + resolve(record); + } else { + Counter('post', '/classes/Counter', { target, time: 0 }) + .then(resp => resp.json()) + .then((record, error) => { + if (error) { + throw error; + } + resolve(record); + }).catch(error => { + console.error('Failed to create: ', error); + reject(error); + }); + } + }).catch((error) => { + console.error('LeanCloud Counter Error: ', error); + reject(error); + }); + }); + } + + // 发起自增请求 + function increment(Counter, incrArr) { + return new Promise(function(resolve, reject) { + Counter('post', '/batch', { + 'requests': incrArr + }).then((res) => { + res = res.json(); + if (res.error) { + throw res.error; + } + resolve(res); + }).catch((error) => { + console.error('Failed to save visitor count: ', error); + reject(error); + }); + }); + } + + // 构建自增请求体 + function buildIncrement(objectId) { + return { + 'method': 'PUT', + 'path' : `/1.1/classes/Counter/${objectId}`, + 'body' : { + 'time': { + '__op' : 'Increment', + 'amount': 1 + } + } + }; + } + + // 校验是否为有效的 Host + function validHost() { + if (CONFIG.web_analytics.leancloud.ignore_local) { + var hostname = window.location.hostname; + if (hostname === 'localhost' || hostname === '127.0.0.1') { + return false; + } + } + return true; + } + + // 校验是否为有效的 UV + function validUV() { + var key = 'LeanCloud_UV_Flag'; + var flag = localStorage.getItem(key); + if (flag) { + // 距离标记小于 24 小时则不计为 UV + if (new Date().getTime() - parseInt(flag, 10) <= 86400000) { + return false; + } + } + localStorage.setItem(key, new Date().getTime().toString()); + return true; + } + + function addCount(Counter) { + var enableIncr = CONFIG.web_analytics.enable && !Fluid.ctx.dnt && validHost(); + var getterArr = []; + var incrArr = []; + + // 请求 PV 并自增 + var pvCtn = document.querySelector('#leancloud-site-pv-container'); + if (pvCtn) { + var pvGetter = getRecord(Counter, 'site-pv').then((record) => { + enableIncr && incrArr.push(buildIncrement(record.objectId)); + var ele = document.querySelector('#leancloud-site-pv'); + if (ele) { + ele.innerText = (record.time || 0) + (enableIncr ? 1 : 0); + pvCtn.style.display = 'inline'; + } + }); + getterArr.push(pvGetter); + } + + // 请求 UV 并自增 + var uvCtn = document.querySelector('#leancloud-site-uv-container'); + if (uvCtn) { + var uvGetter = getRecord(Counter, 'site-uv').then((record) => { + var incrUV = validUV() && enableIncr; + incrUV && incrArr.push(buildIncrement(record.objectId)); + var ele = document.querySelector('#leancloud-site-uv'); + if (ele) { + ele.innerText = (record.time || 0) + (incrUV ? 1 : 0); + uvCtn.style.display = 'inline'; + } + }); + getterArr.push(uvGetter); + } + + // 如果有页面浏览数节点,则请求浏览数并自增 + var viewCtn = document.querySelector('#leancloud-page-views-container'); + if (viewCtn) { + var path = eval(CONFIG.web_analytics.leancloud.path || 'window.location.pathname'); + var target = decodeURI(path.replace(/\/*(index.html)?$/, '/')); + var viewGetter = getRecord(Counter, target).then((record) => { + enableIncr && incrArr.push(buildIncrement(record.objectId)); + var ele = document.querySelector('#leancloud-page-views'); + if (ele) { + ele.innerText = (record.time || 0) + (enableIncr ? 1 : 0); + viewCtn.style.display = 'inline'; + } + }); + getterArr.push(viewGetter); + } + + // 如果启动计数自增,批量发起自增请求 + if (enableIncr) { + Promise.all(getterArr).then(() => { + incrArr.length > 0 && increment(Counter, incrArr); + }); + } + } + + var appId = CONFIG.web_analytics.leancloud.app_id; + var appKey = CONFIG.web_analytics.leancloud.app_key; + var serverUrl = CONFIG.web_analytics.leancloud.server_url; + + if (!appId) { + throw new Error('LeanCloud appId is empty'); + } + if (!appKey) { + throw new Error('LeanCloud appKey is empty'); + } + + function fetchData(api_server) { + var Counter = (method, url, data) => { + return fetch(`${api_server}/1.1${url}`, { + method, + headers: { + 'X-LC-Id' : appId, + 'X-LC-Key' : appKey, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(data) + }); + }; + + addCount(Counter); + } + + var apiServer = serverUrl || `https://${appId.slice(0, 8).toLowerCase()}.api.lncldglobal.com`; + + if (apiServer) { + fetchData(apiServer); + } else { + fetch('https://app-router.leancloud.cn/2/route?appId=' + appId) + .then(resp => resp.json()) + .then((data) => { + if (data.api_server) { + fetchData('https://' + data.api_server); + } + }); + } +})(window, document); diff --git a/js/local-search.js b/js/local-search.js new file mode 100644 index 0000000..0784a80 --- /dev/null +++ b/js/local-search.js @@ -0,0 +1,159 @@ +/* global CONFIG */ + +(function() { + // Modified from [hexo-generator-search](https://github.com/wzpan/hexo-generator-search) + function localSearchFunc(path, searchSelector, resultSelector) { + 'use strict'; + // 0x00. environment initialization + var $input = jQuery(searchSelector); + var $result = jQuery(resultSelector); + + if ($input.length === 0) { + // eslint-disable-next-line no-console + throw Error('No element selected by the searchSelector'); + } + if ($result.length === 0) { + // eslint-disable-next-line no-console + throw Error('No element selected by the resultSelector'); + } + + if ($result.attr('class').indexOf('list-group-item') === -1) { + $result.html('
Loading...

Loading...
'); + } + + jQuery.ajax({ + // 0x01. load xml file + url : path, + dataType: 'xml', + success : function(xmlResponse) { + // 0x02. parse xml file + var dataList = jQuery('entry', xmlResponse).map(function() { + return { + title : jQuery('title', this).text(), + content: jQuery('content', this).text(), + url : jQuery('url', this).text() + }; + }).get(); + + if ($result.html().indexOf('list-group-item') === -1) { + $result.html(''); + } + + $input.on('input', function() { + // 0x03. parse query to keywords list + var content = $input.val(); + var resultHTML = ''; + var keywords = content.trim().toLowerCase().split(/[\s-]+/); + $result.html(''); + if (content.trim().length <= 0) { + return $input.removeClass('invalid').removeClass('valid'); + } + // 0x04. perform local searching + dataList.forEach(function(data) { + var isMatch = true; + if (!data.title || data.title.trim() === '') { + data.title = 'Untitled'; + } + var orig_data_title = data.title.trim(); + var data_title = orig_data_title.toLowerCase(); + var orig_data_content = data.content.trim().replace(/<[^>]+>/g, ''); + var data_content = orig_data_content.toLowerCase(); + var data_url = data.url; + var index_title = -1; + var index_content = -1; + var first_occur = -1; + // Skip matching when content is included in search and content is empty + if (CONFIG.include_content_in_search && data_content === '') { + isMatch = false; + } else { + keywords.forEach(function (keyword, i) { + index_title = data_title.indexOf(keyword); + index_content = data_content.indexOf(keyword); + + if (index_title < 0 && index_content < 0) { + isMatch = false; + } else { + if (index_content < 0) { + index_content = 0; + } + if (i === 0) { + first_occur = index_content; + } + } + }); + } + // 0x05. show search results + if (isMatch) { + resultHTML += '' + orig_data_title + ''; + var content = orig_data_content; + if (first_occur >= 0) { + // cut out 100 characters + var start = first_occur - 20; + var end = first_occur + 80; + + if (start < 0) { + start = 0; + } + + if (start === 0) { + end = 100; + } + + if (end > content.length) { + end = content.length; + } + + var match_content = content.substring(start, end); + + // highlight all keywords + keywords.forEach(function(keyword) { + var regS = new RegExp(keyword, 'gi'); + match_content = match_content.replace(regS, '' + keyword + ''); + }); + + resultHTML += '

' + match_content + '...

'; + } + } + }); + if (resultHTML.indexOf('list-group-item') === -1) { + return $input.addClass('invalid').removeClass('valid'); + } + $input.addClass('valid').removeClass('invalid'); + $result.html(resultHTML); + }); + } + }); + } + + function localSearchReset(searchSelector, resultSelector) { + 'use strict'; + var $input = jQuery(searchSelector); + var $result = jQuery(resultSelector); + + if ($input.length === 0) { + // eslint-disable-next-line no-console + throw Error('No element selected by the searchSelector'); + } + if ($result.length === 0) { + // eslint-disable-next-line no-console + throw Error('No element selected by the resultSelector'); + } + + $input.val('').removeClass('invalid').removeClass('valid'); + $result.html(''); + } + + var modal = jQuery('#modalSearch'); + var searchSelector = '#local-search-input'; + var resultSelector = '#local-search-result'; + modal.on('show.bs.modal', function() { + var path = CONFIG.search_path || '/local-search.xml'; + localSearchFunc(path, searchSelector, resultSelector); + }); + modal.on('shown.bs.modal', function() { + jQuery('#local-search-input').focus(); + }); + modal.on('hidden.bs.modal', function() { + localSearchReset(searchSelector, resultSelector); + }); +})(); diff --git a/js/plugins.js b/js/plugins.js new file mode 100644 index 0000000..2a364b0 --- /dev/null +++ b/js/plugins.js @@ -0,0 +1,164 @@ +/* global Fluid, CONFIG */ + +HTMLElement.prototype.wrap = function(wrapper) { + this.parentNode.insertBefore(wrapper, this); + this.parentNode.removeChild(this); + wrapper.appendChild(this); +}; + +Fluid.plugins = { + + typing: function(text) { + if (!('Typed' in window)) { return; } + + var typed = new window.Typed('#subtitle', { + strings: [ + ' ', + text + ], + cursorChar: CONFIG.typing.cursorChar, + typeSpeed : CONFIG.typing.typeSpeed, + loop : CONFIG.typing.loop + }); + typed.stop(); + var subtitle = document.getElementById('subtitle'); + if (subtitle) { + subtitle.innerText = ''; + } + jQuery(document).ready(function() { + typed.start(); + }); + }, + + fancyBox: function(selector) { + if (!CONFIG.image_zoom.enable || !('fancybox' in jQuery)) { return; } + + jQuery(selector || '.markdown-body :not(a) > img, .markdown-body > img').each(function() { + var $image = jQuery(this); + var imageUrl = $image.attr('data-src') || $image.attr('src') || ''; + if (CONFIG.image_zoom.img_url_replace) { + var rep = CONFIG.image_zoom.img_url_replace; + var r1 = rep[0] || ''; + var r2 = rep[1] || ''; + if (r1) { + if (/^re:/.test(r1)) { + r1 = r1.replace(/^re:/, ''); + var reg = new RegExp(r1, 'gi'); + imageUrl = imageUrl.replace(reg, r2); + } else { + imageUrl = imageUrl.replace(r1, r2); + } + } + } + var $imageWrap = $image.wrap(` + ` + ).parent('a'); + if ($imageWrap.length !== 0) { + if ($image.is('.group-image-container img')) { + $imageWrap.attr('data-fancybox', 'group').attr('rel', 'group'); + } else { + $imageWrap.attr('data-fancybox', 'default').attr('rel', 'default'); + } + + var imageTitle = $image.attr('title') || $image.attr('alt'); + if (imageTitle) { + $imageWrap.attr('title', imageTitle).attr('data-caption', imageTitle); + } + } + }); + + jQuery.fancybox.defaults.hash = false; + jQuery('.fancybox').fancybox({ + loop : true, + helpers: { + overlay: { + locked: false + } + } + }); + }, + + imageCaption: function(selector) { + if (!CONFIG.image_caption.enable) { return; } + + jQuery(selector || `.markdown-body > p > img, .markdown-body > figure > img, + .markdown-body > p > a.fancybox, .markdown-body > figure > a.fancybox`).each(function() { + var $target = jQuery(this); + var $figcaption = $target.next('figcaption'); + if ($figcaption.length !== 0) { + $figcaption.addClass('image-caption'); + } else { + var imageTitle = $target.attr('title') || $target.attr('alt'); + if (imageTitle) { + $target.after(``); + } + } + }); + }, + + codeWidget() { + var enableLang = CONFIG.code_language.enable && CONFIG.code_language.default; + var enableCopy = CONFIG.copy_btn && 'ClipboardJS' in window; + if (!enableLang && !enableCopy) { + return; + } + + function getBgClass(ele) { + return Fluid.utils.getBackgroundLightness(ele) >= 0 ? 'code-widget-light' : 'code-widget-dark'; + } + + var copyTmpl = ''; + copyTmpl += '
'; + copyTmpl += 'LANG'; + copyTmpl += '
'; + jQuery('.markdown-body pre').each(function() { + var $pre = jQuery(this); + if ($pre.find('code.mermaid').length > 0) { + return; + } + if ($pre.find('span.line').length > 0) { + return; + } + + var lang = ''; + + if (enableLang) { + lang = CONFIG.code_language.default; + if ($pre[0].children.length > 0 && $pre[0].children[0].classList.length >= 2 && $pre.children().hasClass('hljs')) { + lang = $pre[0].children[0].classList[1]; + } else if ($pre[0].getAttribute('data-language')) { + lang = $pre[0].getAttribute('data-language'); + } else if ($pre.parent().hasClass('sourceCode') && $pre[0].children.length > 0 && $pre[0].children[0].classList.length >= 2) { + lang = $pre[0].children[0].classList[1]; + $pre.parent().addClass('code-wrapper'); + } else if ($pre.parent().hasClass('markdown-body') && $pre[0].classList.length === 0) { + $pre.wrap('
'); + } + lang = lang.toUpperCase().replace('NONE', CONFIG.code_language.default); + } + $pre.append(copyTmpl.replace('LANG', lang).replace('code-widget">', + getBgClass($pre[0]) + (enableCopy ? ' code-widget copy-btn" data-clipboard-snippet>' : ' code-widget">'))); + + if (enableCopy) { + var clipboard = new ClipboardJS('.copy-btn', { + target: function(trigger) { + var nodes = trigger.parentNode.childNodes; + for (var i = 0; i < nodes.length; i++) { + if (nodes[i].tagName === 'CODE') { + return nodes[i]; + } + } + } + }); + clipboard.on('success', function(e) { + e.clearSelection(); + e.trigger.innerHTML = e.trigger.innerHTML.replace('icon-copy', 'icon-success'); + setTimeout(function() { + e.trigger.innerHTML = e.trigger.innerHTML.replace('icon-success', 'icon-copy'); + }, 2000); + }); + } + }); + } +}; diff --git a/js/utils.js b/js/utils.js new file mode 100644 index 0000000..d61bc26 --- /dev/null +++ b/js/utils.js @@ -0,0 +1,245 @@ +/* global Fluid, CONFIG */ + +window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame; + +Fluid.utils = { + + listenScroll: function(callback) { + var dbc = new Debouncer(callback); + window.addEventListener('scroll', dbc, false); + dbc.handleEvent(); + return dbc; + }, + + unlistenScroll: function(callback) { + window.removeEventListener('scroll', callback); + }, + + listenDOMLoaded(callback) { + if (document.readyState !== 'loading') { + callback(); + } else { + document.addEventListener('DOMContentLoaded', function () { + callback(); + }); + } + }, + + scrollToElement: function(target, offset) { + var of = jQuery(target).offset(); + if (of) { + jQuery('html,body').animate({ + scrollTop: of.top + (offset || 0), + easing : 'swing' + }); + } + }, + + elementVisible: function(element, offsetFactor) { + offsetFactor = offsetFactor && offsetFactor >= 0 ? offsetFactor : 0; + var rect = element.getBoundingClientRect(); + const viewportHeight = window.innerHeight || document.documentElement.clientHeight; + return ( + (rect.top >= 0 && rect.top <= viewportHeight * (1 + offsetFactor) + rect.height / 2) || + (rect.bottom >= 0 && rect.bottom <= viewportHeight * (1 + offsetFactor) + rect.height / 2) + ); + }, + + waitElementVisible: function(selectorOrElement, callback, offsetFactor) { + var runningOnBrowser = typeof window !== 'undefined'; + var isBot = (runningOnBrowser && !('onscroll' in window)) + || (typeof navigator !== 'undefined' && /(gle|ing|ro|msn)bot|crawl|spider|yand|duckgo/i.test(navigator.userAgent)); + if (!runningOnBrowser || isBot) { + return; + } + + offsetFactor = offsetFactor && offsetFactor >= 0 ? offsetFactor : 0; + + function waitInViewport(element) { + Fluid.utils.listenDOMLoaded(function() { + if (Fluid.utils.elementVisible(element, offsetFactor)) { + callback(); + return; + } + if ('IntersectionObserver' in window) { + var io = new IntersectionObserver(function(entries, ob) { + if (entries[0].isIntersecting) { + callback(); + ob.disconnect(); + } + }, { + threshold : [0], + rootMargin: (window.innerHeight || document.documentElement.clientHeight) * offsetFactor + 'px' + }); + io.observe(element); + } else { + var wrapper = Fluid.utils.listenScroll(function() { + if (Fluid.utils.elementVisible(element, offsetFactor)) { + Fluid.utils.unlistenScroll(wrapper); + callback(); + } + }); + } + }); + } + + if (typeof selectorOrElement === 'string') { + this.waitElementLoaded(selectorOrElement, function(element) { + waitInViewport(element); + }); + } else { + waitInViewport(selectorOrElement); + } + }, + + waitElementLoaded: function(selector, callback) { + var runningOnBrowser = typeof window !== 'undefined'; + var isBot = (runningOnBrowser && !('onscroll' in window)) + || (typeof navigator !== 'undefined' && /(gle|ing|ro|msn)bot|crawl|spider|yand|duckgo/i.test(navigator.userAgent)); + if (!runningOnBrowser || isBot) { + return; + } + + if ('MutationObserver' in window) { + var mo = new MutationObserver(function(records, ob) { + var ele = document.querySelector(selector); + if (ele) { + callback(ele); + ob.disconnect(); + } + }); + mo.observe(document, { childList: true, subtree: true }); + } else { + Fluid.utils.listenDOMLoaded(function() { + var waitLoop = function() { + var ele = document.querySelector(selector); + if (ele) { + callback(ele); + } else { + setTimeout(waitLoop, 100); + } + }; + waitLoop(); + }); + } + }, + + createScript: function(url, onload) { + var s = document.createElement('script'); + s.setAttribute('src', url); + s.setAttribute('type', 'text/javascript'); + s.setAttribute('charset', 'UTF-8'); + s.async = false; + if (typeof onload === 'function') { + if (window.attachEvent) { + s.onreadystatechange = function() { + var e = s.readyState; + if (e === 'loaded' || e === 'complete') { + s.onreadystatechange = null; + onload(); + } + }; + } else { + s.onload = onload; + } + } + var ss = document.getElementsByTagName('script'); + var e = ss.length > 0 ? ss[ss.length - 1] : document.head || document.documentElement; + e.parentNode.insertBefore(s, e.nextSibling); + }, + + createCssLink: function(url) { + var l = document.createElement('link'); + l.setAttribute('rel', 'stylesheet'); + l.setAttribute('type', 'text/css'); + l.setAttribute('href', url); + var e = document.getElementsByTagName('link')[0] + || document.getElementsByTagName('head')[0] + || document.head || document.documentElement; + e.parentNode.insertBefore(l, e); + }, + + loadComments: function(selector, loadFunc) { + var ele = document.querySelector('#comments[lazyload]'); + if (ele) { + var callback = function() { + loadFunc(); + ele.removeAttribute('lazyload'); + }; + Fluid.utils.waitElementVisible(selector, callback, CONFIG.lazyload.offset_factor); + } else { + loadFunc(); + } + }, + + getBackgroundLightness(selectorOrElement) { + var ele = selectorOrElement; + if (typeof selectorOrElement === 'string') { + ele = document.querySelector(selectorOrElement); + } + var view = ele.ownerDocument.defaultView; + if (!view) { + view = window; + } + var rgbArr = view.getComputedStyle(ele).backgroundColor.replace(/rgba*\(/, '').replace(')', '').split(/,\s*/); + if (rgbArr.length < 3) { + return 0; + } + var colorCast = (0.213 * rgbArr[0]) + (0.715 * rgbArr[1]) + (0.072 * rgbArr[2]); + return colorCast === 0 || colorCast > 255 / 2 ? 1 : -1; + }, + + retry(handler, interval, times) { + if (times <= 0) { + return; + } + var next = function() { + if (--times >= 0 && !handler()) { + setTimeout(next, interval); + } + }; + setTimeout(next, interval); + } + +}; + +/** + * Handles debouncing of events via requestAnimationFrame + * @see http://www.html5rocks.com/en/tutorials/speed/animations/ + * @param {Function} callback The callback to handle whichever event + */ +function Debouncer(callback) { + this.callback = callback; + this.ticking = false; +} + +Debouncer.prototype = { + constructor: Debouncer, + + /** + * dispatches the event to the supplied callback + * @private + */ + update: function() { + this.callback && this.callback(); + this.ticking = false; + }, + + /** + * ensures events don't get stacked + * @private + */ + requestTick: function() { + if (!this.ticking) { + requestAnimationFrame(this.rafCallback || (this.rafCallback = this.update.bind(this))); + this.ticking = true; + } + }, + + /** + * Attach this as the event listeners + */ + handleEvent: function() { + this.requestTick(); + } +}; diff --git a/links/index.html b/links/index.html new file mode 100644 index 0000000..7edc1cf --- /dev/null +++ b/links/index.html @@ -0,0 +1,394 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Links - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/local-search.xml b/local-search.xml new file mode 100644 index 0000000..8d99a14 --- /dev/null +++ b/local-search.xml @@ -0,0 +1,255 @@ + + + + + + + test + + /2024/07/12/test/ + + + + + + + + + + + 浏览器插件入门 + + /2024/06/02/%E6%B5%8F%E8%A7%88%E5%99%A8%E6%8F%92%E4%BB%B6%E5%85%A5%E9%97%A8/ + + 浏览器插件

开源之夏有点被导师pua到了 所以准备换了个项目 还是一个社区的项目

这个项目主要是对浏览器插件优化

浏览器插件基本项目结构

先看看基本结构吧 基本上都会有一个mainfest.jsonpackage.json基本上我感觉是一样的作用

1
2
3
4
5
6
7
8
9
10
11
{
// 插件名称
"name": "Hello Extensions",
// 插件的描述
"description" : "Base Level Extension",
// 插件的版本
"version": "1.0",
// 配置插件程序的版本号,主流版本是2,最新是3
"manifest_version": 2
}

我们经常会点击右上角插件图标时弹出一个小窗口的页面,焦点离开时就关闭了,一般做一些临时性的交互操作;在配置文件中新增browser_action字段,配置popup弹框:

1
2
3
4
5
6
7
8
9
10
11
{
"name": "Hello Extensions",
"description" : "Base Level Extension",
"version": "1.0",
"manifest_version": 2,
// 新增popup弹框
"browser_action": {
"default_popup": "popup.html",
"default_icon": "popup.png"
}
}

然后创建我们的弹框页面panel.html:

1
2
3
4
5
html复制代码<html>
<body>
<h1>Hello Extensions</h1>
</body>
</html>

点击图标后,插件显示panel.html

后台background

们也发现了,popup页面只能做临时性的交互操作,用完就关了,不能存储信息或者和其他标签页进行交互等等;这时就需要用到background(后台),它是一个常驻的页面,它的生命周期是插件中所有类型页面中最长的;它随着浏览器的打开而打开,随着浏览器的关闭而关闭,所以通常把需要一直运行的、启动就运行的、全局的代码放在background里面。

mainfest.json添加路径

1
2
3
4
5
6
"background": {
"script": [
"background.js"
],
"persistent": true
},

这样基本上结构就差不多了

实现查看组件属性和查看状态管理状态的功能

这个就是开源之夏的第一个任务喽

感觉就是React的那个插件的功能

我去看了看react的源码

感觉操作就是需要先获取到组件的状态 然后看状态管理器的状态

然后监听后台管理器的消息 然后在显示的html中加上显示的部分

在content-Script中新增⼀个stateManager.ts⽂件 以便捕获和发送组件状态和状态管理器信息

哦这里补充一下 一般会有一个content-script文件夹

官方定义是

content-scripts(内容脚本)是在网页上下文中运行的文件。通过使用标准的文档对象模型(DOM),它能够读取浏览器访问的网页的详细信息,对其进行更改,并将信息传递给其父级插件。内容脚本相对于background还是有一些访问API上的限制,它可以直接访问以下chrome的API:

  • i18n
  • storage
  • runtime:
    • connect
    • getManifest
    • getURL
    • id
    • onConnect
    • onMessage
    • sendMessage

我的理解就是监听监听监听 传信号信号信号 💬

基本上用的都是chrome的api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let mockComponentState = { exampleComponent: { state: 'active' } };
let mockStateManagerInfo = { exampleStateManager: { state: 'initialized' } };
chrome.runtime.sendMessage({ type: 'updateComponentState', state:
mockComponentState });
chrome.runtime.sendMessage({ type: 'updateStateManagerInfo', stateManager:
mockStateManagerInfo });
//钩子到框架特定的 API 获取真实的组件状态和状态管理器信息
(function () {
const originalSetState = YourFramework.Component.prototype.setState;
YourFramework.Component.prototype.setState = function (newState, callback) {
chrome.runtime.sendMessage({ type: 'updateComponentState', state:
this.state });
return originalSetState.apply(this, arguments);
};
})();
(function () {
const originalDispatch = YourFramework.store.dispatch;
YourFramework.store.dispatch = function (action) {
chrome.runtime.sendMessage({ type: 'updateStateManagerInfo', stateManager:
YourFramework.store.getState() });
return originalDispatch.apply(this, arguments);
};
})();

chrome.runtime.sendMessage 是 Chrome 扩展程序中的一种方法,用于从扩展程序中的一个部分向另一个部分发送消息,例如从内容脚本向后台脚本发送消息。

用法

1
chrome.runtime.sendMessage(extensionId, message, options, responseCallback);
  • extensionId(可选):字符串类型。指定接收消息的扩展程序的ID。如果消息是发送到本扩展程序,可以省略此参数。
  • message:任意类型。发送的消息内容,可以是任何JSON序列化类型的数据。
  • options(可选):对象类型。包含额外的选项,比如 includeTlsChannelId(布尔值)。
  • responseCallback(可选):函数类型。一个函数,当收到响应消息时调用,传入一个参数 response,该参数是响应的消息。

然后在content-Script的index.ts中引入使用

1
2
3
4
5
6
7
8
9
10
11
 //监听来自 background 的消息
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
//该方法可以监听页面 contentScript 和插件的消息
// 没有 tab 信息说明消息来自插件
if (!sender.tab checkMessage(message, DevToolBackground)) {
changeSource(message, DevToolContentScript);
// 传递消息给页面
window.postMessage(message, '*');
}
sendResponse({ status: 'ok' });
});

他自己写的源码中有个什么window对象不能直接通过 contentScript 代码修改,只能通过添加 js 代码往页面 window 注入 hook 这玩意还没看懂 等要是选上慢慢来吧

]]>
+ + + + + 浏览器插件 + + + + +
+ + + + + blog + + /2024/05/12/blog/ + + + + + + + + + + + Typescript-declare + + /2024/05/12/Typescript-declare/ + + declare 关键字

最近在做那个开源之夏 然后要用到这个declare 进行类型检查就正好写一篇啦 (其实还有很多不会的)👼

本来想写JSX的自定义接口来着 但是没准备看懂 所以写了这个

简介

declare 关键字用来告诉编译器,某个类型是存在的,可以在当前文件中使用🛩️

它的主要作用,就是让当前文件可以使用其他文件声明的类型。举例来说,自己的脚本使用外部库定义的函数,编译器会因为不知道外部函数的类型定义而报错,这时就可以在自己的脚本里面使用declare关键字,告诉编译器外部函数的类型。这样的话,编译单个脚本就不会因为使用了外部类型而报错。

declare 关键字可以描述以下类型。

  • 变量(const、let、var 命令声明)
  • type 或者 interface 命令声明的类型
  • class
  • enum
  • 函数(function)
  • 模块(module)
  • 命名空间(namespace)

declare 关键字的重要特点是,它只是通知编译器某个类型是存在的,不用给出具体实现。比如,只描述函数的类型,不给出函数的实现,如果不使用declare,这是做不到的。

declare 只能用来描述已经存在的变量和数据结构,不能用来声明新的变量和数据结构。另外,所有 declare 语句都不会出现在编译后的文件里面。

declare variable

declare 关键字可以给出外部变量的类型描述。

举例来说,当前脚本使用了其他脚本定义的全局变量x

1
x = 123; // 报错

上面示例中,变量x是其他脚本定义的,当前脚本不知道它的类型,编译器就会报错。

这时使用 declare 命令给出它的类型,就不会报错了。

1
declare let x:number;x = 1;

如果 declare 关键字没有给出变量的具体类型,那么变量类型就是any

1
declare let x;x = 1;

上面示例中,变量x的类型为any

下面的例子是脚本使用浏览器全局对象document

1
declare var document;document.title = 'Hello';

上面示例中,declare 告诉编译器,变量document的类型是外部定义的(具体定义在 TypeScript 内置文件lib.d.ts)。

如果 TypeScript 没有找到document的外部定义,这里就会假定它的类型是any

注意,declare 关键字只用来给出类型描述,是纯的类型代码,不允许设置变量的初始值,即不能涉及值。

1
2
// 报错 
declare let x:number = 1;

上面示例中,declare 设置了变量的初始值,结果就报错了。

declare function

declare 关键字可以给出外部函数的类型描述。

1
2
declare function sayHello(name:string):void;
sayHello('zzx');

上面示例中,declare 命令给出了sayHello()的类型描述,因此可以直接使用它。

注意,这种单独的函数类型声明语句,只能用于declare命令后面。一方面,TypeScript 不支持单独的函数类型声明语句;另一方面,declare 关键字后面也不能带有函数的具体实现。

1
2
// 报错 
function sayHello( name:string):void;function sayHello(name) { return '你好,' + name;}

上面示例中,单独写函数的类型声明就会报错。

declare class

declare 给出 class 的描述描述写法如下。

1
declare class Animal {  constructor(name:string);  eat():void;  sleep():void;}

下面是一个复杂一点的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
declare class C {  
// 静态成员
public static s0():string;
private static s1:string;
// 属性
public a:number;
private b:number;
// 构造函数
constructor(arg:number);
// 方法
m(x:number, y:number):number;
// 存取器
get c():number; set c(value:number);
// 索引签名
[index:string]:any;
}

同样的,declare 后面不能给出 Class 的具体实现或初始值。

declare module,declare namespace

如果想把变量、函数、类组织在一起,可以将 declare 与 module 或 namespace 一起使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
declare namespace AnimalLib {  
class Animal {
constructor(name:string);
eat():void;
sleep():void; }
type Animals = 'Fish' | 'Dog';}
// 或者
declare module AnimalLib
{ class Animal
{ constructor(name:string);
eat(): void;
sleep(): void;
}
type Animals = 'Fish' | 'Dog';}

这个栗子中 declare 关键字给出了 module 或 namespace 的类型描述。

declare module 和 declare namespace 里面,加不加 export 关键字都可以。

1
declare namespace Foo {  export var a: boolean;}declare module 'io' { export function readFile(filename:string):string;}

上面示例中,namespace 和 module 里面使用了 export 关键字。

declare 关键字的另一个用途,是为外部模块添加属性和方法时,给出新增部分的类型描述。

1
import { Foo as Bar } from 'moduleA';declare module 'moduleA' {  interface Bar extends Foo {    custom: {      prop1:string;    }  }}

上面示例中,从模块moduleA导入了Foo接口,将其重命名为Bar,并用 declare 关键字为Bar增加一个属性custom

下面是另一个例子。一个项目有多个模块,可以在一个模型中,对另一个模块的接口进行类型扩展。

1
2
3
4
5
6
7
// a.ts
export interface A { x: number;}
// b.ts
import { A } from './a';
declare module './a' {
interface A { y: number; }}
const a:A = { x: 0, y: 0 };

上面示例中,脚本a.ts定义了一个接口A,脚本b.ts为这个接口添加了属性ydeclare module './a' {}表示对a.ts里面的模块,进行类型声明,而同名 interface 会自动合并,所以等同于扩展类型。

使用这种语法进行模块的类型扩展时,有两点需要注意:

(1)declare module NAME语法里面的模块名NAME,跟 import 和 export 的模块名规则是一样的,且必须跟当前文件加载该模块的语句写法(上例import { A } from './a')保持一致。

(2)不能创建新的顶层类型。也就是说,只能对a.ts模块中已经存在的类型进行扩展,不允许增加新的顶层类型,比如新定义一个接口B

(3)不能对默认的default接口进行扩展,只能对 export 命令输出的命名接口进行扩充。这是因为在进行类型扩展时,需要依赖输出的接口名。

某些第三方模块,原始作者没有提供接口类型,这时可以在自己的脚本顶部加上下面一行命令。

1
2
declare module "模块名";
// 例子declare module "hot-new-module";

加上上面的命令以后,外部模块即使没有类型,也可以通过编译。但是,从该模块输入的所有接口都将为any类型。

declare module 描述的模块名可以使用通配符。

1
2
3
4
5
6
7
8
declare module 'my-plugin-*' {  
interface PluginOptions {
enabled: boolean;
priority: number;
}
function initialize(options: PluginOptions): void;
export = initialize;
}

上面示例中,模块名my-plugin-*表示适配所有以my-plugin-开头的模块名(比如my-plugin-logger)。

]]>
+ + + +
+ + + + + Javascript忍者秘籍blog-1 + + /2024/04/21/Javascript%E5%BF%8D%E8%80%85%E7%A7%98%E7%B1%8Dblog-1/ + + Javascript忍者秘籍(一)

最近一直都在看这本书 感觉之前的js底子太差了 看这本书回顾一下吧(感觉有些概念都是没听过的)🌵

这篇主要写第四章理解函数调用的东西吧

调用函数时,隐式的函数参数 this 和 arguments 会被静默的传递给函数,可以像函数体内显式声明的参数一样使用

this 表示调用函数的上下文对象

arguments 表示函数调用过程中传递的所有参数。通过 arguments 参数可以访问 函数调用过程中传递的实际参数。

函数调用的方式 对 函数的隐式参数有很大的影响🍶

1.隐式函数参数

arguments参数

arguments参数表示传入函数的所有参数的集合。

使用arguments.length属性来获取传递给函数的实际参数个数。

通过数组下标的方式访问到arguments参数中的每个参数值,如arguments[2]将获取第三个参数。

arguments是一个类数组对象,不可以对arguments对象使用数组的方法。

在非严格模式下,arguments对象是函数参数的别名,修改arguments对象会影响对应的函数实参,反之亦然。

arguments作为参数别名使用时,会影响代码可读性,应该避免使用参数别名修改参数。在严格模式下通过arguments修改参数是不起作用的

this参数

this表示函数上下文,即与函数调用相关联的对象。(还是有很多抽象小题的 经典this指向问题)💢

但是,在javascript中,将一个函数作为方法调用仅仅是函数的一种调用方式。this参数是由函数的定义方式调用方式决定

2.函数调用方式

基本上是4种

  1. 作为函数直接被调用;myfunc()
  2. 作为方法关联在一个对象上,实现面向对象编程;obj.myfunc()
  3. 作为构造函数调用,实例化一个对象;new Myfunc()
  4. 通过函数的apply和call方法

作为函数被直接调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*函数定义作为函数被调用*/
function aa(){
console.log(this)
}
aa(); //=>object.window
/*函数表达式作为函数被调用*/
let bb = function(){
console.log(this)
}
bb(); //=>object.window
/*立即调用函数表达式作为函数被调用*/
(function(){console.log(this)})();
//=>object.window

作为对象方法被调用

当一个函数被赋值一个对象的属性,并且通过对象属性引用的方式调用函数时,函数会作为对象的方法被调用。 作为对象方法调用的函数this值与对象关联,通过this可以访问所关联对象的其他方法和属性

1
2
3
4
5
6
7
8
9
10
11
function aa(){return this}
console.log(aa()==window);
//=>true
var obj1 = {}
obj1.aa = aa;
console.log(obj1.aa()==obj1);
//=>true
var obj2 = {}
obj2.bb = aa;
console.log(obj2.bb()==obj2);
//=>true

作为构造函数调用

在函数调用之前加上关键字new,即为构造函数调用。

构造函数目的是用来创建和初始化一个新对象,然后将这个对象作为构造函数的返回值

使用关键字new调用函数会触发以下几个动作:🍔

  1. 创建一个新的空对象;
  2. 该对象作为this参数传递给构造函数,成为构造函数的上下文;
  3. 新构造的对象作为new运算符的返回值。
  4. 如果构造函数返回一个对象,则该对象将作为整个表达式的返回值,而传入构造函数的this将被丢弃。
  5. 如果构建函数返回的是非对象类型,则忽略返回值,返回新创建的对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Ninja(){
//这里的this表示Ninja函数的上下文
this.skulk = function(){
//这里的this表示该匿名函数的上下文
return this;
}
}
//skulk以对象的方式调用是,返回值是其关联的对象
var ninja1 = new Ninja();
var ninja2 = new Ninja();
console.log(ninja1.skulk() == ninja1);
console.log(ninja2.skulk() == ninja2);
// skulk复制给一个变量后,直接调用函数时,非严格模式下skulk返回的值是window
var skulk = ninja2.skulk;
console.log(skulk() == window);

通过apply与call方法调用

javascript提供了可以显示指定任何对象作为函数的上下文的函数调用方式。每个函数都存在apply和call方法。通过apply与call方法来设置函数的上下文。

call函数
1
function.call(context, arg1, arg2, ...)

context 是指定函数中的 this 关键字指向的对象,arg1, arg2, … 是传递给函数的参数

apply函数
1
function.apply(context, [argsArray])

context 是指定函数中的 this 关键字指向的对象,argsArray 是一个数组,其中包含要传递给函数的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function juggle(){
var result = 0;
for(var n=0; n<arguments.length; n++){
result+=arguments[n]
}
this.result = result;
}

var ninja1 = {};
var ninja2 = {};

juggle.apply(ninja1, [1,2,3,4,5])
juggle.call(ninja2, 1,2,3,4,5, 6)

console.log(ninja1.result == 15)
console.log(ninja2.result == 21)

apply和call功能类似,唯一的不同在于如何传递参数。apply和call第一个参数作为函数的上下文,apply第二个参数是一个包含参数值的数组。call可以传入任意数量参数,作为函数的参数。

总结!!!

总结四种函数的调用方式对this取值的影响
  • 如果作为函数调用,在非严格模式下,this指向全局window对象;在严格模式下,this指向undefined。
  • 作为方法调用,this通常指向调用的对象
  • 作为构造函数调用,this指向新创建的对象。
  • 通过call或apply调用,this指向call或apply的第一个参数。

3.this指向问题! (天天被拷打版)

在全局作用域

this->window

在普通函数中

谁调用我 this指向谁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj = {
fn1:function() {
console.log(this);
},
fn2:function(){
fn3()
}
}
function fn3() {
console.log(this);
}
fn3();//this->window
obj.fn1();//this->obj
obj.fn2();//this->window

箭头函数的this

箭头函数没有自己的this,箭头函数的this就是上下文中定义的this,因为箭头函数没有自己的this所以不能用做构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var div = document.querySelector('div'); 
var o={
a:function(){
var arr=[1];
//就是定义所在对象中的this
//这里的this—>o
arr.forEach(item=>{
//所以this -> o
console.log(this);
})
},
//这里的this指向window o是定义在window中的对象
b:()=>{
console.log(this);
},
c:function() {
console.log(this);
}
}
div.addEventListener('click',item=>{
console.log(this);//this->window 这里的this就是定义上文window环境中的this
});
o.a(); //this->o
o.b();//this->window
o.c();//this->o 普通函数谁调用就指向谁

事件绑定中的this

基本上都是指向this->事件源的😆

1
2
3
4
5
6
7
8
9
var div = document.querySelector('div'); 
div.addEventListener('click',function() {
console.log(this); //this->div
});

div.onclick = function() {
console.log(this) //this->div
}

定时器的this

定时器中采用回调函数作为处理函数 回调函数的this->window

1
2
3
4
5
6
7
8
  setInterval(function() {
console.log(this); //this->window
},500)

setTimeout(function() {
console.log(this); //this->window
},500)
442

构造函数的this

构造函数配合new使用, 而new关键字会将构造函数中的this指向实例化对象,所以构造函数中的this->实例化对象

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name,age) {
this.name = name;
this.age = age;
}
var person1 = new Person();
person1.name = 'ggb';
person1.age = 21;
console.log(person1);//Person {name: "ggb", age: 21}
var person2 = new Person();
person2.name = 'syj';
person2.age = 19;
console.log(person2);// Person {name: "syj", age: 19}

]]>
+ + + + + javascript + + + + +
+ + + + + Git宝典 + + /2024/04/11/Git%E5%AE%9D%E5%85%B8/ + + Git宝典

其实这次mini对我自身来说 学到了很多 技术这里就先不说了 终于意识到了git在多人协作写一个大项目的重要性 😆

啥是Git

我对Git这个东西的理解就是 在进行大型项目的开发时候 你不可能单兵作战 一般是有好多好多人一起的 这个时候怎么进行每个人之间的汇总勒

这个时候 就要用到Git

说点官方的话术就是😒

Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理项目

Git是怎么工作的呢❓

如图

img

具体都是什么意思勒

  • Workspace:工作区 (就是你电脑能看到的)
  • Index / Stage:暂存区 (一般是在.git目录下的index文件下)
  • Repository:仓库区(或本地仓库)
  • Remote:远程仓库

Git咋使用呢

1.第一步肯定是新建Git库喽 🆕

有两种方法

1️⃣直接新建!

1
git init

2️⃣克隆下来一个已经存在的库

1
git clone "url"

2.基本操作

该说不说这玩意真多啊😡

git add添加文件到暂存区
git status查看仓库当前的状态 显示有变更的文件
git commit提交暂存区到本地仓库
git mv移动或重命名工作区文件
git log查看历史提交记录
git blame以列表形式查看指定文件的历史修改记录
git remote远程仓库操作
git fetch从远程获取代码库
git pull下载远程代码并合并
git push上传远程代码并合并 添加文件到本地暂存区
  • 添加文件到暂存区

    1
    2
    git add 文件名 //这样是添加某个问价
    git add . //添加该目录下所有未被忽略的文件

    Tips:一般都忽略什么呢?🤔

    一般都会忽略你的node_modules文件夹 因为依赖太多多多多了

  • 提交更改到本地仓库

    1
    git commit -m "说明"

    这个说明也是有规范滴

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    feat - 新功能 feature
    fix - 修复 bug
    docs - 文档注释
    style - 代码格式(不影响代码运行的变动)
    refactor - 重构、优化(既不增加新功能,也不是修复bug)
    perf - 性能优化
    test - 增加测试
    chore - 构建过程或辅助工具的变动
    revert - 回退
    build - 打包

    就像

    git commit规范.png

这样写commit的话 就能非常清楚的看到你每次提交了一些什么

  • 分支管理

    创建分支

    1
    git branch (分支名)

    切换分支

    1
    git checkout (分支名)

    创建新的分支并且立即切换到该分支

    1
    git checkout -b (分支名)

    合并分支

    1
    git merge

    列出分支

    1
    2
    3
    4
    //列出本地分支
    git branch
    //列出全部分支(包括远程分支)
    git branch -a

    删除分支

    1
    git branch -d (分支名)

多人协作

在进行一个大型项目的时候 该怎么使用Git呢

就是刘总经常说的提PR了 😧

怎么提呢

首先你需要找到你用的大型项目仓库

然后fork!!!

fork.png

然后回到你自己仓库

就能看到你的本地仓库了

  • 本地仓库.png

怎么提PR勒 🤔

点击你本地Contribute按钮 然后Open Pull Request

pr.png

就可以跟本地一样写说明喽 标准也是跟commit的标准差不多的

小小小Tips:

在执行git push之前,为了防止远程仓库已有其他人提交的更改与你的更改产生冲突,要先执行git pull拉取远程仓库的代码,如果显示确有冲突,就要在本地手动更改冲突,可利用ide解决。 如果没有冲突,就可以直接推送。如果有冲突,解决冲突后重新执行git add和git commit ,再推送到远程仓库

就像

git 手动操作.png

基本上Git常用的就这些勒 好像博客也可以用Github Actions自动部署 以后有时间试试

😸

]]>
+ + + + + 开发基础 + + + + +
+ + + + + 浅学webpack + + /2024/03/23/%C7%B3%D1%A7webpack/ + + webpack原理(一)

1.啥是webpack捏❔

本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容

2.webpack主要流程

感觉webpack可以分成三部分

  • 构建的核心流程
  • loader 的作用
  • plugin 架构与常用套路

3.核心流程解析 😆

首先得知道这玩意最核心的功能是什么的吧

就像他官网说的一样

1
At its core, webpack is a static module bundler for modern JavaScript applications.

也就是将各种类型的资源,包括图片、css、js等,转译、组合、拼接、生成 JS 格式的 bundler 文件

屏幕截图 2024-03-23 012454.png

这个过程核心完成了 [内容转换+资源合并] 两种功能

实际上包含三个阶段

  • 初始化阶段:

  1. 初始化参数:从配置文件、 配置对象、Shell 参数中读取,与默认配置结合得出最终的参数
  2. 创建编译器对象:用上一步得到的参数创建 Compiler 对象
  3. 初始化编译环境:包括注入内置插件、注册各种模块工厂、初始化 RuleSet 集合、加载配置的插件等
  4. 开始编译:执行 compiler 对象的 run 方法
  5. 确定入口:根据配置中的 entry 找出所有的入口文件,调用 compilition.addEntry 将入口文件转换为 dependence 对象
  • 构建阶段:

  1. 编译模块(make):根据 entry 对应的 dependence 创建 module 对象,调用 loader 将模块转译为标准 JS 内容,调用 JS 解释器将内容转换为 AST 对象,从中找出该模块依赖的模块,再 递归 本步骤直到所有入口依赖的文件都经过了本步骤的处理
  2. 完成模块编译:上一步递归处理所有能触达到的模块后,得到了每个模块被翻译后的内容以及它们之间的 依赖关系图
  • 生成阶段:

  1. 输出资源(seal):根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
  2. 写入文件系统(emitAssets):在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统

屏幕截图 2024-03-23 012530.png

接下来说说细节部分吧 对上面一些听起来很高级的名词解释一手 :happy:

  • Entry:编译入口,webpack 编译的起点

  • Compiler:编译管理器,webpack 启动后会创建 compiler 对象,该对象一直存活知道结束退出

    这玩意记录了webpack运行环境的所有信息 插件可以通过它获取到webpack配置信息,像entry output module等配置

  • Compilation:单次编辑过程的管理器,比如 watch = true 时,运行过程中只有一个 compiler 但每次文件变更触发重新编译时,都会创建一个新的 compilation 对象

    它储存了当前的模块资源、编译生成的资源、变化的文件、以及被跟踪依赖的状态信息

  • Dependence:依赖对象,webpack 基于该类型记录模块间依赖关系

  • Module:webpack 内部所有资源都会以“module”对象形式存在,所有关于资源的操作、转译、合并都是以 “module” 为基本单位进行的

  • Chunk:编译完成准备输出时,webpack 会将 module 按特定的规则组织成一个一个的 chunk,这些 chunk 某种程度上跟最终输出一一对应

  • Loader:资源内容转换器,其实就是实现从内容 A 转换 B 的转换器

  • Plugin:webpack构建过程中,会在特定的时机广播对应的事件,插件监听这些事件,在特定时间点介入编译过程

4.Plugin解析

看插件之前 首先想三个问题

什么是插件

什么时间点会有什么钩子被触发

在钩子回调中,如何影响编译状态

What: 什么是插件🚘

先从形态上来看 插件是个带有apply函数的类

1
2
3
4
class SomePlugin {
apply(compiler){
}
}

apply 函数运行时会得到参数 compiler ,以此为起点可以调用 hook 对象注册各种钩子回调,例如: compiler.hooks.make.tapAsync ,这里面 make 是钩子名称,tapAsync 定义了钩子的调用方式,webpack 的插件架构基于这种模式构建而成,插件开发者可以使用这种模式在钩子回调中,插入特定代码。webpack 各种内置对象都带有 hooks 属性,比如 compilation 对象:

1
2
3
4
5
6
7
class SomePlugin {
apply(compiler) {
compiler.hooks.thisCompilation.tap('SomePlugin', (compilation) => {
compilation.hooks.optimizeChunkAssets.tapAsync('SomePlugin', ()=>{});
})
}
}

When: 什么时候会触发钩子 📦

官网对钩子的说明都比较简短

直接看几个例子吧

compiler.hooks.compilation

  • 时机:启动编译创建出 compilation 对象后触发
  • 参数:当前编译的 compilation 对象
  • 示例:很多插件基于此事件获取 compilation 实例

compiler.hooks.make

  • 时机:正式开始编译时触发
  • 参数:同样是当前编译的 compilation 对象
  • 示例:webpack 内置的 EntryPlugin 基于此钩子实现 entry 模块的初始化

compilation.hooks.optimizeChunks

  • 时机: seal 函数中,chunk 集合构建完毕后触发
  • 参数:chunks 集合与 chunkGroups 集合
  • 示例: SplitChunksPlugin 插件基于此钩子实现 chunk 拆分优化

compiler.hooks.done

  • 时机:编译完成后触发
  • 参数: stats 对象,包含编译过程中的各类统计信息
  • 示例: webpack-bundle-analyzer 插件基于此钩子实现打包分析

看这个钩子时候我直接看 触发时机 传递参数 示例代码

首先看触发时机勒

触发时机是与webpack工作过程紧密相关的,大体上从启动到结束,compiler对象逐次触发以下钩子

屏幕截图 2024-03-23 012707.png

compilation对象逐次触发:

image (1).png

就都是嘟嘟嘟嘟嘟嘟长串 😓

我自己的理解是 这个时机就是几个情况

  • 创建出这个对象触发
  • 正式开始编译就触发
  • 函数构建完毕后触发

传递参数

我在他的配置里没怎么找到 感觉无非传点modules之类的东西

示例代码

webpack这钩子实话实说

抽象程度是小大的 我感觉最好的学习办法 就是查询其他插件中如何使用这些钩子 不然你就看吧 一看一个不吱声😭

How 如何影响编译状态捏

webpack的插件体系跟平常看到的不太一样 hooks回调由webpack决定何时 以何种方式 而在hooks回调内部可以直接修改状态 调用上下文api等方式webpack产生 一些影响

举个🏮子

EntryPlugin插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class EntryPlugin {
apply(compiler) {
compiler.hooks.compilation.tap(
"EntryPlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(
EntryDependency,
normalModuleFactory
);
}
);

compiler.hooks.make.tapAsync("EntryPlugin", (compilation, callback) => {
const { entry, options, context } = this;

const dep = EntryPlugin.createDependency(entry, options);
compilation.addEntry(context, dep, options, (err) => {
callback(err);
});
});
}
}

在这里面 webpack会将上下文信息以参数或this形式传递给钩子回调用

在回调中可以调用上下文对象的方法或者直接修改上下文对象属性的方式,对原定的流程产生 side effect。所以想纯熟地编写插件,除了要理解调用时机,还需要了解我们可以用哪一些api,例如:

  • compilation.addModule:添加模块,可以在原有的 module 构建规则之外,添加自定义模块
  • compilation.emitAsset:直译是“提交资产”,功能可以理解将内容写入到一个特定路径
  • compilation.addEntry:添加入口,功能上与直接定义 entry 配置相同
  • module.addError:添加编译错误信息

5.Loader介绍

看一下loader在编译流程中的生效位置

屏幕截图 2024-03-23 012734.png

在这个流程图里,runloaders会调用用户所配置的loader集合读取,转移资源,前面的内容可以千奇百怪👽

但是转译之后一般都是输出Javascript文本,webpack才可以继续处理模块依赖

理解了这个基本逻辑,loader职责基本上也知道了 就是把内容A =>内容B

]]>
+ + + + + webpack + + + + +
+ + + + + 自定义React hooks + + /2024/03/08/%E8%87%AA%E5%AE%9A%E4%B9%89React%20Hooks/ + + 自定义React Hooks

1.为什么会出现hooks这个东西捏 ❓

刚看hooks 就出现了这个问题 hooks出现的意义在哪里呢

hooks能解决什么问题

React中组件有两种写法 一种类组件 一种函数组件

但是函数组件相对于类组件来说有一个小小滴缺点 就是没有state 😔

所以hooks就应运而生勒😀

hooks是一类特殊的函数 允许在React的函数式组件中"钩入"状态,生命周期等其他React的特性

提供了一种无需类组件的方式,使得现在在函数式组件中可以使用像 this.state,this.props的概念勒🌵

2.那hooks都这么厉害了 为什么还要有自定义的hooks捏❔

正常的useState接受两个参数

1
const [state,setState]=useState('')

正常在类组件中setState会支持两个参数: 一个是更新后的state或者回调式更新的state 另一个参数是更新后的回调函数

tips:什么是回调函数😓

回调 (callback) 是作为参数传递给另一个函数的函数 ,并在被调用函数执行完毕后被调用

个人的小理解: 回调函数就是先定义了functionA然后再定义了functionB

但是在使用时候先用了functionB 并且把functionA当成了参数给了functionB

useState hook并不直接支持像类组件中的setState方法那样可以接收第二个参数作为回调函数。useState hook返回的更新函数只能用于更新状态,并且不会提供回调函数的选项

所以自定义hooks就出现啦

3.来自定义useState吧🍶

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const useStatePro =(initState)=>{
const [state,setState]=useState(initState);
//存储一手回调函数
const isUpdate=useRef()
//定义一个新函数喽 (接受一个新状态和一个回调函数)
const setStatePro =(newState,cb)=>{
//使用setState更新状态 把回调函数储存在current里
//如果newState是个函数的情况下 就计算新状态
setState(
prev=>{
isUpdate.current=cb
return typeof newState='function' ? newState(prev):newState
}
)
}
//检查一下current有无回调函数 有就直接执行
useEffect(()=>{
if(isUpdate.current)
{
return isUpdate.current()
}
})
return [state,useStatePro]
}

这样就实现了useState的功能 但是多了一个在状态更新后执行回调函数的功能

4.自定义一个更新函数useUpdate

如果正常使用hooks想让组件重新渲染 一般是要更新state的

但是有的时候可能一个state掌握着好几个组件的生死大权😈

不能就为了一个小小的组件就让state作出无意义的更新

这时候可以想想能不能定义一个更新的hooks来优雅一些实现组件的强制更新

1
2
3
4
5
6
7
8
const Update=()=>{
const [,setFlag]=useState()
const update=()=>{
//更新一手时间
setFlag(Date.now())
}
return update
}

发现这个函数返回了一个函数 这个函数就是用来强制更新的

咋使用他捏💅

1
2
3
4
5
6
7
const Time=()=>{
const update=useUpdate();
return(
{Date.now()}
<div><button onCLick={update}>更新喽</button></div>
)
}

5.自定义hooks实现redux

Redux目前来说还是常用的管理状态的工具 但是Redux需要遵守的规则和步骤有点小多😡

所以来制作一个属于自己的Redux

1.首先先把应用接口做好

在顶部引入Provider组件为所有的儿孙组件提供所有数据源store

1
2
3
4
5
6
7
8
9
10
11
import React from 'react';
import ReactDOM, {render} from 'react-dom';
import App from './components/App'
import Provider from './store/provider'
// 挂载节点
render((
<Provider>
<App/>
</Provider>
), document.getElementById('app')
)

2.然后就可以开始设计store啦:happy:

首先就是数据项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//初始化数据
const initState={
count:0;
}
//reducer 处理器
const reducer =(state,action)=>{
const{type,payload}=action
switch(type){
case'ADD_COUNT':return{...state ,count:state.count+1}
default : return state;
}
}// 创建上下文
const Context = React.createContext()
const Provider = (props) => {
const [state, dispatch] = useReducer(reducer, initState)
return (
<Context.Provider value={{state, dispatch}}>
{props.children}
</Context.Provider>
)
}
export default { Context, Provider }

在这个数据项中可以看出 initState reducer的定义和使用redux`是一模一样的

重点看下面的创建的上下文 首先通过React.createContext()创建一个空的上下文

然后定义Provider这个组件 在内部用useReducerreducer和初始化的initState传入进去

返回的statedispatch提供到Provider作为数据源

数据项聚合一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 聚合count、todo这些数据项
const providers = [
Count.Provider,
Todo.Provider
];
// 递归包裹Provider
const ProvidersComposer = (props) => (
props.providers.reduceRight((children, Parent) => (
return Parent({children})
), props.children)
)
const Provider = (props) => {
return (
<ProvidersComposer providers={providers}>
{props.children}
</ProvidersComposer>
)
}
export default Provider

最后出来的组件结构: Provider > Context.Provider > Context.Provider > App 我们通过ProviderComposer进行递归包裹,把每个Provider进行一层一层的包裹 这里使用了parent({children})替代了<Parent>{children}</Parent>,这样的做法可以减少一层嵌套结构。

如何使用捏💩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React, { useContext, useEffect } from 'react';
// 引入count数据源
import CountStore from '@/store/modules/count'

const App = (props) => {
// 通过useContext使用Count这个store的上下文
const {state, dispatch} = useContext(CountStore.Context)
// 每秒更新count
useEffect(() => {
setInterval(() => {
dispatch({ type: 'ADD_COUNT' })
}, 1000);
}, [])

return (
<div className='app'>
{JSON.stringify(state)}
</div>
)
}

export default App

这样就实现啦一个小型redux 感觉比正常的redux会好用一些捏

]]>
+ + + + + React + + + + +
+ + + + + React hook + + /2024/02/17/React%20hook/ + + React hook

Hook 的优势

  • Hook 使你在无需改变组件结构的情况下复用状态逻辑(自定义 Hook)
  • Hook 将组件中互相关联的部分拆分成更小的函数(比如设置订阅或请求数据)
  • Hook 使你在非 class 的情况下可以使用更多的 React 特性

Hook 使用规则

Hook 就是 Javascript 函数,使用它们时有两个额外的规则:

  • 只能在函数外层调用 Hook,不要在循环、条件判断或者子函数中调用
  • 只能在 React 的函数组件自定义 Hook 中调用 Hook。不要在其他 JavaScript 函数中调用

在组件中 React 是通过判断 Hook 调用的顺序来判断某个 state 对应的 useState的,所以必须保证 Hook 的调用顺序在多次渲染之间保持一致,React 才能正确地将内部 state 和对应的 Hook 进行关联

useState

useState 可以使函数组件像类组件一样拥有 state,函数组件通过 useState 可以让组件重新渲染,更新视图

1
const [ ①state , ②dispatch ] = useState(③initData)

state,目的提供给 UI ,作为渲染视图的数据源

dispatchAction(setState) 改变 state 的函数,可以理解为推动函数组件渲染的渲染函数

initData 有两种情况,第一种情况是非函数,将作为 state 初始化的值。 第二种情况是函数,函数的返回值作为 useState 初始化的值

基础用法

1
2
3
4
5
6
7
8
9
10
11
const DemoState = (props) => {
/* number为此时state读取值 ,setNumber为派发更新的函数 */
let [number, setNumber] = useState(0) /* 0为初始值 */
return (<div>
<span>{ number }</span>
<button onClick={ ()=> {
setNumber(number+1)
console.log(number) /* 这里的number是不能够即使改变的 */
} } ></button>
</div>)
}

``useState 注意事项`:

① 在函数组件一次执行上下文中,state 的值是固定不变的

② 如果两次 dispatchAction 传入相同的 state 值,那么组件就不会更新

③ 当触发 dispatchAction 在当前执行上下文中获取不到最新的 state, 只有在下一次组件 rerender 中才能获取到。

useReducer

组件中可能有多个位置包括了对某个状态的修改操作

useReducer用于统一管理状态的操作方式

使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为父组件可以向子组件传递 dispatch 而不是回调函数

1
const[state,dispatch]= useReducer (countReducer,0)

① 更新之后的 state 值。

② 派发更新的 dispatch 函数, 本质上和 useState 的 dispatch是一样的。

③ 一个函数countReducer 常规reducer里面的 state 和action

准备一个用来进行状态功能管理的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function countReducer (state,action)
//state reducer管理状态的是哪个
//action 对这个状态进行哪些操作
{
switch(action.type)
{
case"increment" :return state+1
case "decrement":return state-1
default:
throw new Error()
}
}
export default function App ()
{
//state 状态当前值
//dispatch 用来进行状态修改的触发器 (函数值)
const [state,dispath]=useReducer(countReducer,0)
const handleIncrement=()=>dispath({type:"increment"})
const handleDecrement=()=>dispath({type:"decrement"})
return (
<div>
<button onClick={handleIncrement}>-</button>
<span>{state}</span>
<button onClick={handleDecrement}>+</button>
</div>
)
}

useSyncExternalStore

不懂以后再说

useTransition

别急

useDeferredValue

你也别急

useEffect

1
2
3
useEffect(()=>{
return destory
},dep)

第一个参数callback返回的 destroy 作为下一次callback执行之前调用 用于清楚上一次callback产生的副作用

第二个参数作为依赖项,是一个数组,可以有多个依赖项,依赖项改变,执行上一次callback 返回的 destory ,和执行新的 effect 第一个参数 callback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/* 模拟数据交互 */
function getUserInfo(a){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve({
name:a,
age:16,
})
},500)
})
}
const Demo = ({ a }) => {
const [ userMessage , setUserMessage ] :any= useState({})
const div= useRef()
const [number, setNumber] = useState(0)
/* 模拟事件监听处理函数 */
const handleResize =()=>{}
/* useEffect使用 ,这里如果不加限制 ,会是函数重复执行,陷入死循环*/
useEffect(()=>{
/* 请求数据 */
getUserInfo(a).then(res=>{
setUserMessage(res)
})
/* 定时器 延时器等 */
const timer = setInterval(()=>console.log(666),1000)
/* 操作dom */
console.log(div.current) /* div */
/* 事件监听等 */
window.addEventListener('resize', handleResize)
/* 此函数用于清除副作用 */
return function(){
clearInterval(timer)
window.removeEventListener('resize', handleResize)
}
/* 只有当props->a和state->number改变的时候 ,useEffect副作用函数重新执行 ,如果此时数组为空[],证明函数只有在初始化的时候执行一次相当于componentDidMount */
},[ a ,number ])
return (<div ref={div} >
<span>{ userMessage.name }</span>
<span>{ userMessage.age }</span>
<div onClick={ ()=> setNumber(1) } >{ number }</div>
</div>)
}

如上在 useEffect 中做的功能如下:

  • ① 请求数据。
  • ② 设置定时器,延时器等。
  • ③ 操作 Dom
  • ④ 注册事件监听器, 事件绑定
  • ⑤ 还可以清除定时器,延时器,解绑事件监听器等。

useLayoutEffect

别急

useInsertionEffect

你也别急

useContext

可以用useContext 来获取父级组件传递过来的context的值

这个当前值就是最近的父级组件Provider 设置的value值 ,

useContext 参数一般是由createContext方式创建的,也可以父级上下文context传递的(参数是context )

1
const contextValue = useContext(context)

useContext 接受一个参数 一般都是 context 对象,返回值为 context 对象内部保存的 value 值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* 用useContext方式 */
const DemoContext = ()=> {
const value= useContext(Context)
/* my name is alien */
return <div> my name is { value.name }</div>
}

/* 用Context.Consumer 方式 */
const DemoContext1 = ()=>{
return <Context.Consumer>
{/* my name is alien */}
{ (value)=> <div> my name is { value.name }</div> }
</Context.Consumer>
}

export default ()=>{
return <div>
<Context.Provider value={{ name:'alien' , age:18 }} >
<DemoContext />
<DemoContext1 />
</Context.Provider>
</div>
}

useRef

useRef 可以用来获取元素,缓存状态,接受一个状态 initState 作为初始值,返回一个 ref 对象 cur, cur 上有一个 current 属性就是 ref 对象需要获取的内容

1
2
const cur = React.useRef(initState)
console.log(cur.current)

useRef 创建的 ref 对象就是一个普通的对象,而 useRef() 和自建一个 {current: ...} 对象的唯一区别是,useRef 会在每次渲染时返回同一个 ref 对象

useRef 基础用法:

useRef来获取DOM节点

1
2
3
4
5
6
7
8
9
10
11
12
13
const DemoUseRef = ()=>{
const dom= useRef(null)
const handerSubmit = ()=>{
/* <div >表单组件</div> dom 节点 */
console.log(dom.current)
}
return <div>
{/* ref 标记当前dom节点 */}
<div ref={dom} >表单组件</div>
<button onClick={()=>handerSubmit()} >提交</button>
</div>
}

useRef 保存状态

可以利用 useRef 返回的 ref 对象来保存状态,只要当前组件不被销毁,那么状态就会一直存在

1
2
3
4
5
const status = useRef(false)
/* 改变状态 */
const handleChangeStatus = () => {
status.current = true
}

useImperativeHandle

这什么玩意

useMemo

useMemo 可以在函数组件 render 上下文中同步执行一个函数逻辑,这个函数的返回值可以作为一个新的状态缓存起来。那么这个 hooks 的作用就显而易见了

场景一:在一些场景下,需要在函数组件中进行大量的逻辑计算,那么我们不期望每一次函数组件渲染都执行这些复杂的计算逻辑,所以就需要在 useMemo 的回调函数中执行这些逻辑,然后把得到的产物(计算结果)缓存起来就可以了。

场景二:React 在整个更新流程中,diff 起到了决定性的作用,比如 Context 中的 provider 通过 diff value 来判断是否更新

useMemo 基础介绍:

1
const cacheSomething = useMemo(create,deps)

① create:第一个参数为一个函数,函数的返回值作为缓存值,如上 demo 中把 Children 对应的 element 对象,缓存起来。

② deps: 第二个参数为一个数组,存放当前 useMemo 的依赖项,在函数组件下一次执行的时候,会对比 deps 依赖项里面的状态,是否有改变,如果有改变重新执行 create ,得到新的缓存值。

cacheSomething:返回值,执行 create 的返回值。如果 deps 中有依赖项改变,返回的重新执行 create 产生的值,否则取上一次缓存值。

useMemo 基础用法:

派生新状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Scope() {
const keeper = useKeep()
const { cacheDispatch, cacheList, hasAliveStatus } = keeper

/* 通过 useMemo 得到派生出来的新状态 contextValue */
const contextValue = useMemo(() => {
return {
cacheDispatch: cacheDispatch.bind(keeper),
hasAliveStatus: hasAliveStatus.bind(keeper),
cacheDestory: (payload) => cacheDispatch.call(keeper, { type: ACTION_DESTORY, payload })
}

}, [keeper])
return <KeepaliveContext.Provider value={contextValue}>
</KeepaliveContext.Provider>
}

如上通过 useMemo 得到派生出来的新状态 contextValue ,只有 keeper 变化的时候,才改变 Provider 的 value

useCallback

这个好像有点急 但是先别急

useDebugValue

这什么玩意

useId

看不懂思密达

自定义hook

别急

]]>
+ + + + + React + + + + +
+ + + + + 组件通信 + + /2024/02/17/%E7%BB%84%E4%BB%B6%E9%80%9A%E4%BF%A1/ + + React组件通信

组件

dom组件 react支持的htmlsvg标签

props

什么是props html属性的功能

dom属性

类名 className

样式

1
2
3
4
5
6
7
8
style={
{
width:'100vh',
height:100,
backgroundColor:'grey'
//带-的写成驼峰
}
}

也可以书写变量

1
2
3
4
5
6
7
8
9
const imgStyle={
width:'100vh',
height:100,
backgroundColor:'grey'
}
return(
<div>
<img style={imgStyle}/>
</div>)

jsx展开语法

把所有属性书写为一个变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import image from './logo.svg'
const imgData={
className='small',
style:{
width:'100vh',
height:100,
backgroundColor:'grey'
} }
return(
<div>
<img
//src标签不能提出
src={image}
{...imgData}
/>
</div>
)

自定义组件react的props

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Artcile()
{
return(
<div>
<h2>111</h2>
<p>222</p>
</div>
)
}
export default function App()
{
return(
<>
<Article/>
<Article/>
<Article/>
<Article/>
</>


)
}

如果对组件的结构逻辑 样式复用

内容不复用的话 可以写成

对组件内容定制

步骤

1.对父元素(上面的App)进行请求 请求功能所需要的数据

2.创建组件

3.把数据传给组件

父组件向子组件传普通值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
function Detail()
{
return(
<>
<p>{content}</p>
<p>状态:{active?'显示':'隐藏'}</p>
</>
)
}
function Artcile({acticleData})
//采用解构
{
return(
<div>
<h2>{title}</h2>
<Detail {...articleData}
></Detail>
</div>
)
//此时Article没用到content和active
//但是Detail用到了 可以在Article先不传 直接传给Detail
}
export default function App()
{const articleData={
title:'1',
detailData:{
content:'1',active:true
}
}
return(
<>
<Article
{...acticleData}
/>
<Article
title="2"
content="2"
active
/>
<Article
title="3"
content="3"
active
/>
</>


)
}

父组件向子组件传jsx

插槽 jsx作为props传给子组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
function List ({children,title,footer=       
<div>默认底部</div>})
//此时footer需要一个默认值
{
return(
<>
<h2>{title}</h2>
<ul>
{children}
</ul>
{footer}
</>
)
}
//如果需要在列表加元素
export default function App ()
{
return (
<>
//之前是通过props传递 现在通过children
//自动接受组件开始和接受的内容
<List
title="列表1"
footer={
<p>这是底部内容1</p>
}>
<li>内容1</li>
<li>内容2</li>
<li>内容3</li>
</List>

<List
title="列表2"
footer={<p>这是底部内容2</p>}>
<li>内容A</li>
<li>内容B</li>
<li>内容C</li>
</List>
<List
title="列表3"
//此时没设置footer会显示默认值
>
<li>内容X</li>
<li>内容Y</li>
<li>内容Z</li>
</List>
</>
)
}

子组件向父组件传值

父组件给子组件一个自定义事件的设置 再通过事件触发后向父组件传递参数来设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function Detail ({onActive}){
//希望告诉父组件内部状态 在handleClick函数使用的变更 所以在handleClick加入onACtive事件并传入status
const [status,setStatus]=useState(false)
function handleClick()
{
setStatus(!status)
onActive(status)
//每次状态变更 传递一个新值 传递到function App()
}
return (
<div>
<button onClick={handleClick}>按钮</button>
<p
style={{display;staus?
'block':'none'}}>
Detail的内容</p>

</div>
)
}
export default function App(){
function handleActive(status){
console.log(status)
}
return(
<>
//如果希望父组件能接受子组件的状态的话需要给子组件设置一个自定义属性(onActive)可以理解为事件 用代码决定在何时触发

<Detail
onActive={handleActive}></Detail>
</>

)
}

同级组件传值

在父组件进行中转

多层级传值

提供了一个多级属性穿透的hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import {useState} from "react"
export function Section ({children})
{
//获取上一级level
const level=useContext(LevelContext)
return (

<section className="section">
<LevelContect.Provider
value={
level+1
}>
{children}</LevelContect.Provider>

</section>
)
}
export function Heading({level,children})
{
const level=useContext(LevelContext)
//设置之后此时全为h1(若默认值是1)
//但是h1和section嵌套会使h1显示出从大到小的样式
switch(level){
case 1:
return <h1>{children}</h1>
case 2:
return <h2>{children}</h2>
case 3:
return <h3>{children}</h3>
case 4:
return <h4>{children}</h4>
case 4:
return <h4>{children}</h4>
case 5:
return <h5>{children}</h5>
case 6:
return <h6>{children}</h6>
}
}
const LevelContext=createContext(0)
//不能直接用 通过useContext
export default function App (){
return(
<div>
<section>
<Heading >主标题</Heading>
<section>
<Heading >副标题</Heading>
<Heading >副标题</Heading>
<Heading >副标题 </Heading>
<section>
<Heading>子标</Heading>
<Heading>子标</Heading>
<Heading >子标</Heading>
<section>
<Heading >子子标题</Heading>
<Heading>子子标题</Heading>
<Heading >子子标题</Heading>
<section>
<Heading >子子子标题</Heading>
<Heading >子子子标题</Heading>
<Heading >子子子标题</Heading>
<section>>
<Heading >子子子子标题 </Heading>
<Heading >子子子子标题 </Heading>
<Heading >子子子子标题 </Heading>
</section>
</section>
</section>
</section>
</section>
</section>
</div>)
}

Ps:可能会出现都是h1标签但是大小却不同的情况

sectionh1嵌套时候自然出现的状态

如何更改useContext能够获取到的level信息

useContext创建的对象LevelContext提供一个Provider

context提供值的一个方式

]]>
+ + + + + React + + + + +
+ + + + + Javascript-class类 + + /2023/12/16/Javascript-class%E7%B1%BB/ + + Javascript-class类

类的定义

有两个组成部分

类表达式和类声明

每个类中包含了一个特殊的方法 constructor(),它是类的构造函数,这种方法用于创建和初始化一个由 class 创建的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//类声明
class A {
constructor(name,age)
{
this.name=name;
this.age=age;
}
}
//类表达式
const B=class{
constructor(name,age)
{
this.name=name
this.age=age
}
}

以上创造了一个类 叫A

初始化了两个参数 name , age

使用类

1
2
3
const a1=new A ('shanyujia',18)
console.log(a1)
// =>{ name: 'shanyujia', age: 18 }

基于这个类去创造这个类的实例

基于这个对象生成 结构相同 内部数据不同的对象形式

自定义方法

1
2
3
4
5
6
7
8
9
10
11
12
class A {
constructor(name,age)
{
this.name=name;
this.age=age;
}
introduce(){
console.log(`我的名字是${this.name},我的年龄是${this.age} `)
}
}
a1.introduce()
// => 我的名字是shanyujia,我的年龄是18

类的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class B extends A{
constructor(name,age,sex){
//如果希望继承A的属性 写一个super的调用
//调用父类的constructor 将属性也在B类生成
super(name,age)
this.sex=sex
}
sayHello(){
console.log('你好我是'+this.name)
}
}
const b1=new B('张三',20,'男')
console.log(b1)
b1.sayHello()
b1.introduce()
/*{ name: '张三', age: 20, sex: '男' }
我的名字是张三,我的年龄是20
你好我是张三*/
]]>
+ + + + + Javascript + + + + +
+ + + + + Javascript 函数 + + /2023/12/09/Javascript-%E5%87%BD%E6%95%B0/ + + JavaScript 函数

1.typeof

会产生一个字符串的值,内容是给定值的具体类型

1
2
3
4
console.log(typeof 4.5)
// → number
console.log(typeof "x")
// → string

2.prompt

包含一函数,个显示一个小对话框,请求用户输入

1
prompt("Enter passcode");

3.cosole.log

输出值

1
2
3
let x = 30;
console.log("the value of x is", x);
// → the value of x is 30

4.Math.max

接受任意数量的参数并返回最大

1
2
console.log(Math.max(2, 4));
// → 4

5.Math.min

跟Math.max相反

1
2
console.log(Math.min(2, 4) + 100);
// → 102

6.Number.isNaN

仅当它给出的参数是NaN时才返回true

当你给它一个不代表有效数字的字符串时

Number函数恰好返回NaN

7.函数定义

1
2
3
4
function square(x)
{
return x*x;
}

以关键字function起始的表达式创建

函数有一组参数(例子中只有x)和一个主体

它包含调用该函数时要执行的语句

return 决定 函数返回值

没有return的函数 返回undefined

8.箭头函数

=>

1
2
const square1 = (x) => { return x * x; };
const square2 = x => x * x;

箭头出现在参数列表之后,然后是函数主体

表达的意思类似于 这个输入(参数)产生这个结果(主体)

9.push

将值添加到数组的末尾

1
2
3
4
5
6
7
8
9
let sequence = [1, 2, 3];
sequence.push(4);
sequence.push(5);
console.log(sequence);
// → [1, 2, 3, 4, 5]
console.log(sequence.pop());
// → 5
console.log(sequence);
// → [1, 2, 3, 4]

10.pop

与push相反 删除数组中最后的一个值并将其返回

11.delete

一元运算符

当应用于对象属性时,将从对象中删除指定的属性

1
2
3
4
5
6
7
8
9
10
let anObject = {left: 1, right: 2};
console.log(anObject.left);
// → 1
delete anObject.left;
console.log(anObject.left);
// → undefined
console.log("left" in anObject);
// → false
console.log("right" in anObject);
// → true

12.in

二元运算符 会告诉你该对象是否具有名称为它的属性

将属性设置为 undefined 和实际删除它的区别在于

在设置为undefined的时候 对象仍然具有属性 只是没有意义

删除它时 属性不再存在 in 会返回 false

13.Object.keys

给它一个对象 它返回一个字符串数组 -对象的属性名称

1
2
console.log(Object.keys({x: 0, y: 0, z: 2}));
// → ["x", "y", "z"]

14.Object.assgin

可以将一个对象的所有属性复制到另一个对象中

1
2
3
4
let objectA = {a: 1, b: 2};
Object.assign(objectA, {b: 3, c: 4});
console.log(objectA);
// → {a: 1, b: 3, c: 4}

15.Math.sqrt

平方根函数

16.unshift

在数组开头添加元素

17.shift

在数组的开头删除元素

remember("groceries")将任务添加到队列的末尾

getTask()从队列中获取(并删除)第一个项目

rememberUrgently函数也添加任务 .

但将其添加到队列的前面而不是队列的后面

1
2
3
4
5
6
7
8
9
10
let todoList = [];
function remember(task) {
todoList.push(task);
}
function getTask() {
return todoList.shift();
}
function rememberUrgently(task) {
todoList.unshift(task);
}

18.lastIndexOf

indexof 类似 indexof从数组第一个元素开始搜索

lastIndexOf 从最后一个元素向前搜索

1
2
3
4
console.log([1, 2, 3, 2, 1].indexOf(2));
// → 1
console.log([1, 2, 3, 2, 1].lastIndexOf(2));
// → 3

19.Math

Math.max最大值

Math.min最小值

Math.sqrt平方根

Math.random生成一个随机数 范围在 0(包括)到 1(不包括)之间

Math.PI表示数字π

Math.floor向下取整到与当前数字最接近的整数

Math.ceil向上取整

Math.abs取数字的绝对值

Math.round四舍五入

]]>
+ + + + + Javascript + + + + +
+ + + + + javascript1 + + /2023/11/27/javascript%E7%9F%A5%E8%AF%86%E7%82%B9/ + + javascript第一章
一元运算符

1
2
3
4
console.log(typeof 4.5)
//输出 number
console.log(typeof "x")
//输出 string

typeof生成一个字符串值 得出你给它的值的类型名

布尔值

有两个值 truefalse

生成布尔值的方法

1
2
3
4
console.log (3>2)
// true
console.log (3<2)
// false

符号 > 和 < 表示 “大于”和“小于” 二元运算符

返回的结果是一个布尔值,表示其运算是否为真。

也可以用这种方法比较字符串

1
2
console.log("Aardvark" < "Zoroaster")
// → true

在 JavaScript 中,只有一个值不等于其自身,那就是NaN(非数值)

1
2
console.log (NaN == NaN)
// → false

NaN用于表示非法运算的结果

逻辑运算符

&& 运算符表示逻辑与,该运算符是二元运算符,只有当赋给它的两个值均为true时其结果才是真

1
2
3
4
console.log(true && false)
// → false
console.log(true && true)
// → true

||运算符表示逻辑或。当两个值中任意一个为true时,结果就为真

1
2
3
4
console.log(false || true)
// → true
console.log(false || false)
// → false

优先级问题

||优先级最低,其次是&&,然后是比较运算符(>,==,<)

最后是其他运算符

三元预算符

A?B:C

1
2
3
4
console.log(true ? 1 : 2);
// → 1
console.log(false ? 1 : 2);
// → 2

被叫做条件运算符

问号左面的值挑选一个值

它为 true 是 选择中间的值

它为false 是选择右面的值

空值

null undefined

表示不存在有意义的值

两者差别不大

]]>
+ + + + + Javascript + + + + +
+ + + + + my new post + + /2023/11/20/C%E8%AF%AD%E8%A8%80%E5%87%BD%E6%95%B0/ + + ctype.h
1.isascii()

判断是不是在0-127

是ASCII 是1

不是ASCII 是0

2.isalpha

小写2 大写1 不是字母0

3.isdigit

是不是十进制

0-9 返回非0

不然返回0

4.isalnum()

是不是字母或者数字

是返回非0 不是返回0

5.isblank()

是不是空白字符

是 输出非0

不是 输出0

6.isspace()

是不是空白字符

‘ ’ or ‘\t’ or ‘ \r’ or ‘ \n’ or ‘ \v ’or ‘\f ’

7.islower()

是不是小写字母

是为 非零 不是为0

8.isupper()

是不是大写字母

是 非零 不是零

9.toupper()

转换为大写字母

touppper(‘a’)

10.tolower()

转换为小写字母

tolower(‘A’)

]]>
+ + + + + C++ + + + + +
+ + + + +
diff --git a/page/2/index.html b/page/2/index.html new file mode 100644 index 0000000..b0cd27d --- /dev/null +++ b/page/2/index.html @@ -0,0 +1,563 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + + + + + + + + + + + + + + + + +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/placeholder b/placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/tags/index.html b/tags/index.html new file mode 100644 index 0000000..bf17bd4 --- /dev/null +++ b/tags/index.html @@ -0,0 +1,340 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tags - Shanyujia's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + +
+ +
+ +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/local-search.xml b/xml/local-search.xml new file mode 100644 index 0000000..d7d0c01 --- /dev/null +++ b/xml/local-search.xml @@ -0,0 +1,45 @@ + + + {% if posts %} + {% for post in posts.toArray() %} + {% if post.indexing == undefined or post.indexing %} + + {{ post.title }} + + {{ [url, post.path] | urlJoin | uriencode }} + {% if content %} + + {% endif %} + {% if post.categories and post.categories.length>0 %} + + {% for cate in post.categories.toArray() %} + {{ cate.name }} + {% endfor %} + + {% endif %} + {% if post.tags and post.tags.length>0 %} + + {% for tag in post.tags.toArray() %} + {{ tag.name }} + {% endfor %} + + {% endif %} + + {% endif %} + {% endfor %} + {% endif %} + {% if pages %} + {% for page in pages.toArray() %} + {% if post.indexing == undefined or post.indexing %} + + {{ page.title }} + + {{ [url, post.path] | urlJoin | uriencode }} + {% if content %} + + {% endif %} + + {% endif %} + {% endfor %} + {% endif %} +