├── .gitignore
├── .npmrc
├── .tokeignore
├── .vscode
└── settings.json
├── README.md
├── bun.lock
├── justfile
├── netlify.toml
├── package.json
├── playground.js
├── pnpm-lock.yaml
├── slides
├── JavaScript-API-export.pdf
├── JavaScript-API.md
├── Vue3-API-export.pdf
├── Vue3-API.md
├── components
│ ├── My.vue
│ ├── Ques.vue
│ └── Tag.vue
├── guide.md
├── public
│ ├── Vue3-doc.png
│ ├── background.avif
│ ├── js-doc.png
│ ├── 全球新冠疫情数据统计.gif
│ ├── 指令语法.png
│ ├── 灯的颜色变化.gif
│ ├── 组件树.png
│ └── 课程列表.gif
├── 专题-异步编程.md
├── 浏览器API-export.pdf
└── 浏览器API.md
├── snippets
├── JavaScript
│ ├── ISBN转换与生成.js
│ ├── JSON转换.js
│ ├── Markdown解析器.js
│ ├── 井字棋.js
│ ├── 分享点滴.js
│ ├── 商品浏览足迹.js
│ ├── 寻找小狼人.js
│ ├── 小兔子找萝卜.js
│ ├── 工作协调.js
│ ├── 悠然画境.js
│ ├── 收集帛书碎片.js
│ ├── 猜硬币.js
│ ├── 解密工具.js
│ └── 谁最长.js
├── Node
│ ├── tree命令助手.js
│ ├── tree命令助手bfs.cjs
│ ├── tree命令助手dfs.cjs
│ ├── 代码量统计bfs.js
│ ├── 代码量统计dfs.js
│ ├── 扫雷.js
│ ├── 项目语言分析.js
│ ├── 项目语言分析bfs.js
│ └── 项目语言分析dfs.js
├── Promise
│ ├── MyPromise
│ │ ├── .gitignore
│ │ ├── MyPromise.js
│ │ ├── README.md
│ │ ├── bun.lock
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── testAsyncTask.js
│ │ ├── testResolve.js
│ │ └── tsconfig.json
│ ├── NPM Download Simulator.js
│ ├── note.md
│ └── 产品360度演示.js
├── Vue3
│ ├── GithubDesktop.js
│ ├── Github明星项目统计.html
│ ├── 个性化桌面.html
│ ├── 个性化桌面.js
│ ├── 多表单校验.js
│ ├── 学生探览.js
│ ├── 小蓝驿站.html
│ └── 需求管理
│ │ ├── DemandList.js
│ │ ├── FilterGroup.js
│ │ ├── Stats.js
│ │ └── index.html
├── 模拟1
│ ├── 2.js
│ ├── 4.js
│ ├── 5.js
│ ├── 6.js
│ ├── 7.js
│ └── 第9、10题都在前面讲过.txt
├── 模拟2
│ ├── 2.js
│ ├── 3.js
│ ├── 4.js
│ ├── 5.js
│ ├── 6.js
│ └── 7.js
└── 浏览器
│ ├── 冬奥大抽奖.js
│ ├── 图片水印生成.js
│ ├── 布局切换.js
│ ├── 年度明星项目.js
│ ├── 新增地址.js
│ ├── 水果消消乐.js
│ ├── 灯的颜色变化.js
│ ├── 真人鉴定器.js
│ ├── 神奇的滤镜.js
│ ├── 简易JSX解析器.js
│ ├── 请到下一步.js
│ └── 课程列表.js
├── theme
├── CHANGELOG.md
├── LICENSE
├── README.md
├── example.md
├── layoutHelper.ts
├── layouts
│ ├── cover.vue
│ ├── fact.vue
│ ├── intro.vue
│ ├── quote.vue
│ ├── section.vue
│ └── statement.vue
├── package.json
└── styles
│ ├── index.ts
│ ├── layouts.css
│ └── prism.css
└── vercel.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | *.local
5 | .vite-inspect
6 | .remote-assets
7 | components.d.ts
8 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | # for pnpm
2 | shamefully-hoist=true
3 | auto-install-peers=true
4 |
--------------------------------------------------------------------------------
/.tokeignore:
--------------------------------------------------------------------------------
1 | *.yaml
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "slidev.force-enabled": true,
3 | "slidev.port": 3030,
4 | "slidev.include": [
5 | "**/slides.md",
6 | "slides/异步编程.md",
7 | "slides/浏览器API.md",
8 | "slides/浏览器API与Vue3.md",
9 | "slides/Vue3.md",
10 | "slides/JavaScript-API.md"
11 | ],
12 | "Codegeex.GenerationPreference": "automatic"
13 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 蓝桥杯Web组备赛指南 · 2025年省赛
2 |
3 | 你好!我是B站的一个up主[CodeNebula](https://space.bilibili.com/3493089530350281),讲解蓝桥杯Web组的基础知识和刷题技巧。
4 |
5 | 这是我视频的仓库,存放着课件、代码等资料。
6 |
7 |
8 | ## 幻灯片
9 |
10 | 幻灯片使用[slidev](https://sli.dev/)编写,存放在`slides/`目录下。
11 |
12 | 你可以在本地运行slidev项目,查看幻灯片:
13 |
14 | ```bash
15 | # 预先安装node、pnpm、just
16 | pnpm install
17 |
18 | just dev guide # 这将启动slides/guide.md
19 | just dev another-slide # 这将启动slides/another-slide.md
20 |
21 | just pick # 启动交互式选择器,需要预先安装fzf并在bash/zsh中运行
22 | ```
23 |
24 | 当然,我也为你提供了可以直接查看的PDF版本,无需任何配置即可直接下载。但是没有幻灯片的动画效果。
25 |
26 | ## 刷题视频代码
27 |
28 | 我的刷题视频对应的代码存放在`snippets/`目录下。
29 |
30 | ## 关于我
31 |
32 | 我是一名大二的学生,主要写TypeScript、Rust和Python,也喜欢折腾shell和开发环境。你可以关注我的其它项目,让我更快地成长起来!
--------------------------------------------------------------------------------
/justfile:
--------------------------------------------------------------------------------
1 | # 启动对应的Web演示。
2 | dev name:
3 | pnpm run dev slides/{{ name }}.md
4 |
5 | # 需npm i -D playwright-chromium,导出为PDF。
6 | export name:
7 | pnpm run export slides/{{ name }}.md
8 |
9 | # 需安装fzf并在unix环境下:
10 | pick:
11 | ls slides/*.md | fzf | xargs pnpm run dev
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | publish = "dist"
3 | command = "npm run build"
4 |
5 | [build.environment]
6 | NODE_VERSION = "20"
7 |
8 | [[redirects]]
9 | from = "/.well-known/*"
10 | to = "/.well-known/:splat"
11 | status = 200
12 |
13 | [[redirects]]
14 | from = "/*"
15 | to = "/index.html"
16 | status = 200
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lanqiao-web-slides",
3 | "type": "module",
4 | "private": true,
5 | "scripts": {
6 | "build": "slidev build",
7 | "dev": "slidev --open",
8 | "export": "slidev export"
9 | },
10 | "dependencies": {
11 | "@slidev/cli": "^51.1.0",
12 | "@slidev/theme-default": "latest",
13 | "@slidev/theme-seriph": "latest",
14 | "consola": "^3.4.2",
15 | "slidev-theme-dracula": "^0.2.0",
16 | "vue": "^3.5.13"
17 | },
18 | "devDependencies": {
19 | "playwright-chromium": "^1.51.0"
20 | }
21 | }
--------------------------------------------------------------------------------
/playground.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaojunran/lanqiao-web-slides/ab4a21d2b387ee1a6e1f7514571d3760d65c4016/playground.js
--------------------------------------------------------------------------------
/slides/JavaScript-API-export.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaojunran/lanqiao-web-slides/ab4a21d2b387ee1a6e1f7514571d3760d65c4016/slides/JavaScript-API-export.pdf
--------------------------------------------------------------------------------
/slides/JavaScript-API.md:
--------------------------------------------------------------------------------
1 | ---
2 | # You can also start simply with 'default'
3 | theme: ../theme
4 | # random image from a curated Unsplash collection by Anthony
5 | # like them? see https://unsplash.com/collections/94734566/slidev
6 | background: https://images.unsplash.com/photo-1621237023000-6a628c285938?q=80&w=3270&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D
7 | # some information about your slides (markdown enabled)
8 | title: JavaScript API
9 | info: |
10 | Created with [Sli.dev](https://sli.dev)
11 | # apply unocss classes to the current slide
12 | class: text-center
13 | # https://sli.dev/features/drawing
14 | drawings:
15 | persist: false
16 | # slide transition: https://sli.dev/guide/animations.html#slide-transitions
17 | transition: fade-out
18 | # enable MDC Syntax: https://sli.dev/features/mdc
19 | mdc: true
20 |
21 | ---
22 |
23 | # JavaScript API { .!text-yellow-100}
24 |
25 | 蓝桥杯Web组 省赛备赛 {.!text-white/50}
26 |
27 | ---
28 |
29 |
30 | ## JavaScript 学习资源
31 |
32 | [MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript)
33 |
34 | [JavaScript 权威指南](https://github.com/apachecn/JavaScript-The-Definitive-Guide-7th-zh)
35 |
36 |
37 | ---
38 |
39 |
40 | ## JavaScript速览
41 |
42 |
43 |
44 | 1. JavaScript是一门**动态、解释性**编程语言,非常适合**面向对象和函数式编程**风格。
45 |
46 | 2. 核心JavaScript定义了最小限度的API,用于操作数值、文本、数组、Map、Set等。
47 |
48 | 3. **浏览器**是JavaScript最早的运行环境;**Node.js**是JavaScript的另一个运行环境,给予了JavaScript访问整个操作系统的权限。
49 |
50 | 4. JavaScript类型可以分为**原始类型(Number、String、Boolean、null、undefined、Symbol)和对象类型(Array、Set、Map、RegExp、Date等)**。
51 |
52 | 5. JavaScript有一个灵活而被诟病的**自动转换规则**。
53 |
54 |
55 |
56 | ---
57 |
58 |
59 | ## `Number`
60 |
61 |
62 |
63 | ### 数学计算 {.!mt-6}
64 |
65 | ```js{hide|1|2|3|4|5|6|7|8|9|all}
66 | Math.round(1.5) // 2
67 | Math.fround(1.5) // 1.5,舍入到最接近的32位浮点数
68 | Math.ceil(1.1) // 2
69 | Math.floor(1.9) // 1
70 | Math.sqrt(4) // 2
71 | Math.pow(2, 10) // 1024
72 | Math.random() // 0.0 ~ 1.0
73 | Math.max(1, 2, 3) // 3
74 | Math.min(1, 2, 3) // 1
75 | ```
76 |
77 | ### 上溢出?下溢出?被零除? {.!mt-6}
78 |
79 | ```js{hide|1|2|3|4|5|6|all}
80 | Number.MAX_VALUE // 1.7976931348623157e+308
81 | Number.MIN_VALUE // 5e-324
82 | Number.isNaN(x)
83 | Number.isFinite(x)
84 | Number.isInteger(x)
85 | Number.isSafeInteger(x)
86 | ```
87 |
88 |
89 |
90 | ---
91 |
92 |
93 | ## `String`
94 |
95 | ::my
96 | ::
97 |
98 | ```js{hide|1|1-2|1-3|1-4|1,5|1,5-6|1,5-7|1,5-8|all}
99 | let str = 'hello world';
100 | str.length // 11
101 | str[0] // 'h'
102 | str[str.length - 1] // 'd'
103 | str.substring(0, 5) // 'hello',
104 | str.slice(0, 5) // 'hello'
105 | str.slice(6) // 'world',默认截取到结尾
106 | str.slice(-1) // 'd',支持负数
107 | ```
108 |
109 | ```js{hide|1|1,2,3|1,4|1,5,6|all}
110 | let str = 'hello world';
111 | str.indexOf('world') // 6
112 | str.lastIndexOf('o') // 7
113 | str.includes('hello') // true
114 | str.startsWith('hello') // true
115 | str.endsWith('world') // true
116 | ```
117 |
118 | ```js{hide|all}
119 | let str = 'hello world';
120 | str.toUpperCase() // 'HELLO WORLD'
121 | str.toLowerCase() // 'hello world'
122 | ```
123 |
124 | ---
125 |
126 | ## `String`
127 |
128 | ::my
129 | ::
130 |
131 | ```js{hide|1,2|1,3|all}
132 | let str = 'hello world';
133 | str.split(' ') // ['hello', 'world']
134 | str.replace('world', 'javascript') // 'hello javascript'
135 | ```
136 |
137 | ```js{hide|all}
138 | let str = ' hello world ';
139 | str.trim() // 'hello world'
140 | str.trimStart() // 'hello world '
141 | str.trimEnd() // ' hello world'
142 | ```
143 |
144 | ```js{hide|1-3|5|all}
145 | "x".padStart(3, "*") // '**x'
146 | "x".padEnd(3, "*") // 'x**
147 | "x".padStart(3) // ' x'
148 |
149 | "<>".repeat(3) // '<><><>'
150 | ```
151 |
152 |
153 |
154 | ---
155 |
156 | ## `Boolean`
157 |
158 | ::my
159 | ::
160 |
161 |
162 | ```js{hide|1|2|3|4|5|6|7|8|9|all}
163 | Boolean(0) // false
164 | Boolean(NaN) // false
165 | Boolean('') // false
166 | Boolean(null) // false
167 | Boolean(undefined) // false
168 | Boolean([]) // true!!!
169 | Boolean({}) // true!!!
170 | Boolean(' ') // true
171 | Boolean('false') // true
172 | ```
173 |
174 | ---
175 |
176 | ## 原始值类型转换
177 |
178 |
179 |
180 | 隐式的类型转换:
181 |
182 | ```js{hide|all}
183 | x + "" // String(x)
184 | `${x}` // String(x), recommended
185 | +x // Number(x)
186 | !!x // Boolean(x)
187 | ```
188 |
189 | 显式的类型转换:
190 |
191 | ```js{hide|all}
192 | String(x)
193 | Number(x)
194 | Boolean(x)
195 | ```
196 |
197 | 在每次进行类型转换时,都需要**想一下`undefined`和`null`**!
198 |
199 | 控制数字转字符串的格式:
200 |
201 | ```js{hide|1|1,2|1,3|1,4|all}
202 | let num = 123456.789;
203 | num.toFixed(2) // "123456.79"
204 | num.toPrecision(5) // "1.2346e+5"
205 | num.toExponential(2) // "1.23e+5"
206 | ```
207 |
208 |
209 |
210 | ---
211 |
212 | ## `Object`
213 |
214 | ```js{hide|1,2,3,17|1,2,3,17,19|1,2,3,17,20|1,2,3,17,21|1,2,3,17,20-22|1,2,3,17,20-23|1,2,3,17,20-24|1,4-6,17|1,7-9,17|1,4-6,7-9,17|1,10,17|1,2,11-16,17|all}{lines:true}
215 | let obj = {
216 | name: 'Alice',
217 | age: 18,
218 | sayHello: function() {
219 | console.log('Hello, my name is ' + this.name);
220 | },
221 | sayHello2() { // 简写语法
222 | console.log('Hello, my name is ' + this.name);
223 | },
224 | ['key' + '1']: 'value1', // 方括号里支持 JavaScript 表达式
225 | get info() {
226 | return this.name + ' ' + this.age;
227 | }, // getter, 计算属性
228 | set setName(name) {
229 | this.name = name;
230 | } // setter
231 | }
232 |
233 | obj.name // 'Alice'
234 | obj.age.length // undefined
235 | "xxx" in obj // false
236 | obj.xxx.length // Error
237 | obj?.xxx?.length // 可空属性访问,undefined
238 | obj?.xxx?.length ?? 0 // 默认值
239 | ```
240 |
241 | ---
242 |
243 | ## `Array`
244 |
245 | ::my
246 | ::
247 |
248 |
249 | ⚠ **数组是一种对象**,其下标是一种特殊的**对象属性**,这可以解释很多JavaScript数组与其他语言不同的语法特性。
250 |
251 | ```js{hide|1-2|1-4|1-5|1-6|8|8-10|12|14|14-15|all}{lines:true}
252 | let arr = [1, , , 2]
253 | arr.length // 4
254 |
255 | arr[0] // 1
256 | arr[-1] // undefined
257 | arr[10] // undefined
258 |
259 | arr = new Array(10)
260 | arr.length // 10
261 | arr[0] // undefined
262 |
263 | Array.of(1, 2, 3) // [1, 2, 3]
264 |
265 | Array.from('hello') // ['h', 'e', 'l', 'l', 'o']
266 | [...'hello'] // the same
267 | ```
268 |
269 | ---
270 |
271 | ## `Array`的就地操作
272 |
273 | ::my
274 | ::
275 |
276 |
277 |
278 | - `push(item)`: 末尾添加元素。
279 | - `pop()`: 删除并返回末尾元素。
280 |
281 | 使用`push`和`pop`可以模拟栈。
282 |
283 | - `unshift(item)`: 开头添加元素。
284 | - `shift()`: 删除并返回开头元素。
285 |
286 | 使用`push`和`shift`可以模拟队列。
287 |
288 | - `splice(loc)`:删除从索引`loc`开始的所有元素,返回被删除的元素数组。
289 | - `splice(loc, count)`:切出从索引`loc`开始的`count`个元素,返回被删除的元素数组。
290 | - `splice(loc, count, ...items)`: 在切出元素的基础上,在`loc`位置插入元素。
291 |
292 | ⚠ 这几个方法都是对数组的**就地操作**,会改变原数组但不会返回原数组。
293 |
294 |
295 |
296 | ---
297 |
298 | ## `Array`的其他操作
299 |
300 | ::my
301 | ::
302 |
303 |
304 |
305 | - `slice(start, end)`: 返回从`start`到`end`(不包括`end`)的片段。
306 |
307 | - `fill(item, start, end)`: 用`item`填充从`start`到`end`(不包括`end`)的片段。
308 |
309 | - `sort(func)`:按照指定规则排序。默认按照字符串字典序排序。
310 |
311 |
312 |
313 | ```js{hide|1|2|1-2|3-6|all}
314 | [1, 2, 3].sort((a, b) => a - b) // [1, 2, 3]
315 | [1, 2, 3].sort((a, b) => b - a) // [3, 2, 1]
316 | [
317 | {name: "Alice", grade: 1},
318 | {name: "Bob", grade: 2}
319 | ].sort((a, b) => a.grade - b.grade)
320 | // [{name: "Alice", grade: 1}, {name: "Bob", grade: 2}]
321 | ```
322 |
323 |
324 |
325 | - `reverse()`:反转数组。
326 |
327 | - `concat(...items)`: 返回新数组,新数组是原数组的副本,并在末尾添加`items`。
328 |
329 | - `join(sep)`: 返回字符串,字符串由原数组的元素组成,元素之间用`sep`分隔。
330 |
331 |
332 |
333 | ---
334 |
335 | ## `Array`的迭代 🗡
336 |
337 | ::my
338 | ::
339 |
340 |
341 |
342 | - `forEach(func)`: 对每个元素调用`func`。
343 |
344 | - `map(func)`: 对每个元素调用`func`,返回新数组。
345 |
346 | - `filter(func)`: 对每个元素调用`func`,返回新数组,其中只包含`func`返回`true`的元素。
347 |
348 | - `reduce(func, init)`: 对每个元素调用`func`,`func`的返回值作为下一次调用`func`时的第一个参数,`init`是第一次调用`func`时的第一个参数。
349 |
350 |
351 |
352 |
353 |
354 | ````md magic-move
355 |
356 | ```js
357 | // 生成一个长度为20的列表
358 | new Array(20)
359 | ```
360 |
361 |
362 | ```js
363 | // 生成一个1~20的列表
364 | new Array(20).fill(0)
365 | .map((_, idx) => idx + 1)
366 | ```
367 |
368 | ```js
369 | // 生成一个1~20的列表,筛选出其中的奇数
370 | new Array(20).fill(0)
371 | .map((_, idx) => idx + 1)
372 | .filter(x => x % 2 != 0)
373 | ```
374 |
375 | ```js
376 | // 生成一个1~20的列表,筛选出其中的奇数,然后求和
377 | new Array(20).fill(0)
378 | .map((_, idx) => idx + 1)
379 | .filter(x => x % 2 != 0)
380 | .reduce((a, b) => a + b, 0)
381 | ```
382 | ````
383 |
384 |
385 |
386 | ---
387 |
388 | ## `Array`的迭代 🗡
389 |
390 | ::my
391 | ::
392 |
393 |
394 |
395 | - `every(func)`: 对每个元素调用`func`,如果所有`func`都返回`true`,则返回`true`。
396 |
397 | - `some(func)`: 对每个元素调用`func`,如果任意一个`func`返回`true`,则返回`true`。
398 |
399 | - `find(func)`: 对每个元素调用`func`,返回第一个`func`返回`true`的元素。
400 |
401 | - `findIndex(func)`: 对每个元素调用`func`,返回第一个`func`返回`true`的元素的索引。
402 |
403 | - 其它数组方法:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
404 |
405 |
406 |
407 |
408 | ---
409 |
410 |
411 |
412 | ## `Set`
413 |
414 | 与数组不同的是,集合**没有索引或顺序,也不允许重复**:一个值要么是集合的成员,要么不是;不可能存在一个值在一个集合中出现多次。
415 |
416 | ```js{hide|1|all}
417 | let set = new Set([1, 2, 3])
418 | set.add(4)
419 | set.delete(2)
420 | set.has(3) // true
421 | set.size // 2
422 | ```
423 |
424 | ::my
425 | ::
426 |
427 | ## `Map`
428 |
429 | `Map`对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。
430 |
431 | ```js{hide|1|1-3|all}
432 | let map = new Map()
433 | map.set('name', 'Alice')
434 | map.set('age', 20)
435 | map.get('name') // 'Alice'
436 | map.has('age') // true
437 | map.delete('age')
438 | map.size // 1
439 | ```
440 |
441 |
442 |
443 | ---
444 |
445 | ## `Array`, `Set`, `Map`, `Object`之间的转换
446 |
447 | ::my
448 | ::
449 |
450 | ```mermaid
451 | graph LR
452 | A[Array]
453 | C[Array with entries]
454 | D[Object]
455 | E[Set]
456 | F[Map]
457 |
458 | D --> |Object.keys/values| A
459 | D --> |Object.entries| C
460 | C --> |new Map| F
461 | F --> |"Array.from"| C
462 | F --> |"[...map.keys/values]"| A
463 | F --> |Object.fromEntries| D
464 | C --> |Object.fromEntries| D
465 | A --> |new Set| E
466 | E --> |Array.from| A
467 | ```
468 |
469 |
470 | ---
471 |
472 |
473 | ## `RegExp`
474 |
475 |
476 |
477 | 学习正则表达式:[Geek Hour](https://www.youtube.com/watch?v=uPBtum7QRvw)
478 |
479 | ```js{hide|1|1-2|1-3|5|5-6|5-7|5-9|5-10|all}
480 | // `String.search`: 返回第一个匹配项起点的位置,或-1.
481 | "JavaScript".search(/script/ui) // => 4
482 | "Python".search(/script/ui) // => -1
483 |
484 | // `String.replace`: 按正则替换字符串,支持捕获组和命名捕获组。
485 | let quote = /"([^"]*)"/g // 一个引号 + 任意多个非引号字符 + 引号
486 | 'He said "stop"'.replace(quote, '$1
') // => 'He said stop
'
487 |
488 | let quote = /"(?[^"]*)"/g
489 | 'He said "stop"'.replace(quote, '$
') // => 'He said stop
'
490 | ```
491 |
492 | `replace`传入函数的高级用法:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#specifying_a_function_as_the_replacement
493 |
494 |
495 | ```js{hide|5|1-4|all}
496 | function replacer(match, p1, p2, p3, offset, string, groups) {
497 | // p1 is non-digits, p2 digits, and p3 non-alphanumerics
498 | return [p1, p2, p3].join(" - ");
499 | }
500 | const newString = "abc12345#$*%".replace(/([^\d]*)(\d*)([^\w]*)/, replacer); // abc - 12345 - #$*%
501 | ```
502 |
503 |
504 |
505 | ---
506 |
507 | ```js{hide|1|1-2|1-3|1-4|1,6|7-10|all}
508 | // `String.match`: 返回一个数组,数组中包含所有匹配项(如果有g标识),或第一个匹配项的详细信息(如果无g标识)。
509 | let nums = "12345678"
510 | nums.match(/\d/g) // => ["1", "2", "3", "4", "5", "6", "7", "8"]
511 | nums.match(/\d/) // => ["1", index: 0, input: "12345678", groups: undefined]
512 |
513 | // `String.matchAll`:适用于循环遍历所有匹配项,必须带g标识。
514 | [...nums.matchAll(/\d/g)] // => 0: ['1', index: 0, input: '12345678', groups: undefined]
515 | // 1: ['2', index: 1, input: '12345678', groups: undefined]
516 | // ...
517 | // 7: ['8', index: 7, input: '12345678', groups: undefined]
518 | ```
519 |
520 |
521 |
522 | ⚠️ **会被g标志影响的方法**:`String.match()`, `String.replace()`。
523 |
524 | ⚠️ **不会被g标志影响的方法**:`String.search()`
525 |
526 | ⚠️ **必须带g标志的方法**:`String.matchAll()`。
527 |
528 | ::my
529 | ::
530 |
531 | `RegExp`的方法:
532 |
533 | `test()`: 返回一个布尔值,表示当前模式是否能匹配参数字符串。
534 |
535 | `exec()`: 始终返回一个匹配项。每次匹配后会更新搜索起点。
536 |
537 |
538 |
539 | ---
540 |
541 | ## `Date`
542 |
543 | ```js{1|2-5|6|7|8|9|all}
544 | let date = new Date()
545 | date.getFullYear() // => 2025
546 | date.getMonth() // => 0 (0表示1月)
547 | date.getDate() // => 1
548 | date.getDay() // => 0 (0表示星期日)
549 | date.getTime() // => 1704128000000 (时间戳,毫秒)
550 | date.setFullYear(2020) // => 1577836800000
551 | date.setMonth(date.getMonth() + 1) // 可进行计算
552 | date > new Date(2020, 0, 1) // => true,可进行比较
553 | ```
554 |
555 | 更多用法:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
556 |
--------------------------------------------------------------------------------
/slides/Vue3-API-export.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaojunran/lanqiao-web-slides/ab4a21d2b387ee1a6e1f7514571d3760d65c4016/slides/Vue3-API-export.pdf
--------------------------------------------------------------------------------
/slides/Vue3-API.md:
--------------------------------------------------------------------------------
1 | ---
2 | # You can also start simply with 'default'
3 | theme: ../theme
4 | # random image from a curated Unsplash collection by Anthony
5 | # like them? see https://unsplash.com/collections/94734566/slidev
6 | background: https://images.unsplash.com/photo-1621237023000-6a628c285938?q=80&w=3270&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D
7 | # some information about your slides (markdown enabled)
8 | title: Vue3
9 | info: |
10 | Created with [Sli.dev](https://sli.dev)
11 | # apply unocss classes to the current slide
12 | class: text-center
13 | # https://sli.dev/features/drawing
14 | drawings:
15 | persist: false
16 | # slide transition: https://sli.dev/guide/animations.html#slide-transitions
17 | transition: fade-out
18 | # enable MDC Syntax: https://sli.dev/features/mdc
19 | mdc: true
20 |
21 | ---
22 |
23 | # Vue3 API { .!text-yellow-100}
24 |
25 | 蓝桥杯Web组 省赛备赛 {.!text-white/50}
26 |
27 |
28 | ---
29 | layout: two-cols
30 | ---
31 |
32 | # 浏览器API
33 |
34 | + CSS选择器
35 |
36 | + 文档结构与遍历
37 |
38 | + 属性
39 |
40 | + 元素内容
41 |
42 | + 操作节点
43 |
44 | + 操作样式
45 |
46 | ::right::
47 |
48 | # Vue3
49 |
50 |
51 |
52 | + 模板语法
53 |
54 | + 响应式API
55 |
56 | + 生命周期API
57 |
58 | + 组件API
59 |
60 |
61 |
62 |
63 |
64 | ---
65 |
66 | ## Vue3学习最佳资源
67 |
68 | 官方文档:https://vuejs.org
69 |
70 |  {.!w-7/8}
71 |
72 | ---
73 |
74 | # 模板语法
75 |
76 | Vue 使用基于 HTML 的模板语法,允许您**声明性**地将渲染的 DOM 绑定到底层组件实例的数据。所有 Vue 模板都是语法有效的 HTML,可以被符合规范的浏览器和 HTML 解析器解析。
77 |
78 |
79 |
80 | 1. 数据绑定
81 |
82 | ```vue
83 |
84 | Message: {{ msg.toUpperCase() }}
85 |
86 | ```
87 |
88 | 2. 属性绑定
89 |
90 | ```vue
91 |
94 |
95 |
96 | Message: {{ msg }}
97 |
98 | ```
99 |
100 |
101 |
102 | ---
103 |
104 |
105 |
106 | 3. 多属性绑定
107 |
108 | ```vue
109 |
115 |
116 |
117 |
118 |
119 | ```
120 |
121 | 4. 完整的指令语法
122 |
123 |  {.!w-1/2}
124 |
125 | 详见:https://vuejs.org/guide/essentials/template-syntax.html
126 |
127 |
128 |
129 | ---
130 |
131 |
132 |
133 | 5. 条件渲染(操作DOM)
134 |
135 | ```vue
136 |
137 | Vue is awesome!
138 | Oh no 😢
139 |
140 | ```
141 |
142 | 6. 条件渲染(控制`display`)
143 |
144 | ```vue
145 |
146 | Vue is awesome!
147 |
148 | ```
149 |
150 | 详见:https://vuejs.org/guide/essentials/conditional.html
151 |
152 | 7. 列表渲染
153 |
154 | ```vue
155 |
156 |
157 | -
158 | {{ item.text }}
159 |
160 |
161 |
162 | ```
163 |
164 | 详见:https://vuejs.org/guide/essentials/list.html
165 |
166 |
167 |
168 | ---
169 |
170 | 8. 事件绑定
171 |
172 | ```vue{none|1-3|5-8|10-14|all}
173 |
174 |
175 | Count is: {{ count }}
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 | ```
184 | ```vue{none|all}
185 |
194 | ```
195 |
196 | 详见:https://vuejs.org/guide/essentials/event-handling.html
197 |
198 | ---
199 |
200 |
201 |
202 | 9. `class`的绑定
203 |
204 | ```vue
205 |
206 |
207 |
208 | ```
209 |
210 | ```vue
211 |
212 |
213 |
214 | ```
215 |
216 | 10. `style`的绑定
217 |
218 | ```vue
219 |
223 |
224 |
225 |
226 |
227 | ```
228 |
229 |
230 | 11. 双向绑定
231 |
232 | ```vue
233 | Message is: {{ message }}
234 |
235 | ```
236 |
237 |
238 |
239 |
240 |
241 |
242 | ---
243 |
244 | # 响应式API
245 |
246 |
247 |
248 | 📢 不要忘记导入Vue的API!
249 |
250 | 1. `setup()`和`
268 | ```
269 |
270 | 使用SFC(单文件组件)时,可以使用`
297 | ```
298 |
299 |
300 | ```vue
301 |
318 | ```
319 | ````
320 |
321 | `ref`**可以持有任何值类型**,包括对象、数组或 JavaScript 内置数据结构如 `Map` 。`ref`**自带深度响应性**,可以观测嵌套的对象。
322 |
323 | `reactive`本身就是一个响应式对象,对于初学者来说由于其有使用限制,所有情景下都使用`ref`即可。
324 |
325 | 详见:https://vuejs.org/guide/essentials/reactivity-fundamentals.html#deep-reactivity
326 |
327 |
328 |
329 | ---
330 |
331 | 3. 计算属性
332 |
333 |
334 |
335 | ```vue{none|1-11,19-22|all}{lines:true}
336 |
353 |
354 |
355 | Has published books:
356 | {{ publishedBooksMessage }}
357 |
358 | ```
359 |
360 | 计算属性**自动跟踪**其响应式依赖,默认**只读**。详见https://vuejs.org/guide/essentials/computed.html。
361 |
362 |
363 |
364 | ---
365 |
366 | 4. 侦听器
367 |
368 | 计算属性允许我们**声明式地计算派生值**。然而,在某些情况下,我们需要在**状态变化时执行“副作用”**,例如修改 DOM,或根据异步操作的结果更改另一部分状态。详见:https://vuejs.org/guide/essentials/watchers.html。
369 |
370 | ```vue{hide|all}{maxHeight:'400px'}
371 |
394 |
395 |
396 |
397 | Ask a yes/no question:
398 |
399 |
400 | {{ answer }}
401 |
402 |
403 | ```
404 |
405 | ---
406 |
407 | # 组件API
408 |
409 |  {.!w-1/2}
410 |
411 | ## 1. 属性传递 {.!mt-2}
412 |
413 | ````md magic-move
414 | ```vue{hide|3|4-5|all}
415 |
424 | ```
425 |
426 | ```vue
427 |
437 | ```
438 | ````
439 |
440 | 详见:https://vuejs.org/guide/components/props.html
441 |
442 | ---
443 |
444 | ## 2. 事件传递
445 |
446 |
447 |
448 | ````md magic-move
449 | ```vue
450 |
451 |
452 | ```
453 |
454 | ```vue
455 |
456 |
457 | ```
458 |
459 | ```vue
460 |
461 |
462 | ```
463 | ````
464 |
465 | 在`setup`函数中声明事件:
466 |
467 | ````md magic-move
468 | ```js{all|2|3,5|all}
469 | export default {
470 | emits: ['inFocus', 'submit'],
471 | setup(props, ctx) {
472 | ctx.emit('submit')
473 | }
474 | }
475 | ```
476 |
477 | ```js
478 | export default {
479 | emits: ['inFocus', 'submit'],
480 | setup(props, { emit }) {
481 | emit('submit')
482 | }
483 | }
484 | ```
485 | ````
486 |
487 | 详见:https://vuejs.org/guide/components/events.html
488 |
489 |
490 |
491 | ---
492 |
493 | ## 生命周期API
494 |
495 | 例如,可以使用 onMounted 钩子在组件完成初始渲染并创建 DOM 节点后运行代码:
496 |
497 | ````md magic-move
498 | ```vue
499 |
507 | ```
508 |
509 | ```vue
510 |
520 | ```
521 | ````
522 |
523 | 详见:https://vuejs.org/guide/built-ins/lifecycle.html
524 |
525 |
526 | ---
527 |
528 | ## 这里略去不讲,但你应该了解的话题
529 |
530 |
531 |
532 | 1. **可写的计算属性**:https://vuejs.org/guide/essentials/computed.html#writable-computed
533 |
534 | 2. **表单输入的值绑定**:https://vuejs.org/guide/essentials/forms.html#value-bindings
535 |
536 | 3. **侦听器的其它用法:深度侦听、立即侦听、`watchEffect`**:https://vuejs.org/guide/essentials/watchers.html#watchers
537 |
538 | 4. **模板引用**:https://vuejs.org/guide/essentials/template-refs.html
539 |
540 | 5. **定义`model`**:https://vuejs.org/guide/components/v-model.html
541 |
542 | 6. **定义`slots`**:https://vuejs.org/guide/components/slots.html
543 |
544 | 7. **依赖注入**:https://vuejs.org/guide/components/provide-inject.html
545 |
546 | 8. **逻辑复用**:https://vuejs.org/guide/reusability/composables.html
547 |
548 |
--------------------------------------------------------------------------------
/slides/components/My.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/slides/components/Ques.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 | {{ name }}
8 |
9 |
13 |
{{ year }}年
14 |
15 |
16 |
![]()
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/slides/components/Tag.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ props.name }}
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/slides/guide.md:
--------------------------------------------------------------------------------
1 | ---
2 | # You can also start simply with 'default'
3 | theme: dracula
4 | # random image from a curated Unsplash collection by Anthony
5 | # like them? see https://unsplash.com/collections/94734566/slidev
6 | background: https://images.unsplash.com/photo-1621237023000-6a628c285938?q=80&w=3270&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D
7 | # some information about your slides (markdown enabled)
8 | title: 蓝桥杯Web组 备赛指南
9 | info: |
10 | Created with [Sli.dev](https://sli.dev)
11 | # apply unocss classes to the current slide
12 | class: text-center
13 | # https://sli.dev/features/drawing
14 | drawings:
15 | persist: false
16 | # slide transition: https://sli.dev/guide/animations.html#slide-transitions
17 | transition: slide-left
18 | # enable MDC Syntax: https://sli.dev/features/mdc
19 | mdc: true
20 | ---
21 |
22 | # 蓝桥杯Web组 备赛指南 {.!text-yellow-100 }
23 |
24 | 2025年 省赛 { .!text-white/50 .text-2xl .!mt-8 }
25 |
26 |
27 | Press Space for next page
28 |
29 |
30 |
38 |
39 | ---
40 |
41 | ## 比赛基本信息 📢
42 |
43 |
44 |
45 | - 蓝桥杯Web组从2022年开始开设,已经举办**三届**。
46 |
47 | - 比赛共设**大学组、职业院校组**两个组别,两个组别约有一半的题目是一样的。
48 |
49 | - 省赛比赛时间**2025年4月**。
50 |
51 | - 省赛比赛共**10道题**,共**4小时**,开赛后一小时可以交卷。
52 |
53 | - 省赛每个组别设立一、二、三等奖,原则上各奖项的比例为**10%、20%、30%**。
54 |
55 | - 全部题目将使用前端自动化测试技术完成机器**自动评分**。
56 |
57 |
58 |
59 |
60 |
61 | 详情:https://dasai.lanqiao.cn/notices/839/
62 |
63 |
64 |
65 |
66 | ---
67 |
68 | ## 比赛细节 👀
69 |
70 |
71 |
72 | - 考试开始后,选手首先下载题目,并使用考场现场公布的解压密码解压试题。
73 |
74 | - 题面以PDF的形式给出,基础源代码会以压缩包的形式给出。考生需要把作答完成的代码重新**打成压缩包**提交到指定位置。
75 |
76 | - 作答期间**无互联网连接**,而且大概率评判题目期间也不会有互联网连接,所以不要引用除本地文件以外的资源。
77 |
78 | - 本地开发环境:**Chrome、Visual Studio Code、Node.js**。
79 |
80 | - 比赛环境中的VS Code可以提供**HTML标签**补全、**CSS样式**补全、**JavaScript基本语法**补全、**浏览器JavaScript API**补全、**Nodejs API**补全。
81 |
82 | - 比赛环境中的VS Code不能提供**jQuery(已从考纲中移除)**、**Axios**、**Vue3**、**Element Plus**、**ECharts**等库的补全,如果考察Element Plus,一般会给出用法文档。
83 |
84 | - 每道题目都有实际情境,无论是封装函数还是实现页面,都可以直观地自行判断是否成功实现功能。
85 |
86 |
87 |
88 | ---
89 |
90 | ## 比赛内容和难度 📚
91 |
92 |
93 |
94 | - 比赛主要围绕着前端生态系统进行考察。
95 |
96 | - 历届比赛的题目难度大体上**逐年递增**。
97 |
98 | - Web组的整体难度**远低于**算法组,需要做对一半以上的题才能考虑省三。
99 |
100 | - 和算法组相比,Web组的**知识点更多、涉及范围更广**,需要在赛前多积累API的使用经验。
101 |
102 | - 通常每年的考纲会有细微的不同,但今年的考纲和去年相比**没有任何变化**。
103 |
104 | - 这两年的考纲删除了:**jQuery**;将考察Vue2调整为**考察Vue3**;将考察ElementUI调整为**考察Element Plus**。
105 |
106 |
107 |
108 | ---
109 | # layout: two-cols
110 | ---
111 |
112 | ## 考纲详细内容 🌏
113 |
114 |
115 |
116 | **1. HTML5**
117 | - HTML 基本标签
118 | - HTML5 新特性
119 | - HTML5 本地存储
120 |
121 | **2. CSS3**
122 | - CSS 基础语法
123 | - 盒子模型
124 | - 浮动与定位
125 | - CSS3 新特性
126 | - 弹性盒子
127 | - 媒体查询
128 |
129 | **3. JavaScript**
130 | - JavaScript 基础语法
131 | - DOM 与 BOM
132 | - JavaScript 内置对象
133 | - JavaScript 事件
134 | - JavaScript AJAX
135 | - 正则表达式
136 |
137 | **4. ES6**
138 | - let 和 const 命令
139 | - class
140 | - set 和 map
141 | - Proxy
142 | - 字符串、函数、数组和对象的扩展
143 | - 异步编程与模块化
144 |
145 | **5. Axios**
146 | - Axios API
147 | - Axios 实例
148 | - 请求配置
149 | - 默认配置
150 | - 拦截器
151 |
152 | **6. Vue.js**
153 | - Vue 核心语法
154 | - Vue 组件化开发
155 | - vue-router (v4.x)
156 | - pinia 使用
157 |
158 | **7. ElementPlus**
159 | - 基础组件的使用
160 | - 表单和表格组件
161 | - 反馈组件
162 | - 导航组件
163 | - 组件的二次封装
164 |
165 | **8. ECharts**
166 | - ECharts 基础语法
167 | - ECharts 绘制图表
168 | - ECharts 异步数据加载和更新
169 | - ECharts 交互组件
170 | - ECharts 事件处理
171 |
172 | **9. Node.js(仅大学组考察)**
173 | - Node.js 基础
174 | - 内置模块使用 (fs、http 等)
175 |
176 |
177 |
178 | ---
179 |
180 | ## 注意事项
181 |
182 |
183 |
184 | 1. 历届题目有很多相似之处,你经常能在某道题中看到前几届赛题的影子,所以**务必刷完**✍🏻前几届的题目!
185 |
186 | 2. 赛题和实际开发的差距比较大,例如完全不考察**TypeScript**、**Vue SFC**等、也完全不考察构建打包工具,几乎每道题都是填空题,个别题目甚至只需要你填几行代码,要熟悉题目的考察方式!
187 |
188 | 3. 绝大多数的题目都只明确地**考察一个知识点**(考CSS的题就不会考你JS),不要看到题目最后的效果觉得很难做就轻易放弃,很多题目的效果是给你校验作答是否正确用的,不是需要你自己实现的!
189 |
190 | 4. 蓝桥杯**不是在线评测**!你交卷的时候不知道自己得了多少分!因此一定要注意审题👀,避免“会而不得分”的情况。
191 |
192 | 5. 不要买蓝桥杯官方的课😂。
193 |
194 |
195 |
196 | ---
197 |
198 | ## 视频内容规划
199 |
200 |
201 |
202 | 1. 带练**前三届**省赛大学组、职业院校组**省赛真题**14 + 12 + 14 = **40**道题。
203 |
204 | 2. 精选国赛中经典的、值得借鉴的题目进行讲解。
205 |
206 | 3. 总结和拓展考纲中的知识点、考察点。
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 | Github: https://github.com/gaojunran/lanqiao-web-slides
216 |
217 | Bilibili: CodeNebula
218 |
219 |
--------------------------------------------------------------------------------
/slides/public/Vue3-doc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaojunran/lanqiao-web-slides/ab4a21d2b387ee1a6e1f7514571d3760d65c4016/slides/public/Vue3-doc.png
--------------------------------------------------------------------------------
/slides/public/background.avif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaojunran/lanqiao-web-slides/ab4a21d2b387ee1a6e1f7514571d3760d65c4016/slides/public/background.avif
--------------------------------------------------------------------------------
/slides/public/js-doc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaojunran/lanqiao-web-slides/ab4a21d2b387ee1a6e1f7514571d3760d65c4016/slides/public/js-doc.png
--------------------------------------------------------------------------------
/slides/public/全球新冠疫情数据统计.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaojunran/lanqiao-web-slides/ab4a21d2b387ee1a6e1f7514571d3760d65c4016/slides/public/全球新冠疫情数据统计.gif
--------------------------------------------------------------------------------
/slides/public/指令语法.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaojunran/lanqiao-web-slides/ab4a21d2b387ee1a6e1f7514571d3760d65c4016/slides/public/指令语法.png
--------------------------------------------------------------------------------
/slides/public/灯的颜色变化.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaojunran/lanqiao-web-slides/ab4a21d2b387ee1a6e1f7514571d3760d65c4016/slides/public/灯的颜色变化.gif
--------------------------------------------------------------------------------
/slides/public/组件树.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaojunran/lanqiao-web-slides/ab4a21d2b387ee1a6e1f7514571d3760d65c4016/slides/public/组件树.png
--------------------------------------------------------------------------------
/slides/public/课程列表.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaojunran/lanqiao-web-slides/ab4a21d2b387ee1a6e1f7514571d3760d65c4016/slides/public/课程列表.gif
--------------------------------------------------------------------------------
/slides/专题-异步编程.md:
--------------------------------------------------------------------------------
1 | ---
2 | # You can also start simply with 'default'
3 | theme: ../theme
4 | # random image from a curated Unsplash collection by Anthony
5 | # like them? see https://unsplash.com/collections/94734566/slidev
6 | background: https://images.unsplash.com/photo-1621237023000-6a628c285938?q=80&w=3270&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D
7 | # some information about your slides (markdown enabled)
8 | title: 专题:异步编程
9 | info: |
10 | Created with [Sli.dev](https://sli.dev)
11 | # apply unocss classes to the current slide
12 | class: text-center
13 | # https://sli.dev/features/drawing
14 | drawings:
15 | persist: false
16 | # slide transition: https://sli.dev/guide/animations.html#slide-transitions
17 | transition: fade-out
18 | # enable MDC Syntax: https://sli.dev/features/mdc
19 | mdc: true
20 | ---
21 |
22 | # 专题:异步编程 { .!text-yellow-100}
23 |
24 | 蓝桥杯Web组 省赛备赛 {.!text-white/50}
25 |
26 | ---
27 |
28 | # 异步编程
29 |
30 |
31 |
32 | 1. 使用回调的异步编程
33 |
34 | 2. 期约(`Promise`)
35 |
36 | 3. `async`/`await`
37 |
38 |
39 |
40 | ---
41 |
42 |
43 |
44 | # 使用回调的异步编程
45 |
46 | 回调就是函数,可以传给其它函数。其它函数会在满足一定条件(或发生一定事件)后,调用这个函数。
47 |
48 |
49 |
50 |
51 |
52 | ## 定时器
53 |
54 | `setTimeout`:
55 | ```js
56 | const ding = () => console.log('dong');
57 |
58 | setTimeout(ding, 1000);
59 | ```
60 |
61 | `setInterval`:
62 | ````md magic-move
63 |
64 | ```js
65 | const beep = () => console.log('beep');
66 |
67 | setInterval(beep, 1000);
68 | ```
69 |
70 | ```js
71 | const beep = () => console.log('beep');
72 |
73 | let intervalId = setInterval(beep, 1000);
74 | ```
75 |
76 | ```js
77 | let count = 0;
78 | const beep = () => {
79 | count++;
80 | console.log('beep');
81 | if (count === 3) clearInterval(intervalId);
82 | }
83 |
84 | let intervalId = setInterval(beep, 1000);
85 | ```
86 | ````
87 |
88 |
89 |
90 | ---
91 |
92 | # 定时器
93 |
94 | 注意:`setTimeout`不是真正的等待时间,而是只是一个几秒后执行任务的**回调钩子**。
95 |
96 |
97 |
98 | ---
99 |
100 | # 事件
101 |
102 | 客户端JavaScript几乎全都是事件驱动的。等待用户做一些事,然后响应用户的动作。
103 |
104 |
105 |
106 | 事件监听器是通过`addEventListener`来添加的。
107 |
108 | ```js{hide|1|all}
109 | let okey = document.querySelector('#okey');
110 |
111 | okey.addEventListener('click', () => {
112 | console.log('clicked');
113 | });
114 | ```
115 |
116 | **思考**:如何绑定一个事件?
117 |
118 | ````md magic-move
119 | ```vue
120 |
121 |
122 |
123 | ```
124 |
125 | ```html
126 |
127 |
128 |
129 | ```
130 |
131 |
132 | ```vue
133 |
134 |
135 |
136 | ```
137 | ````
138 |
139 |
140 |
141 | ---
142 |
143 | # `defineEmits`
144 |
145 | https://vuejs.org/guide/components/events.html
146 |
147 |
148 |
149 | **思考**:Vue3中如何声明一个组件将发出的事件?
150 |
151 | ````md magic-move
152 | ```vue
153 |
154 |
155 | ```
156 |
157 | ```vue
158 |
159 |
160 | ```
161 |
162 | ```vue
163 |
164 |
165 | ```
166 | ````
167 |
168 | 在`setup`函数中声明事件:
169 |
170 | ````md magic-move
171 | ```js{all|2|3,5|all}
172 | export default {
173 | emits: ['inFocus', 'submit'],
174 | setup(props, ctx) {
175 | ctx.emit('submit')
176 | }
177 | }
178 | ```
179 |
180 | ```js
181 | export default {
182 | emits: ['inFocus', 'submit'],
183 | setup(props, { emit }) {
184 | emit('submit')
185 | }
186 | }
187 | ```
188 | ````
189 |
190 | 在`
197 | ```
198 |
199 | ```vue
200 |
205 | ```
206 |
207 | ```ts
208 | // TypeScript & Vue3.3+
209 | const emit = defineEmits<{
210 | inFocus: [id: number]
211 | submit: [value: string]
212 | }>()
213 | ```
214 |
215 | ````
216 |
217 |
218 |
219 | ---
220 |
221 | ## 调用事件处理程序
222 |
223 |
224 | ```js{none|1-4|all}
225 | // 箭头函数 + event参数,拿到事件对象
226 | ele.addEventListener('click', (event) => {
227 | console.log(event.target);
228 | })
229 |
230 | // function函数表达式,使用this拿到事件对象
231 | ele.addEventListener('click', function() {
232 | console.log(this);
233 | })
234 | ```
235 |
236 |
237 | ---
238 |
239 | # Node.js中的回调
240 |
241 | ## `fs`模块
242 |
243 | ```js{none|1|2-5|6-18|all}
244 | const fs = require("fs"); // "fs"模块有文件系统相关的API
245 | let options = {
246 | // 保存程序选项的对象
247 | // 默认选项可以写在这里
248 | };
249 |
250 | // 读取配置文件,然后调用回调函数
251 | fs.readFile("config.json", "utf-8", (err, text) => {
252 | if (err) {
253 | // 如果有错误,显示一条警告消息,但仍然继续
254 | console.warn("Could not read config file:", err);
255 | } else {
256 | // 否则,解析文件内容并赋值给选项对象
257 | Object.assign(options, JSON.parse(text));
258 | }
259 |
260 | startProgram(options);
261 | });
262 | ```
263 |
264 | ---
265 |
266 | ## `https`模块
267 |
268 | ```js{none|1|3-4|5-22|6-11|13|15-21|all}{lines:true}
269 | const https = require("https");
270 |
271 | function getText(url, callback) {
272 | request = https.get(url);
273 | request.on("response", response => { // 处理 "response" 事件
274 | // 此时收到了响应头
275 | let httpStatus = response.statusCode;
276 |
277 | // 此时并没有收到 HTTP 响应体,因此还要再注册几个事件处理程序,以便收到响应体时被调用
278 | response.setEncoding("utf-8");
279 | let body = "";
280 |
281 | response.on("data", chunk => { body += chunk; }); // 每个响应体块就绪时
282 |
283 | response.on("end", () => { // 响应完成时
284 | if (httpStatus === 200) {
285 | callback(null, body); // 把响应体传给回调
286 | } else {
287 | callback(httpStatus, null); // 否则传递错误
288 | }
289 | });
290 | });
291 | }
292 | ```
293 |
294 | 思考:这个复杂的代码里一共有几个回调😂?
295 |
296 |
297 | ---
298 |
299 | # 期约(`Promise`)
300 |
301 | 期约是一个对象,表示异步操作的结果。
302 |
303 |
304 |
305 | 期约有一个最重要的优点,就是以线性`then`方法调用链的形式表达一系列异步的操作,而不用把每个操作嵌套在前一个操作的回调内部。
306 |
307 | ## 使用Fetch API
308 |
309 | ```js
310 | fetch("/api/user/profile").then(response => { // response: Response
311 | response.json().then(profile => { // profile: JSON object
312 | data.value = profile; // 在Vue3的onMounted钩子里更新数据
313 | })
314 | })
315 | ```
316 |
317 | ````md magic-move
318 |
319 | ```js
320 | fetch("/api/user/profile")
321 | .then(response => {
322 | return response.json();
323 | })
324 | .then(profile => {
325 | data.value = profile;
326 | })
327 | ```
328 |
329 | ```js
330 | fetch(url)
331 | .then(callback1)
332 | .then(callback2)
333 | ```
334 |
335 | ```js
336 | fetch(url)
337 | .then(callback1, onError1)
338 | .then(callback2, onError2)
339 | ```
340 |
341 |
342 | ```js
343 | fetch(url)
344 | .then(callback1, onError1)
345 | .then(callback2, onError2)
346 | .then(callback3)
347 | .catch(error => { console.error(error); }) // 用于处理Promise链中的错误
348 | .finally(() => { console.log("done"); }); // 用于收尾工作,无论错误与否均会触发
349 | ```
350 | ````
351 |
352 |
353 |
354 | ---
355 |
356 | ## 使用Axios API
357 |
358 | ```js
359 | axios.get("/api/user/profile").then(response => { // response: AxiosResponse
360 | data.value = response.data; // 在Vue3的onMounted钩子里更新数据
361 | })
362 | ```
363 |
364 |
365 |
366 |
367 |
368 |
369 | ---
370 |
371 | # 🎉 `JS Doc`!
372 |
373 | VS Code在不安装插件的情况下,原生对JS Doc提供支持。
374 |
375 | 这意味着对于很多后端开发者,或者习惯TypeScript的前端开发者来说,JavaScript不再缺少类型信息。
376 |
377 |
378 |
379 |  {.w-1/2 .mx-auto .border .border-red-300}
380 |
381 |
382 |
383 | ---
384 |
385 | # `async`和`await`
386 |
387 |
388 |
389 | 允许我们以同步的方式编写异步的代码。
390 |
391 | 我们发现,基于线性`then`的异步代码不能像正常的同步代码一样**返回一个值**或是**抛出一个异常**。
392 |
393 | `await`关键字接收一个期约,将其转换成一个返回值或一个抛出的异常。
394 |
395 |
396 |
397 |
398 |
399 | ```js
400 | fetch("/api/user/profile")
401 | .then(response => {
402 | return response.json();
403 | })
404 | .then(profile => {
405 | data.value = profile;
406 | })
407 | ```
408 |
409 |
410 |
411 |
412 |
413 | ```js
414 | let response = await fetch("/api/user/profile");
415 | let profile = await response.json();
416 | data.value = profile;
417 | ```
418 |
419 |
420 |
421 |
422 |
423 | 📢 注意:只能在`async`定义的函数中使用`await`关键字。
424 |
425 | 把函数标注为`async`意味着它**返回一个期约**,而不是直接返回一个值。
426 |
427 |
428 |
429 | ---
430 |
431 | ## 并行期约
432 |
433 |
434 |
435 | ```js
436 | async function getJSON(url) {
437 | let response = await fetch(url);
438 | let body = await response.json();
439 | return body;
440 | }
441 |
442 | let value1 = await getJSON("/api/user/profile");
443 | let value2 = await getJSON("/api/user/friends");
444 | ```
445 |
446 |
447 | 以上代码的问题在于它本不必顺序执行。要等候一组并发执行的`async`函数,可以使用`Promise.all()`方法。
448 |
449 | ```js
450 | let [value1, value2] = await Promise.all([
451 | getJSON("/api/user/profile"),
452 | getJSON("/api/user/friends")
453 | ]);
454 | ```
455 |
456 |
457 |
458 | ---
459 |
460 | ## 练习:串行期约
461 |
462 | 有这样一组未知数量的`url`数组,为了避免网络过载,你一次只能抓取一个`url`,编写一个函数来构造这样的期约。
463 |
464 | ````md magic-move
465 | ```js
466 | function fetchAll(urls) {
467 | const results = [];
468 | function fetchOne(url) {
469 | // TODO
470 | }
471 | // TODO
472 | }
473 | ```
474 |
475 | ```js{all|3-5|3-8|all}
476 | function fetchAll(urls) {
477 | const results = [];
478 | function fetchOne(url) {
479 | return fetch(url).then(response => response.json());
480 | }
481 | urls.forEach(url => {
482 | results.push(fetchOne(url));
483 | })
484 | return Promise.all(results);
485 | }
486 | ```
487 |
488 | ```js{all|3-9|10|10-13|10-14|all}
489 | function fetchAll(urls) {
490 | const results = [];
491 | function fetchOne(url) {
492 | return fetch(url)
493 | .then(response => response.json())
494 | .then(result => {
495 | results.push(result);
496 | })
497 | }
498 | let p = Promise.resolve(); // 立即兑现的期约
499 | urls.forEach(url => {
500 | p = p.then(() => fetchOne(url));
501 | })
502 | return p.then(() => results);
503 | }
504 | ```
505 | ````
506 |
507 |
508 |
509 | ---
510 |
511 | ## 练习:`async/await`的实现原理
512 |
513 |
514 |
515 | ````md magic-move
516 | ```js
517 | async function f(x) { /* body */ }
518 | ```
519 |
520 | ```js
521 | function f(x) {
522 | return new Promise((resolve, reject) => {
523 | try {
524 | resolve(function(x) { /* body */ }(x));
525 | }
526 | catch (e) {
527 | reject(e);
528 | }
529 | })
530 | }
531 | ```
532 |
533 | ````
534 |
535 |
536 |
--------------------------------------------------------------------------------
/slides/浏览器API-export.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaojunran/lanqiao-web-slides/ab4a21d2b387ee1a6e1f7514571d3760d65c4016/slides/浏览器API-export.pdf
--------------------------------------------------------------------------------
/slides/浏览器API.md:
--------------------------------------------------------------------------------
1 | ---
2 | # You can also start simply with 'default'
3 | theme: ../theme
4 | # random image from a curated Unsplash collection by Anthony
5 | # like them? see https://unsplash.com/collections/94734566/slidev
6 | background: https://images.unsplash.com/photo-1621237023000-6a628c285938?q=80&w=3270&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D
7 | # some information about your slides (markdown enabled)
8 | title: 浏览器API
9 | info: |
10 | Created with [Sli.dev](https://sli.dev)
11 | # apply unocss classes to the current slide
12 | class: text-center
13 | # https://sli.dev/features/drawing
14 | drawings:
15 | persist: false
16 | # slide transition: https://sli.dev/guide/animations.html#slide-transitions
17 | transition: fade-out
18 | # enable MDC Syntax: https://sli.dev/features/mdc
19 | mdc: true
20 |
21 | ---
22 |
23 | # 浏览器API { .!text-yellow-100}
24 |
25 | 蓝桥杯Web组 省赛备赛 {.!text-white/50}
26 |
27 | ---
28 | layout: two-cols
29 | ---
30 |
31 | # 浏览器API
32 |
33 |
34 |
35 | + CSS选择器
36 |
37 | + 文档结构与遍历
38 |
39 | + 属性
40 |
41 | + 元素内容
42 |
43 | + 操作节点
44 |
45 | + 操作样式
46 |
47 |
48 |
49 | ::right::
50 |
51 | # Vue3
52 |
53 |
54 |
55 | + 模板语法
56 |
57 | + 响应式API
58 |
59 | + 生命周期API
60 |
61 | + 组件API
62 |
63 |
64 |
65 | ---
66 |
67 | # CSS选择器
68 |
69 |
70 |
71 | 浏览器提供了 `document.querySelector` 和 `document.querySelectorAll` 两个方法,可以用来根据 CSS 选择器来获取 DOM 元素。
72 |
73 | ### 通过标签名、id、class
74 |
75 | ```js
76 | div
77 | #nav
78 | .warning
79 | ```
80 |
81 | ### 通过属性
82 |
83 | ```js
84 | p[lang="fr"]
85 | *[name="x"]
86 | ```
87 |
88 | ### 组合
89 |
90 | ```js
91 | span.fatal.error // 取交集,同时满足
92 | img, video // 取并集,满足其一
93 | ```
94 |
95 | ```js
96 | #log span // 后代
97 | #log > span // 子代
98 | body > h1 :first-child // 伪类
99 | img + p.caption // 紧邻
100 | img ~ p.caption // 兄弟
101 | ```
102 |
103 |
104 |
105 | ---
106 |
107 | # 练习 —— `空后、大子、加紧跟、浪同辈`
108 |
109 | ```html{all|2-7|2,3,7|2-7|2,3,7|3,7|3,7|5|5,6}{lines:true}
110 |
111 |
112 |
117 |
118 | ```
119 |
120 |
121 | 1. `h2 img`
122 | 2. `h2 > img`
123 | 3. `h2 #p1`
124 | 4. `h2 > #p1`
125 | 5. `img + div`
126 | 6. `img ~ div`
127 | 7. `#p1 + #p3`
128 | 8. `#p1 ~ #p3`
129 |
130 |
131 |
132 | ---
133 |
134 | ## 文档结构与遍历
135 |
136 |
137 |
138 | **`Element`对象**上的方法:
139 |
140 | - `parentElement`
141 | - `children`
142 | - `firstElementChild`
143 | - `lastElementChild`
144 | - `nextElementSibling`
145 | - `previousElementSibling`
146 |
147 | 这些方法都是不带Text节点、Comment节点的,如果想学习**Node对象上定义**的节点树属性,可参阅https://developer.mozilla.org/en-US/docs/Web/API/Node。
148 |
149 |
150 |
151 |
152 | ---
153 |
154 | ## 属性
155 |
156 |
157 |
158 | ### HTML属性 {.!mt-4}
159 |
160 | ```js{hide|1-3|1-7|all}
161 | let image = document.querySelector('#main_img');
162 | let url = image.src;
163 | image.id == 'main_img';
164 |
165 | let f = document.querySelector('#form');
166 | f.action = 'https://example.com';
167 | f.method = 'POST';
168 |
169 | let button = document.querySelector('#submit');
170 | button.onclick = function() { // 更推荐使用`addEventListener`
171 | alert('Button clicked');
172 | }
173 | ```
174 |
175 | 注意:`class`映射到`classList`, `for`映射到`htmlFor`。`classList`是一个可迭代的**类数组对象**,可以使用数组方法来操作。
176 |
177 | ```js{hide|1|1-2|1-4|1-5}
178 | let spinner = document.querySelector('#spinner');
179 | spinner.classList.has('show') === true;
180 | spinner.classList.add('show');
181 | spinner.classList.remove('show');
182 | spinner.classList.toggle('show');
183 | ```
184 |
185 |
186 |
187 | ---
188 |
189 | ## 通用属性管理 {.!mb-4}
190 |
191 |
192 |
193 | - `getAttribute(name)`:获取属性值
194 | - `setAttribute(name, value)`:设置属性值
195 | - `removeAttribute(name)`:移除属性
196 | - `hasAttribute(name)`:检查属性是否存在
197 |
198 |
199 |
200 | ---
201 |
202 | ## 元素内容 {.!mb-4}
203 |
204 | ````md magic-move
205 | ```html{hide|all|2}
206 |
207 | This is the element content.
208 |
209 | ```
210 |
211 | ```html
212 |
213 | Inserted here
214 | This is the element content.
215 |
216 | ```
217 |
218 | ```html
219 |
220 | This is the element content.
221 | Inserted here
222 |
223 | ```
224 |
225 | ```html
226 |
227 | This is the element content.
228 |
229 | ```
230 |
231 | ```html
232 | Inserted here
233 |
234 | This is the element content.
235 |
236 | ```
237 |
238 | ```html
239 |
240 | This is the element content.
241 |
242 | Inserted here
243 | ```
244 |
245 | ````
246 |
247 |
248 | 1. `innerHTML`
249 | 2. `ele.innerHTML = "Inserted here" + ele.innerHTML`
250 | 3. `ele.innerHTML += "Inserted here"`
251 | 4. `outerHTML`
252 | 5. `ele.outerHTML = "Inserted here" + ele.outerHTML`
253 | 6. `ele.outerHTML += "Inserted here"`
254 |
255 |
256 |
257 |
258 |
259 | `textContent`用于获取元素中的纯文本内容,或者向文档中插入纯文本内容。
260 |
261 |
262 |
263 | ---
264 |
265 | ## 创建、插入和删除节点
266 |
267 |
268 |
269 | 使用`document.createElement(tagName)`创建一个新元素;
270 |
271 | 使用`append(node)`, `prepend(node)`, `before(node)`, `after(node)`, `replaceWith(node)`插入元素或文本;
272 |
273 | 使用`remove()`删除元素。
274 |
275 |
276 |
277 | ````md magic-move
278 | ```js{hide|1-2|1,4-5|all}
279 | let para = document.createElement('p');
280 | let img = document.createElement('img');
281 |
282 | para.append("World!")
283 | para.prepend("Hello, ")
284 |
285 | img.src = "https://example.com/image.jpg";
286 | ```
287 |
288 | ```js{1-5|1-6|1-7|all}
289 | let para = document.createElement('p');
290 | let img = document.createElement('img');
291 |
292 | para.after(img);
293 | para.before(img);
294 | para.replaceWith(img);
295 | para.remove(); // 删除自己
296 | para.removeChildren(); // 删除所有子节点;还可以怎样操作?
297 | ```
298 |
299 | ````
300 |
301 |
302 |
303 | ---
304 |
305 | # 操作CSS {.!mb-6}
306 |
307 |
308 |
309 | ## 基于class的样式
310 |
311 | 见[属性 —— classList](6?clicks=11)。
312 |
313 | ## 基于style的样式
314 |
315 | 常用的CSS属性都挂在了`Element.style`上,⚠️ 使用**小驼峰**命名,⚠️ 且**包含单位**。
316 |
317 | ```js{hide|1-3|1-5|all}
318 | function displayAt(tooltip, x, y) {
319 | tooltip.style.left = x + 'px';
320 | tooltip.style.top = y + 'px';
321 | tooltip.style.display = 'block';
322 | tooltip.style.position = 'absolute';
323 | tooltip.style.backgroundColor = 'white';
324 | }
325 | ```
326 |
327 | 如果要对一个带单位的style做数值计算,需要做**两次转换**。
328 |
329 |
330 |
331 | ---
332 |
333 | ## 计算样式
334 |
335 |
336 |
337 | ⚠️ 计算属性是**只读**的,任何指定大小的属性都将以**像素**度量,且**包含单位**。
338 |
339 | ::my
340 | ::
341 |
342 | ```js
343 | let title = document.querySelector('h1');
344 | let fontSize = window.getComputedStyle(title).fontSize;
345 | console.log(fontSize); // "16px"
346 | ```
347 |
348 |
349 |
--------------------------------------------------------------------------------
/snippets/JavaScript/ISBN转换与生成.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2023年省赛。中等难度。
3 | */
4 |
5 | // 将用户输入的带分隔符的 isbn 字符串转换只有纯数字和大写 X 字母的字符串
6 | // 入参 str 为转换为包含任意字符的字符串
7 | function getNumbers(str) {
8 | // TODO: 待补充代码
9 | return str.replace(/[^\dX]/g, "")
10 | }
11 |
12 | // 验证当前 ISBN10 字符串是否有效
13 | // 入参 str 为待判断的只有纯数字和大写 X 字母的字符串
14 | function validISBN10(str) {
15 |
16 | if (!/^\d{9}[\dX]$/.test(str)) { // 检验10位和每位格式
17 | return false;
18 | }
19 |
20 | // if (!/^(\d{9})([\d|X])$/.exec(str)) { // 不建议用match
21 | // return false;
22 | // }
23 |
24 | // if (str.search(/^\d{9}[\dX]$/) === -1) {
25 | // return false;
26 | // }
27 |
28 | // If you need to know if a string matches a regular expression RegExp, use RegExp.prototype.test().
29 | // 如果您需要知道一个字符串是否与正则表达式匹配,请使用 RegExp 。
30 | // If you only want the first match found, you might want to use RegExp.prototype.exec() instead.
31 | // 如果您只想找到的第一个匹配项,您可能想使用 RegExp.prototype.exec() 代替。
32 | // If you want to obtain capture groups and the global flag is set, you need to use RegExp.prototype.exec() or String.prototype.matchAll() instead.
33 | // 如果您想获取捕获组并且全局标志被设置,您需要使用 RegExp.prototype.exec() 或 String.prototype.matchAll() 代替。
34 |
35 | // 字符串转数组:str.split(""), Array.from(str), [...str]
36 | let sum = str.slice(0, 9).split("").reduce((acc, cur, idx) => {
37 | return acc + (idx + 1) * (+cur)
38 | }, 0)
39 | let check = sum % 11 === 10 ? "X" : String(sum % 11);
40 | return check == str[9]
41 | }
42 |
43 |
44 | // 验证当前 ISBN10 字符串是否有效
45 | // 入参 str 为待判断的只有纯数字和大写 X 字母的字符串
46 |
47 | function validISBN102(str) {
48 | const match = /^(\d{9})([\d|X])$/.exec(str);
49 | if (!match) return false;
50 | // 用加号拼成JavaScript表达式再eval;match[1]是第一个匹配结果
51 | let evaled = [...match[1]].map((cur, idx) => `${idx + 1}*${cur}`)
52 | .join("+");
53 | const checkNum = eval(evaled) % 11;
54 | return match[2] === (checkNum === 10 ? "X" : checkNum.toString())
55 | }
56 |
57 |
58 | // 将用户输入的 ISBN-10 字符串转化为 ISBN-13 字符串
59 | // 入参 isbn 为有效的 ISBN-10 字符串
60 | function ISBN10To13(isbn) {
61 | let temp = "978" + isbn.slice(0, 9);
62 | // 注意:奇数位是偶数索引,偶数位是奇数索引,注意数据类型的转换。
63 | let sum = [...temp].filter((_, idx) => idx % 2 != 0).reduce((acc, cur) => acc + 3 * +cur, 0)
64 | + [...temp].filter((_, idx) => idx % 2 == 0).reduce((acc, cur) => acc + +cur, 0)
65 | let check = `${10 - sum % 10}`
66 | return temp + check
67 | }
68 |
69 | // 测试用例
70 | console.log(getNumbers("7-5600-3879-4")); // 7560038794
71 | console.log(getNumbers("7 5600 3879 4")); // 7560038794
72 |
73 | console.log(validISBN10("7560038794")); // true
74 | console.log(validISBN10("7560038793")); // false
75 | console.log(validISBN10("756003879")); // false
76 | console.log(validISBN10("756003879004")); // false
77 |
78 | console.log(ISBN10To13("7560038794")); // 9787560038797
79 | console.log(ISBN10To13("3598215088")); // 9783598215087
80 |
--------------------------------------------------------------------------------
/snippets/JavaScript/JSON转换.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @param {*} 左侧输入框输入的值转化成的 js 数据
3 | * @return {*} 根据传入的数据生成对应的 js 格式数据
4 | */
5 |
6 | let generateBetween = (start, end) => {
7 | return Math.floor(Math.random() * (end - start + 1)) + start;
8 | }
9 |
10 | /**
11 | *
12 | * @param {Object} obj
13 | */
14 | let mainReplace = (obj) => {
15 | const bool = /\{\{bool\(\)\}\}/
16 | const integer = /\{\{integer\((\d*)\,\s*(\d*)\)\}\}/
17 | result = {}
18 | for (const [key, value] of Object.entries(obj)) {
19 | if (typeof value === "string" && integer.test(value)) {
20 | result[key] = +value.replace(integer, (_match, start, end) => {
21 | // start = 1, end = 3
22 | return generateBetween(+start, +end);
23 | })
24 | } else if (typeof value === "string" && bool.test(value)) {
25 | result[key] = Math.random() > 0.5
26 | } else {
27 | result[key] = value;
28 | }
29 | }
30 | return result;
31 | }
32 |
33 | let generateData = (data) => {
34 | // TODO:待补充代码
35 | const repeat1 = /\{\{repeat\((\d*)\)\}\}/
36 | const repeat2 = /\{\{repeat\((\d*)\,\s*(\d*)\)\}\}/
37 |
38 | console.log(data);
39 | if (repeat1.test(data[0])) {
40 | const result = []
41 | const main = mainReplace(data[1])
42 | const times = +(data[0].match(repeat1)[1])
43 | for (let i = 0; i < times; i++) {
44 | result.push(main)
45 | }
46 | return result;
47 | } else if (repeat2.test(data[0])) {
48 | const result = []
49 | const main = mainReplace(data[1])
50 | const [start, end] = [+(data[0].match(repeat2)[1]), +(data[0].match(repeat2)[2])]
51 | const times = generateBetween(start, end);
52 | for (let i = 0; i < times; i++) {
53 | result.push(main)
54 | }
55 | return result;
56 | } else {
57 | return mainReplace(data[0])
58 | }
59 | };
60 |
61 | module.exports = { generateData };
62 |
--------------------------------------------------------------------------------
/snippets/JavaScript/Markdown解析器.js:
--------------------------------------------------------------------------------
1 | const START = 1
2 | const MIDDLE = 2
3 | const END = 3
4 | const BLOCKQUOTE = 1
5 | const UL = 2
6 |
7 | class Parser {
8 | constructor() {
9 | this.heading = /^(#{1,6}\s+)/;
10 | this.blockQuote = /^(\>\s+(.*))/;
11 | this.unorderedList = /^((\*|-){1}\s+(.*))/;
12 | this.image = /\!\[(.*?)\]\((.*?)\)/g;
13 | this.strongText = /\*{2}(.*?)\*{2}/g;
14 | this.codeLine = /\`{1}(.*?)\`{1}/g;
15 | // TODO: 补充分割符正则
16 | this.hr = /\-{3}\-*/;
17 | }
18 |
19 | // 获取单行内容
20 | parseLineText(lineText) {
21 | this.lineText = lineText;
22 | }
23 |
24 | // 是否是空行
25 | isEmptyLine() {
26 | return this.lineText === "";
27 | }
28 |
29 | // 是否为符合标题规范
30 | isHeading() {
31 | return this.heading.test(this.lineText);
32 | }
33 |
34 | // 解析标题
35 | parseHeading() {
36 | const temp = this.lineText.split(" ");
37 | const headingLevel = temp[0].length;
38 | const title = temp[1].trim();
39 | return `${title}`;
40 | }
41 |
42 | /**
43 | * TODO: 请完成剩余各种语法的解析
44 | * 1. 完成对分隔符的解析
45 | * 2. 完成对引用区块的解析
46 | * 3. 完成对图片,和文字效果的解析
47 | * 4. 完成对无序列表的解析
48 | */
49 | isHr() {
50 | return this.hr.test(this.lineText);
51 | }
52 |
53 | parseHr() {
54 | return `
`
55 | }
56 |
57 | isBlockQuote() {
58 | return this.blockQuote.test(this.lineText);
59 | }
60 |
61 | parseBlockQuote(position) {
62 | const match = this.blockQuote.exec(this.lineText);
63 | if (position == START) {
64 | return `${match[2]}
`
65 | } else if (position == MIDDLE) {
66 | return `${match[2]}
`
67 | } else {
68 | return `
`
69 | }
70 | }
71 | isUl() {
72 | return this.unorderedList.test(this.lineText);
73 | }
74 | parseUl(position) {
75 | const match = this.unorderedList.exec(this.lineText);
76 | if (position == START) {
77 | return `- ${match[3]}
`
78 | } else if (position == MIDDLE) {
79 | return `- ${match[3]}
`
80 | } else {
81 | return `
`
82 | }
83 | }
84 | // isImg() {
85 | // return this.image.test(this.lineText)
86 | // }
87 | // parseImg() {
88 | // const match = this.image.exec(this.lineText)
89 | // // console.log(match);
90 | // return `
`
91 | // }
92 | // isStrong() {
93 | // return this.strongText.test(this.lineText)
94 | // }
95 | // parseStrong() {
96 | // const match = this.strongText.exec(this.lineText);
97 | // return `${match[1]}`
98 | // }
99 | // isCode() {
100 | // return this.codeLine.test(this.lineText);
101 | // }
102 | // parseCode() {
103 | // const match = this.codeLine.exec(this.lineText);
104 | // return `${match[1]}
`
105 | // }
106 | }
107 |
108 |
109 |
110 | class Reader {
111 | constructor(text) {
112 | //获取全部原始文本
113 | this.text = text;
114 | this.lines = this.getLines();
115 | this.parser = new Parser();
116 | }
117 |
118 | runParser() {
119 | let currentLine = 0;
120 | let hasParsed = [];
121 | let last = undefined;
122 |
123 | while (!this.reachToEndLine(currentLine)) {
124 | // 获取行文本
125 | this.parser.parseLineText(this.getLineText(currentLine));
126 |
127 | // 判断空白行
128 | if (this.parser.isEmptyLine()) {
129 | // TODO: 解决引用区块的结束
130 | if (last == BLOCKQUOTE) {
131 | hasParsed.push(this.parser.parseBlockQuote(END));
132 | last = undefined;
133 | }
134 | if (last == UL) {
135 | hasParsed.push(this.parser.parseUl(END));
136 | last = undefined;
137 | }
138 | currentLine++;
139 | continue;
140 | }
141 |
142 | if (this.parser.isHeading()) {
143 | hasParsed.push(this.parser.parseHeading());
144 | currentLine++;
145 | continue;
146 | }
147 | // TODO: 请完成剩余各种语法的解析
148 | if (this.parser.isHr()) {
149 | hasParsed.push(this.parser.parseHr());
150 | currentLine++;
151 | continue;
152 | }
153 |
154 | if (this.parser.isBlockQuote()) {
155 | if (last == BLOCKQUOTE) {
156 | hasParsed.push(this.parser.parseBlockQuote(MIDDLE))
157 | } else {
158 | hasParsed.push(this.parser.parseBlockQuote(START))
159 | }
160 | last = BLOCKQUOTE;
161 | currentLine++
162 | continue;
163 | }
164 |
165 | if (this.parser.isUl()) {
166 | if (last == UL) {
167 | hasParsed.push(this.parser.parseUl(MIDDLE))
168 | } else {
169 | hasParsed.push(this.parser.parseUl(START))
170 | }
171 | last = UL;
172 | currentLine++
173 | continue;
174 | }
175 |
176 | // if (this.parser.isImg()) {
177 | // hasParsed.push(this.parser.parseImg());
178 | // currentLine++
179 | // continue;
180 | // }
181 |
182 | hasParsed.push(this.parser.lineText
183 | .replace(this.parser.image, "
")
184 | .replace(this.parser.strongText, "$1")
185 | .replace(this.parser.codeLine, "$1
")
186 | );
187 | currentLine++;
188 | }
189 | console.log(hasParsed);
190 | return hasParsed.join("");
191 | }
192 |
193 | getLineText(lineNum) {
194 | return this.lines[lineNum];
195 | }
196 |
197 | getLines() {
198 | this.lines = this.text.split("\n");
199 | return this.lines;
200 | }
201 |
202 | reachToEndLine(line) {
203 | return line >= this.lines.length;
204 | }
205 | }
206 |
207 | module.exports = function parseMarkdown(markdownContent) {
208 | return new Reader(markdownContent).runParser();
209 | };
210 |
--------------------------------------------------------------------------------
/snippets/JavaScript/井字棋.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * 2024年国赛。中等难度。
4 | */
5 | function checkMap() {
6 | const wins = [
7 | [0, 1, 2],
8 | [3, 4, 5],
9 | [6, 7, 8],
10 | [0, 3, 6],
11 | [1, 4, 7],
12 | [2, 5, 8],
13 | [0, 4, 8],
14 | [2, 4, 6]
15 | ];
16 |
17 | // 逻辑:所有胜利组合中存在一种组合,这种组合里的每格都是x或o
18 | const isWinner = (player) => wins.some(combination =>
19 | combination.every(index => map.flat()[index] === player)
20 | );
21 |
22 |
23 | // 逻辑:依次判断每一行、每一列、两条对角线是否全为x或o,否则为false
24 | const isWinner2 = (player) => {
25 | if (map.some(row => row.every(cell => cell === player))) return true;
26 | if (map.some((_, i) => map.every(row => row[i] === player))) return true;
27 | if (map.every((_, i) => map[i][i] === player)) return true;
28 | if (map.every((_, i) => map[i][map.length - 1 - i] === player)) return true;
29 | return false;
30 | }
31 |
32 | if (isWinner('x')) return 'x';
33 | if (isWinner('o')) return 'o';
34 | if (map.flat().every(cell => cell !== null)) return true; // 平局
35 | return null;
36 | }
37 |
--------------------------------------------------------------------------------
/snippets/JavaScript/分享点滴.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2024年省赛。中等难度。
3 | * 拼接URL的查询参数。
4 | *
5 | * 拼接结果示例: https://example.com/page?key1=value1&key2=value2
6 | * url中可能本身就有查询参数,也可能没有。
7 | */
8 |
9 | /**
10 | * @param {string} url 目标 URL
11 | * @param {object} params 参数对象
12 | * @return {string} 拼接后的 url
13 | */
14 | function appendParamsToURL1(url, params) {
15 | // TODO: 待补充代码
16 | return Object.entries(params).reduce((acc, [k, v]) => {
17 | if (acc.includes("?")) {
18 | acc += "&" + k + "=" + v
19 | } else {
20 | acc += "?" + k + "=" + v
21 | }
22 | return acc;
23 | // let sep = url.includes("?") ? "&" : "?"
24 | // return url + sep + k + "=" + v
25 | }, url)
26 | }
27 |
28 |
29 | function appendParamsToURL2(url, params) {
30 | // TODO: 待补充代码
31 | return url
32 | + (url.includes("?") ? "&" : "?")
33 | + Object.entries(params)
34 | .map(([k, v]) => `${k}=${v}`)
35 | .join("&")
36 | }
--------------------------------------------------------------------------------
/snippets/JavaScript/商品浏览足迹.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2024年省赛。这个题判题有问题。中高难度。
3 | */
4 | window.onload = async ()=> {
5 | const MockUrl =`./js/data.json`;// 请求地址
6 | let data=[];// 存储请求后的数据
7 | // TODO:待补充代码,目标 1
8 | const res = await fetch(MockUrl)
9 | data = await res.json()
10 | // TODO:END
11 | // 请求完成后调用下面的代码
12 | const newData = getData(data);
13 | showData(newData);
14 | };
15 |
16 | /**
17 | * 将同一天浏览的相同商品去重并作为数组返回
18 | * @param {Array} defaultData json 文件中读取到的原始数据
19 | * @returns 去重后的数据,数据结构与 defaultData 相同
20 | */
21 |
22 | // 转成Map
23 | const removeDuplicates1 = defaultData => {
24 | return [...new Map(defaultData.map((it) => {
25 | let date = it.viewed_on.split("T")[0]
26 | return [it.id + date, it]
27 | })).values()]
28 | }
29 |
30 | // 转成Object
31 | const removeDuplicates2 = defaultData => {
32 | let date = it.viewed_on.split("T")[0]
33 | return Object.values(Object.fromEntries(
34 | defaultData.map(it => [it.id + date, it])
35 | ))
36 | }
37 |
38 | /**
39 | * 将去重后的数据根据字段 viewed_on(格式化为 YYYY-MM-DD) 降序排序
40 | * @param {*} defaultData 去重后的数据
41 | * @returns 根据字段 viewed_on(格式化为 YYYY-MM-DD) 降序排序
42 | */
43 | const sortByDate = defaultData => {
44 | defaultData.sort((a, b) => {
45 | return (a.viewed_on.split("T")[0] < b.viewed_on.split("T")[0]) ? 1 : -1
46 | // return defaultData.sort((a, b) => b.viewed_on.localeCompare(a.viewed_on))
47 | })
48 | return defaultData
49 | }
50 |
51 | /**
52 | * 将去重排序后的所有商品数据,作为一个对象存储并返回,
53 | * 该对象的所有 `key` 为浏览商品的当天日期(即,字段 viewed_on 格式化为 YYYY-MM-DD),
54 | * `value` 为当天浏览的所有商品(以数组形式表现)。
55 | * @param {Array} defaultData 重后的所有商品数据
56 | * @returns
57 | */
58 | const transformStructure = defaultData => {
59 | return defaultData.reduce((acc, cur) => {
60 | let date = cur.viewed_on.split("T")[0]
61 | if (date in acc) {
62 | acc[date].push(cur)
63 | } else {
64 | acc[date] = [cur]
65 | }
66 | return acc
67 | }, {})
68 |
69 | }
70 |
71 | const transformStructure2 = defaultData => {
72 | return Object.groupBy(defaultData, ({ viewed_on }) => {
73 | return viewed_on.split("T")[0]
74 | })
75 | }
76 |
77 |
78 | const getData = (defaultData) => {
79 | let newData = removeDuplicates(defaultData);
80 | let sortData = sortByDate(newData);
81 | let objData = transformStructure(sortData);
82 | return objData;
83 | };
84 | const showData = (data) => {
85 | let str = ``;
86 | for (let k in data) {
87 | str += `${k}
`;
88 | data[k].forEach((goods) => {
89 | str += `
90 |
91 |
92 |
${goods.name}
93 |
${goods.description}
94 |
95 | ¥${goods.price}
96 |
97 |
98 |
99 |
`;
100 | });
101 | }
102 | document.querySelector("#goodsList").innerHTML = str;
103 | };
104 |
--------------------------------------------------------------------------------
/snippets/JavaScript/寻找小狼人.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2022年省赛。签到题。
3 | */
4 |
5 | Array.prototype.myarray = function (cb) {
6 | return this.reduce((acc, cur) => cb(cur) ? [...acc, cur] : acc, [])
7 | };
8 |
9 | Array.prototype.myarray = function (cb) {
10 | return this.reduce((acc, cur) => cb(cur) ? (acc.push(cur), acc) : acc, [])
11 | };
12 |
13 |
14 |
--------------------------------------------------------------------------------
/snippets/JavaScript/小兔子找萝卜.js:
--------------------------------------------------------------------------------
1 | const $ = (ele) => document.querySelector(ele)
2 |
3 | const blocks = [...document.querySelectorAll(".container .lawn")]
4 | let curr = 0;
5 |
6 | // TODO:游戏开始
7 | function start() {
8 | $("#move").style.display = "block"
9 | $("#start").style.display = "none"
10 | }
11 | // TODO:重置游戏
12 | function reset() {
13 | blocks[curr].classList.remove("active")
14 | curr = 0;
15 | blocks[curr].classList.add("active")
16 | $("#reset").style.display = "none"
17 | $("#start").style.display = "block"
18 | $(".result").textContent = ""
19 | $(".process input").value = ""
20 | }
21 | // TODO:移动
22 | function move() {
23 | const step = +($(".process input").value)
24 | if (!(step == 1 || step == 2)) {
25 | $(".result").textContent = "输入的步数不正确,请重新输入。"
26 | } else {
27 | $(".result").textContent = ""
28 | blocks[curr].classList.remove("active")
29 | curr += step
30 | blocks[curr].classList.add("active")
31 | if (curr == 12) {
32 | $("#move").style.display = "none"
33 | $("#reset").style.display = "block"
34 | $(".result").textContent = "哎呀!兔子踩到炸弹了,游戏结束!"
35 | } else if (curr == 23) {
36 | $("#move").style.display = "none"
37 | $("#reset").style.display = "block"
38 | $(".result").textContent = "小兔子吃到胡萝卜啦,游戏获胜!"
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/snippets/JavaScript/工作协调.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * 2024年省赛。中等难度。
4 | */
5 |
6 |
7 | /**
8 | * 返回两个集合的差集 a-b
9 | * @param a 类型为 XSet 的集合
10 | * @param b 类型为 XSet 的集合
11 | * @returns {XSet} 返回差集集合,类型为 XSet
12 | */
13 | function difference(a, b) {
14 | // 从a中筛选出不在b中的元素
15 | return new XSet([...a].filter(it => !b.has(it)))
16 | }
17 | /**
18 | * 返回两个或多个集合的交集
19 | * @param a 类型为 XSet 的集合
20 | * @param bSets 元素类型为 XSet 的数组
21 | * @returns {XSet} 返回交集集合,类型为 XSet
22 | */
23 | function intersection(a, ...bSets) {
24 | // 从a中筛选出在bSets的每个set都出现的元素
25 | return new XSet([...a].filter(it => bSets.every(set => set.has(it))));
26 | }
27 |
28 | function intersection2(a, ...bSets) {
29 | // 从a中筛选出在bSets的每个set都出现的元素
30 | return new XSet(bSets.reduce((acc, cur) => {
31 | return [...acc].filter(it => cur.has(it))
32 | }, [...a]));
33 | }
34 |
35 |
36 | /**
37 | * 返回两个或多个集合的并集
38 | * @param a 类型为 XSet 的集合
39 | * @param bSets 元素类型为 XSet 的数组
40 | * @returns {XSet} 返回并集集合,类型为 XSet
41 | */
42 | function union(a, ...bSets) {
43 | // 把a和bSets的所有元素全部放入XSet中,自然去重。
44 | return new XSet([...[...a], ...bSets.map(s => [...s]).flat()])
45 | }
46 |
47 | function union2(a, ...bSets) {
48 | return new XSet(bSets.reduce((acc, cur) => {
49 | return [...acc, ...cur] // 重复也没关系,XSet会自动去重
50 | }, [...a]))
51 | }
--------------------------------------------------------------------------------
/snippets/JavaScript/悠然画境.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2024年省赛。中等难度。
3 | */
4 | /**
5 | * @param {*} imageCount 生成的图片数量
6 | * @param {String} selectedText 用户输入的文本
7 | */
8 | function generateAndDisplayImages(imageCount, selectedText) {
9 | // TODO:待补充代码
10 | let filtered = artDataArray.map((it) => {
11 | let weight = it.tags.split("、")
12 | .filter(tag => selectedText.includes(tag)).length
13 | return {...it, "weight": weight}
14 | })
15 | filtered.sort((a, b) => b.weight - a.weight) // 降序
16 | return filtered.slice(0, imageCount)
17 | }
--------------------------------------------------------------------------------
/snippets/JavaScript/收集帛书碎片.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2023省赛 签到题
3 | * 将一个二维数组展平,并去重后返回数组结果。
4 | */
5 | function collectPuzzle1(...puzzles) {
6 | // TODO:在这里写入具体的实现逻辑
7 | // 对所有的拼图进行收集,获取不同拼图类型的结果,并返回
8 | return [...new Set(puzzles.flat())]
9 | // return Array.from(new Set(puzzles.flat()));
10 | }
11 |
12 | function collectPuzzle2(...puzzles) {
13 | // TODO:在这里写入具体的实现逻辑
14 | // 对所有的拼图进行收集,获取不同拼图类型的结果,并返回
15 | return puzzles.flat().filter((item, index) => arr.indexOf(item) === index)
16 | }
17 |
18 | function collectPuzzle3(...puzzles) {
19 | // TODO:在这里写入具体的实现逻辑
20 | // 对所有的拼图进行收集,获取不同拼图类型的结果,并返回
21 | return puzzles.flat().reduce((acc, item) => {
22 | // return acc.includes(item) ? acc : [...acc, item];
23 | if (!acc.includes(item)) {
24 | acc.push(item);
25 | }
26 | // acc.includes(item) || acc.push(item);
27 | return acc;
28 | }, []);
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/snippets/JavaScript/猜硬币.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2022年国赛。中等难度。
3 | *
4 | */
5 |
6 | /**
7 | * @param {*} input_values input 框中输入的值
8 | * @returns Array 将输入的值中 1-9 组成一个数组
9 | */
10 |
11 | // 将输入的值中 1-9 组成一个数组
12 | function findNum(input_values) {
13 | // TODO:待补充代码
14 | return [...input_values].map(s => +s).filter(i => 1 <= i && i <= 9)
15 | }
16 |
17 | function findNum2(input_values) {
18 | // TODO:待补充代码
19 | return input_values.replace(/\D/g, '').split('').map(Number)
20 | }
21 |
22 | // 将 1-9 中三个不重复的随机数放入数组中,并返回这个数组
23 | let randomCoin = () => {
24 | return new Array(9).fill(0) // 重要!否则会创建稀疏数组
25 | .map((_, i) => i + 1) // or: Array.from({ length: 9 }, (_, i) => i + 1) // 类数组对象
26 | .map(it => [it, Math.random()])
27 | .sort((a, b) => a[1] - b[1])
28 | .slice(0, 3)
29 | .map(it => it[0])
30 | };
31 |
32 |
33 | // 可以借鉴的序列生成器
34 | const range = (start, stop, step) =>
35 | Array.from({ length: (stop - start) / step + 1 }, (_, i) => start + i * step);
36 |
37 | let randomCoin2 = () => {
38 | let set = new Set();
39 | while (set.size < 3) {
40 | set.add(Math.floor(Math.random() * 9) + 1) // 0.x * 9 => 0 ~ 8.999 向下舍入 => 0 ~ 8 => +1
41 | // set.add(Math.ceil(Math.random() * 9)) // 0.x * 9 => 0 ~ 8.999 向上舍入 => 1 ~ 9
42 | }
43 | return [...set]
44 | }
45 |
46 | // 请勿删除和修改下面代码
47 | try {
48 | module.exports = { randomCoin, findNum };
49 | } catch (e) {}
50 |
--------------------------------------------------------------------------------
/snippets/JavaScript/解密工具.js:
--------------------------------------------------------------------------------
1 | /**
2 | * https://www.lanqiao.cn/problems/19906/learning/?contest_id=248
3 | * @param {*} max
4 | * @param {*} count
5 | * @returns
6 | */
7 |
8 | function getPossiblePasswords(max, count) {
9 | // TODO: 待补充代码
10 | function boom(arr) {
11 | return Array.from({ length: max }, (_, i) => i + 1)
12 | .filter(n => arr.length === 0 ? true : n > arr[arr.length - 1])
13 | .map(n => [...arr, n]);
14 | }
15 |
16 | let arr = Array.from({ length: max }, (_, i) => i + 1);
17 | let times = 0;
18 | let result = boom([]);
19 | while (times < count - 1) {
20 | result = result.flatMap(boom);
21 | times++;
22 | }
23 | return result;
24 | }
25 |
--------------------------------------------------------------------------------
/snippets/JavaScript/谁最长.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2022年省赛 中等难度。
3 | * 返回长度最大的数组集合。
4 | *
5 | * 传入的参数可能:
6 | * 有一个最长数组、
7 | * 多个最长数组(都返回)、
8 | * 所有数组等长(返回空数组)、
9 | * 无参数(返回空数组)、
10 | * 传入的不是数组(返回空数组)、
11 | * 传入的部分不是数组(返回空数组)
12 | */
13 |
14 | const getMaxArrays1 = (...arrays) => {
15 | // TODO:待补充代码
16 | if (!arrays.every(arr => Array.isArray(arr))) { // or use instanceof
17 | return []
18 | }
19 |
20 | // if (arrays.some((a) => !Array.isArray(a))) return [];
21 |
22 | if (arrays.every(arr => arr.length === arrays[0].length)) {
23 | return []
24 | } // 对于空数组压根不会执行这段
25 |
26 | return arrays.reduce((acc, item) => {
27 | if (acc.length == 0 || item.length > acc[0].length) {
28 | acc = [item]
29 | } else if (item.length == acc[0].length) {
30 | acc.push(item)
31 | }
32 | return acc
33 | }, []) // 空数组同样不会执行
34 | };
35 |
36 | const getMaxArrays2 = (...arrays) => {
37 | // TODO:待补充代码
38 | if (arrays.some((a) => !Array.isArray(a))) return [];
39 | const max = Math.max(...arrays.map((a) => a.length));
40 | if (arrays.every((a) => a.length === max)) return [];
41 | return arrays.filter((a) => a.length === max);
42 | };
43 |
44 |
--------------------------------------------------------------------------------
/snippets/Node/tree命令助手.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | function generateTree(dirPath) {
5 | const items = fs.readdirSync(dirPath); // 获取目录下的所有文件和文件夹
6 |
7 | return items.map(item => {
8 | const fullPath = path.join(dirPath, item); // 重要
9 | const isDirectory = fs.statSync(fullPath).isDirectory();
10 |
11 | if (isDirectory) {
12 | return { name: item, children: generateTree(fullPath) };
13 | } else {
14 | return { name: item };
15 | }
16 | });
17 | }
18 |
--------------------------------------------------------------------------------
/snippets/Node/tree命令助手bfs.cjs:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | function generateTreeBFS(rootDir) {
5 | const queue = [{ dirPath: rootDir, node: { name: path.basename(rootDir), children: [] } }];
6 | const rootNode = queue[0].node;
7 |
8 | while (queue.length > 0) {
9 | const { dirPath, node } = queue.shift();
10 | const items = fs.readdirSync(dirPath);
11 |
12 | for (const item of items) {
13 | const fullPath = path.join(dirPath, item);
14 | const isDirectory = fs.statSync(fullPath).isDirectory();
15 | const newNode = { name: item, children: isDirectory ? [] : undefined };
16 |
17 | if (isDirectory) {
18 | queue.push({ dirPath: fullPath, node: newNode });
19 | }
20 |
21 | node.children.push(newNode);
22 | }
23 | }
24 |
25 | return rootNode;
26 | }
27 |
28 | // 示例
29 | console.log(JSON.stringify(generateTreeBFS(__dirname), null, 2));
30 |
--------------------------------------------------------------------------------
/snippets/Node/tree命令助手dfs.cjs:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | function generateTreeBFS(rootDir) {
5 | const stack = [{ dirPath: rootDir, node: { name: path.basename(rootDir), children: [] } }];
6 | const rootNode = queue[0].node;
7 |
8 | while (stack.length > 0) {
9 | const { dirPath, node } = stack.pop();
10 | const items = fs.readdirSync(dirPath);
11 |
12 | for (const item of items) {
13 | const fullPath = path.join(dirPath, item);
14 | const isDirectory = fs.statSync(fullPath).isDirectory();
15 | const newNode = { name: item, children: isDirectory ? [] : undefined };
16 |
17 | if (isDirectory) {
18 | stack.push({ dirPath: fullPath, node: newNode });
19 | }
20 |
21 | node.children.push(newNode);
22 | }
23 | }
24 |
25 | return rootNode;
26 | }
27 |
28 | // 示例
29 | console.log(JSON.stringify(generateTreeBFS(__dirname), null, 2));
30 |
--------------------------------------------------------------------------------
/snippets/Node/代码量统计bfs.js:
--------------------------------------------------------------------------------
1 | function structBFS(path, names, value) {
2 | let queue = [{ path, names: [...names] }];
3 |
4 | while (queue.length > 0) {
5 | let { path, names } = queue.shift();
6 |
7 | let match = path.find(node => node.name === names[0]);
8 |
9 | if (match) {
10 | if (names.length === 1) {
11 | match.value = value; // 叶子节点更新 value
12 | } else {
13 | queue.push({ path: match.children, names: names.slice(1) }); // 继续遍历
14 | }
15 | } else {
16 | let newNode = {
17 | name: names[0],
18 | children: []
19 | };
20 | path.push(newNode);
21 |
22 | if (names.length === 1) {
23 | newNode.value = value; // 叶子节点更新 value
24 | } else {
25 | queue.push({ path: newNode.children, names: names.slice(1) }); // 继续遍历
26 | }
27 | }
28 | }
29 | }
30 |
31 | let tree2 = []; // 初始化空的树
32 |
33 | structBFS(tree2, ["A", "B", "C"], 10);
34 | structBFS(tree2, ["A", "B", "D"], 20);
35 | structBFS(tree2, ["A", "E"], 30);
36 | structBFS(tree2, ["X", "Y", "Z"], 40);
37 |
38 | console.log(JSON.stringify(tree2, null, 2));
39 |
--------------------------------------------------------------------------------
/snippets/Node/代码量统计dfs.js:
--------------------------------------------------------------------------------
1 | function structDFS(path, names, value) {
2 | let stack = [{ path, names: [...names] }]; // 使用栈替代队列
3 |
4 | while (stack.length > 0) {
5 | let { path, names } = stack.pop(); // 使用 pop() 来模拟 DFS
6 |
7 | let match = path.find(node => node.name === names[0]);
8 |
9 | if (match) {
10 | if (names.length === 1) {
11 | match.value = value; // 叶子节点更新 value
12 | } else {
13 | stack.push({ path: match.children, names: names.slice(1) }); // 继续遍历
14 | }
15 | } else {
16 | let newNode = {
17 | name: names[0],
18 | children: []
19 | };
20 | path.push(newNode);
21 |
22 | if (names.length === 1) {
23 | newNode.value = value; // 叶子节点更新 value
24 | } else {
25 | stack.push({ path: newNode.children, names: names.slice(1) }); // 继续遍历
26 | }
27 | }
28 | }
29 | }
30 |
31 | let tree2 = []; // 初始化空的树
32 |
33 | structDFS(tree2, ["A", "B", "C"], 10);
34 | structDFS(tree2, ["A", "B", "D"], 20);
35 | structDFS(tree2, ["A", "E"], 30);
36 | structDFS(tree2, ["X", "Y", "Z"], 40);
37 |
38 | console.log(JSON.stringify(tree2, null, 2));
39 |
--------------------------------------------------------------------------------
/snippets/Node/扫雷.js:
--------------------------------------------------------------------------------
1 | // 扫雷算法
2 | function mineSweeperAlgorithms(arr, { row, col }) {
3 | const queue = [{ row, col }];
4 | while (queue.length > 0) {
5 | const { row, col } = queue.shift()
6 | if (flagData[row][col]) {
7 | continue;
8 | }
9 | flagData[row][col] = true;
10 | const { positionWithoutMineArr, count } = getAroundAndCount(arr, { row, col });
11 | if (count === 0) {
12 | queue.push(...positionWithoutMineArr)
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/snippets/Node/项目语言分析.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const path = require("path");
3 |
4 | function scanFiles(directoryPath, fileExtensionArr) {
5 | if (!fs.existsSync(directoryPath)) return `目录路径 ${directoryPath} 不存在`;
6 | if (!Array.isArray(fileExtensionArr)) return "fileExtensionArr 必须是数组类型";
7 |
8 | const stats = {};
9 | fileExtensionArr.forEach((ext) => (stats[ext] = 0));
10 | stats["other"] = 0;
11 |
12 | function scanDir(dir) {
13 | const fileStat = fs.statSync(dir);
14 | if (fileStat.isDirectory()) {
15 | fs.readdirSync(dir).forEach((child) => scanDir(path.resolve(dir, child)));
16 | } else {
17 | const ext = path.extname(dir).slice(1);
18 | if (fileExtensionArr.includes(ext)) {
19 | stats[ext] += fileStat.size;
20 | } else {
21 | stats["other"] += fileStat.size;
22 | }
23 | }
24 | }
25 |
26 | scanDir(directoryPath);
27 |
28 | const totalSize = Object.values(stats).reduce((sum, size) => sum + size, 0);
29 |
30 | return Object.entries(stats).map(([filename, size]) => ({
31 | filename,
32 | percentage: totalSize ? ((size * 100) / totalSize).toFixed(2) : "0.00",
33 | }));
34 | }
35 |
--------------------------------------------------------------------------------
/snippets/Node/项目语言分析bfs.js:
--------------------------------------------------------------------------------
1 | const queue = [directoryPath]; // 用队列实现 BFS
2 | while (queue.length > 0) {
3 | const currentPath = queue.shift(); // 取出最早加入的目录(先进先出)
4 |
5 | const fileStat = fs.statSync(currentPath);
6 | if (fileStat.isDirectory()) {
7 | // 先把所有子目录加入队列
8 | fs.readdirSync(currentPath).forEach((child) =>
9 | queue.push(path.resolve(currentPath, child))
10 | );
11 | } else {
12 | // 处理文件逻辑
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/snippets/Node/项目语言分析dfs.js:
--------------------------------------------------------------------------------
1 | const stack = [directoryPath]; // 用栈实现 DFS
2 | while (stack.length > 0) {
3 | const currentPath = stack.pop(); // 取出最后添加的元素(后进先出)
4 |
5 | const fileStat = fs.statSync(currentPath);
6 | if (fileStat.isDirectory()) {
7 | // 先把子目录全部加入栈
8 | fs.readdirSync(currentPath).forEach((child) =>
9 | stack.push(path.resolve(currentPath, child))
10 | );
11 | } else {
12 | // 处理文件逻辑
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/snippets/Promise/MyPromise/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies (bun install)
2 | node_modules
3 |
4 | # output
5 | out
6 | dist
7 | *.tgz
8 |
9 | # code coverage
10 | coverage
11 | *.lcov
12 |
13 | # logs
14 | logs
15 | _.log
16 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
17 |
18 | # dotenv environment variable files
19 | .env
20 | .env.development.local
21 | .env.test.local
22 | .env.production.local
23 | .env.local
24 |
25 | # caches
26 | .eslintcache
27 | .cache
28 | *.tsbuildinfo
29 |
30 | # IntelliJ based IDEs
31 | .idea
32 |
33 | # Finder (MacOS) folder config
34 | .DS_Store
35 |
--------------------------------------------------------------------------------
/snippets/Promise/MyPromise/MyPromise.js:
--------------------------------------------------------------------------------
1 | import { consola } from 'consola'
2 |
3 | const PENDING = 'pending'
4 | const FULFILLED = 'fulfilled'
5 | const REJECTED = 'rejected'
6 |
7 | function runAsyncTask(cb) {
8 | setTimeout(cb, 0)
9 | }
10 |
11 | class MyPromise {
12 | state = PENDING
13 | result = undefined
14 | #handlers = [] // { onFulfilled, onRejected }
15 |
16 | constructor(executor) {
17 | const resolve = (res) => {
18 | // pending -> fulfilled
19 | // reason
20 | if (this.state === PENDING) {
21 | this.state = FULFILLED
22 | this.result = res
23 | this.#handlers.forEach(({ onFulfilled }) => {
24 | onFulfilled(this.result)
25 | })
26 | }
27 | }
28 | const reject = (res) => {
29 | // pending -> rejected
30 | // reason
31 | if (this.state === PENDING) {
32 | this.state = REJECTED
33 | this.result = res
34 | this.#handlers.forEach(({ onRejected }) => {
35 | onRejected(this.result)
36 | })
37 | }
38 | }
39 | try {
40 | executor(resolve, reject)
41 | } catch (e) {
42 | reject(e)
43 | }
44 | }
45 |
46 | then(onFulfilled, onRejected) {
47 | onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (x) => x
48 | onRejected = typeof onRejected === 'function' ? onRejected : (x) => { throw x }
49 |
50 | const p2 = new MyPromise((resolve, reject) => {
51 | const handleCallback = (callback) => {
52 | runAsyncTask(() => {
53 | try {
54 | const result = callback(this.result)
55 | if (result === p2) {
56 | throw new TypeError('Chaining cycle detected for promise')
57 | }
58 | if (result instanceof MyPromise) {
59 | result.then(resolve, reject)
60 | } else {
61 | resolve(result)
62 | }
63 | } catch (e) {
64 | reject(e)
65 | }
66 | })
67 | }
68 |
69 | if (this.state === FULFILLED) {
70 | handleCallback(onFulfilled)
71 | } else if (this.state === REJECTED) {
72 | handleCallback(onRejected)
73 | } else if (this.state === PENDING) {
74 | this.#handlers.push({
75 | onFulfilled: () => handleCallback(onFulfilled),
76 | onRejected: () => handleCallback(onRejected)
77 | })
78 | }
79 | return p2;
80 | });
81 | }
82 |
83 | catch(onRejected) {
84 | return this.then(undefined, onRejected)
85 | }
86 |
87 | finally(onFinally) {
88 | return this.then(onFinally, onFinally)
89 | }
90 |
91 | static resolve(value) {
92 | if (value instanceof MyPromise) {
93 | return value;
94 | } else {
95 | return new MyPromise((resolve, _) => resolve(value))
96 | }
97 | }
98 |
99 | static reject(value) {
100 | return new MyPromise((_, reject) => reject(value))
101 | }
102 |
103 | static race(promises) {
104 | return new MyPromise((resolve, reject) => {
105 | if (!Array.isArray(promises)) {
106 | return reject(new TypeError('Argument is not iterable'))
107 | }
108 | promises.forEach(p => {
109 | MyPromise.resolve(p).then(resolve, reject)
110 | })
111 | })
112 | }
113 |
114 | static all(promises) {
115 | return new MyPromise((resolve, reject) => {
116 | if (!Array.isArray(promises)) {
117 | return reject(new TypeError('Argument is not iterable'))
118 | }
119 | if (promises.length === 0) {
120 | return resolve([])
121 | }
122 | promises.reduce((acc, cur, i) => {
123 | MyPromise.resolve(cur).then(res => {
124 | acc[i] = res
125 | if (Object.keys(acc).length === promises.length) {
126 | resolve(acc)
127 | }
128 | return acc
129 | }, err => {
130 | reject(err)
131 | })
132 | }, [])
133 | })
134 | }
135 |
136 | static allSettled(promises) {
137 | return new MyPromise((resolve, reject) => {
138 | if (!Array.isArray(promises)) {
139 | return reject(new TypeError('Argument is not iterable'))
140 | }
141 | if (promises.length === 0) {
142 | return resolve([])
143 | }
144 | promises.reduce((acc, cur, i) => {
145 | MyPromise.resolve(cur).then(res => {
146 | acc[i] = { status: FULFILLED, value: res }
147 | if (Object.keys(acc).length === promises.length) {
148 | resolve(acc)
149 | }
150 | return acc
151 | }, err => {
152 | acc[i] = { status: REJECTED, reason: err }
153 | if (Object.keys(acc).length === promises.length) {
154 | resolve(acc)
155 | }
156 | return acc
157 | })
158 | }, [])
159 | })
160 | }
161 |
162 | static any(promises) {
163 | return new MyPromise((resolve, reject) => {
164 | if (!Array.isArray(promises)) {
165 | return reject(new TypeError('Argument is not iterable'))
166 | }
167 | if (promises.length === 0) {
168 | return reject(new AggregateError('All promises were rejected'))
169 | }
170 | promises.reduce((acc, cur, i) => {
171 | MyPromise.resolve(cur).then(resolve, err => {
172 | acc[i] = err
173 | if (Object.keys(acc).length === promises.length) {
174 | reject(new AggregateError(acc))
175 | }
176 | return acc
177 | })
178 | }, [])
179 | })
180 | }
181 | }
182 |
183 | export default MyPromise
184 |
--------------------------------------------------------------------------------
/snippets/Promise/MyPromise/README.md:
--------------------------------------------------------------------------------
1 | # mypromise
2 |
3 | To install dependencies:
4 |
5 | ```bash
6 | bun install
7 | ```
8 |
9 | To run:
10 |
11 | ```bash
12 | bun run index.ts
13 | ```
14 |
15 | This project was created using `bun init` in bun v1.2.8. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
16 |
--------------------------------------------------------------------------------
/snippets/Promise/MyPromise/bun.lock:
--------------------------------------------------------------------------------
1 | {
2 | "lockfileVersion": 1,
3 | "workspaces": {
4 | "": {
5 | "name": "mypromise",
6 | "devDependencies": {
7 | "@types/bun": "latest",
8 | },
9 | "peerDependencies": {
10 | "typescript": "^5",
11 | },
12 | },
13 | },
14 | "packages": {
15 | "@types/bun": ["@types/bun@1.2.8", "", { "dependencies": { "bun-types": "1.2.7" } }, "sha512-t8L1RvJVUghW5V+M/fL3Thbxcs0HwNsXsnTEBEfEVqGteiJToOlZ/fyOEaR1kZsNqnu+3XA4RI/qmnX4w6+S+w=="],
16 |
17 | "@types/node": ["@types/node@22.14.0", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA=="],
18 |
19 | "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
20 |
21 | "bun-types": ["bun-types@1.2.7", "", { "dependencies": { "@types/node": "*", "@types/ws": "*" } }, "sha512-P4hHhk7kjF99acXqKvltyuMQ2kf/rzIw3ylEDpCxDS9Xa0X0Yp/gJu/vDCucmWpiur5qJ0lwB2bWzOXa2GlHqA=="],
22 |
23 | "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
24 |
25 | "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/snippets/Promise/MyPromise/index.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaojunran/lanqiao-web-slides/ab4a21d2b387ee1a6e1f7514571d3760d65c4016/snippets/Promise/MyPromise/index.ts
--------------------------------------------------------------------------------
/snippets/Promise/MyPromise/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mypromise",
3 | "module": "index.ts",
4 | "type": "module",
5 | "private": true,
6 | "devDependencies": {
7 | "@types/bun": "latest"
8 | },
9 | "peerDependencies": {
10 | "typescript": "^5"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/snippets/Promise/MyPromise/testAsyncTask.js:
--------------------------------------------------------------------------------
1 | import MyPromise from "./MyPromise";
2 |
3 | console.log("start");
4 |
5 | const p = new MyPromise((resolve, reject) => {
6 | reject("error");
7 | })
8 |
9 | p.then(res => {
10 | console.log(res);
11 | }, err => {
12 | console.log(err);
13 | })
14 |
15 | console.log("end");
16 |
--------------------------------------------------------------------------------
/snippets/Promise/MyPromise/testResolve.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaojunran/lanqiao-web-slides/ab4a21d2b387ee1a6e1f7514571d3760d65c4016/snippets/Promise/MyPromise/testResolve.js
--------------------------------------------------------------------------------
/snippets/Promise/MyPromise/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | // Environment setup & latest features
4 | "lib": ["ESNext"],
5 | "target": "ESNext",
6 | "module": "ESNext",
7 | "moduleDetection": "force",
8 | "jsx": "react-jsx",
9 | "allowJs": true,
10 |
11 | // Bundler mode
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "verbatimModuleSyntax": true,
15 | "noEmit": true,
16 |
17 | // Best practices
18 | "strict": true,
19 | "skipLibCheck": true,
20 | "noFallthroughCasesInSwitch": true,
21 | "noUncheckedIndexedAccess": true,
22 |
23 | // Some stricter flags (disabled by default)
24 | "noUnusedLocals": false,
25 | "noUnusedParameters": false,
26 | "noPropertyAccessFromIndexSignature": false
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/snippets/Promise/NPM Download Simulator.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @param {iterable} iterable 可迭代对象
3 | * @return {promise}
4 | */
5 | function myRace(iterable) {
6 | // TODO:待补充代码
7 | if (iterable === null) {
8 | return Promise.reject(new TypeError('object null is not iterable (cannot read property Symbol(Symbol.iterator))'))
9 | }
10 | if (typeof iterable[Symbol.iterator] !== 'function') {
11 | return Promise.reject(new TypeError(`${typeof iterable} ${iterable} is not iterable (cannot read property Symbol(Symbol.iterator))`))
12 | }
13 | return new Promise((resolve, reject) => {
14 | [...iterable].forEach(p => {
15 | Promise.resolve(p).then(resolve, reject)
16 | })
17 | })
18 | }
19 |
--------------------------------------------------------------------------------
/snippets/Promise/note.md:
--------------------------------------------------------------------------------
1 | # 手写实现Promise
2 |
3 | 1. new Promise
4 | 2. then
5 | 3. catch
6 | 4. finally
7 | 5. resolve
8 | 6. reject
9 | 7. all
10 | 8. race
11 | 9. allSettled
12 | 10. any
13 |
14 | ## 实现核心功能
15 |
16 | ### 定义类
17 |
18 | 1. 定义状态
19 | 2. 定义结果
20 | 3. 定义resolve/reject方法,更新状态和结果(不可逆)
21 | 4. 在构造函数中立刻执行回调函数
22 |
23 | ```js
24 | import { consola } from 'consola'
25 |
26 | const PENDING = 'pending'
27 | const FULFILLED = 'fulfilled'
28 | const REJECTED = 'rejected'
29 |
30 | class MyPromise {
31 | state = PENDING
32 | result = undefined
33 |
34 | constructor(executor) {
35 | const resolve = (res) => {
36 | // pending -> fulfilled
37 | // reason
38 | if (this.state === PENDING) {
39 | this.state = FULFILLED
40 | this.result = res
41 | }
42 | }
43 | const reject = (res) => {
44 | // pending -> rejected
45 | // reason
46 | if (this.state === PENDING) {
47 | this.state = REJECTED
48 | this.result = res
49 | }
50 | }
51 |
52 | executor(resolve, reject)
53 | }
54 | }
55 |
56 | const p = new MyPromise((resolve, reject) => {
57 | resolve(1) // pending -> fulfilled
58 | reject(2) // pending -> rejected
59 | })
60 | consola.info(p) // ℹ MyPromise { state: 'fulfilled', result: 1 }
61 |
62 | ```
63 |
64 | ### then 方法
65 |
66 | 基本功能:
67 | - 返回一个Promise
68 | - 如果参数不是函数:
69 | - 如果`onFulfilled`不是函数 -> 替换为一个恒等函数`(x) => x`
70 | - 如果`onRejected`不是函数 -> 替换为一个抛出静函数`(x) => { throw x }`
71 | - then方法应该支持异步任务、多次调用(并行)、链式调用(串行)
72 |
73 | 用户调用then时:
74 | - pending:将回调函数缓存起来,等待状态改变后执行
75 | - fulfilled或rejected:立即执行回调函数
76 |
77 | ```js
78 | import { consola } from 'consola'
79 |
80 | const PENDING = 'pending'
81 | const FULFILLED = 'fulfilled'
82 | const REJECTED = 'rejected'
83 |
84 | function runAsyncTask(cb) {
85 | setTimeout(cb, 0)
86 | }
87 |
88 | class MyPromise {
89 | state = PENDING
90 | result = undefined
91 | #handlers = [] // { onFulfilled, onRejected }
92 |
93 | constructor(executor) {
94 | const resolve = (res) => {
95 | // pending -> fulfilled
96 | // reason
97 | if (this.state === PENDING) {
98 | this.state = FULFILLED
99 | this.result = res
100 | this.#handlers.forEach(({ onFulfilled }) => {
101 | onFulfilled(this.result)
102 | })
103 | }
104 | }
105 | const reject = (res) => {
106 | // pending -> rejected
107 | // reason
108 | if (this.state === PENDING) {
109 | this.state = REJECTED
110 | this.result = res
111 | this.#handlers.forEach(({ onRejected }) => {
112 | onRejected(this.result)
113 | })
114 | }
115 | }
116 |
117 | try {
118 | executor(resolve, reject)
119 | } catch (e) {
120 | reject(e)
121 | }
122 | }
123 |
124 | then(onFulfilled, onRejected) {
125 | onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (x) => x
126 | onRejected = typeof onRejected === 'function' ? onRejected : (x) => { throw x }
127 |
128 | const p2 = new MyPromise((resolve, reject) => {
129 | const handleCallback = (callback) => {
130 | runAsyncTask(() => {
131 | try {
132 | const result = callback(this.result)
133 | if (result === p2) {
134 | throw new TypeError('Chaining cycle detected for promise')
135 | }
136 | if (result instanceof MyPromise) {
137 | result.then(resolve, reject)
138 | } else {
139 | resolve(result)
140 | }
141 | } catch (e) {
142 | reject(e)
143 | }
144 | })
145 | }
146 |
147 | if (this.state === FULFILLED) {
148 | handleCallback(onFulfilled)
149 | } else if (this.state === REJECTED) {
150 | handleCallback(onRejected)
151 | } else if (this.state === PENDING) {
152 | this.#handlers.push({
153 | onFulfilled: () => handleCallback(onFulfilled),
154 | onRejected: () => handleCallback(onRejected)
155 | })
156 | }
157 | return p2;
158 | });
159 | }
160 |
161 | }
162 |
163 | export default MyPromise
164 | ```
165 |
166 | ### 实例方法:catch、finally
167 |
168 | `catch`:注册一个在期约被拒绝的时候调用的函数,是`Promise.then(undefined, onRejected)`的简写形式.
169 |
170 | `finally`:注册一个在期约状态改变后调用的函数,是`Promise.then(onFinally, onFinally)`的简写形式.
171 |
172 | ```js
173 | catch(onRejected) {
174 | return this.then(undefined, onRejected)
175 | }
176 |
177 | finally(onFinally) {
178 | return this.then(onFinally, onFinally)
179 | }
180 | ```
181 |
182 | ## 实现静态方法
183 |
184 | ### resolve
185 |
186 | - 传入Promise:直接返回
187 | - 传入值:转成fulfilled状态的Promise
188 |
189 | ```js
190 | static resolve(value) {
191 | if (value instanceof MyPromise) {
192 | return value;
193 | } else {
194 | return new MyPromise((resolve, _) => resolve(value))
195 | }
196 | }
197 | ```
198 |
199 | ### reject
200 |
201 | - 不论传入的参数如何,返回一个rejected状态的Promise
202 |
203 | ```js
204 | static reject(value) {
205 | return new MyPromise((_, reject) => reject(value))
206 | }
207 | ```
208 |
209 |
210 | ### race
211 |
212 | 返回的Promise会随着第一个Promise的敲定(包括兑现和拒绝)而敲定。
213 |
214 | ```js
215 | static race(promises) {
216 | return new MyPromise((resolve, reject) => {
217 | if (!Array.isArray(promises)) {
218 | return reject(new TypeError('Argument is not iterable'))
219 | }
220 | promises.forEach(p => {
221 | MyPromise.resolve(p).then(resolve, reject)
222 | })
223 | })
224 | }
225 | ```
226 |
227 | ### all
228 |
229 | 当所有输入的Promise都被兑现时,返回一个包含所有兑现值的数组。如果输入的任何Promise被拒绝,则返回的Promise将被拒绝并带有第一个被拒绝的原因。
230 |
231 | ```js
232 | static all(promises) {
233 | return new MyPromise((resolve, reject) => {
234 | if (!Array.isArray(promises)) {
235 | return reject(new TypeError('Argument is not iterable'))
236 | }
237 | if (promises.length === 0) {
238 | return resolve([])
239 | }
240 | promises.reduce((acc, cur, i) => {
241 | MyPromise.resolve(cur).then(res => {
242 | acc[i] = res
243 | if (Object.keys(acc).length === promises.length) {
244 | resolve(acc)
245 | }
246 | return acc
247 | }, err => {
248 | reject(err)
249 | })
250 | }, [])
251 | })
252 | }
253 | ```
254 |
255 | ### allSettled
256 |
257 | 当所有输入的Promise都已经敲定时,返回的Promise将被兑现,并带有描述每个Promise结果的对象数组。
258 |
259 | ```js
260 | static allSettled(promises) {
261 | return new MyPromise((resolve, reject) => {
262 | if (!Array.isArray(promises)) {
263 | return reject(new TypeError('Argument is not iterable'))
264 | }
265 | if (promises.length === 0) {
266 | return resolve([])
267 | }
268 | promises.reduce((acc, cur, i) => {
269 | MyPromise.resolve(cur).then(res => {
270 | acc[i] = { status: FULFILLED, value: res }
271 | if (Object.keys(acc).length === promises.length) {
272 | resolve(acc)
273 | }
274 | return acc
275 | }, err => {
276 | acc[i] = { status: REJECTED, reason: err }
277 | if (Object.keys(acc).length === promises.length) {
278 | resolve(acc)
279 | }
280 | return acc
281 | })
282 | }, [])
283 | })
284 | }
285 | ```
286 |
287 | ### any
288 |
289 | 当输入的任何一个Promise被兑现时,返回的Promise将被兑现,并带有第一个兑现值。如果所有Promise都被拒绝,则返回的Promise将被拒绝,并带有所有拒绝原因的数组。
290 |
291 | ```js
292 | static any(promises) {
293 | return new MyPromise((resolve, reject) => {
294 | if (!Array.isArray(promises)) {
295 | return reject(new TypeError('Argument is not iterable'))
296 | }
297 | if (promises.length === 0) {
298 | return reject(new AggregateError('All promises were rejected'))
299 | }
300 | promises.reduce((acc, cur, i) => {
301 | MyPromise.resolve(cur).then(resolve, err => {
302 | acc[i] = err
303 | if (Object.keys(acc).length === promises.length) {
304 | reject(new AggregateError(acc))
305 | }
306 | return acc
307 | })
308 | }, [])
309 | })
310 | }
311 | ```
312 |
--------------------------------------------------------------------------------
/snippets/Promise/产品360度演示.js:
--------------------------------------------------------------------------------
1 | const pipeline = (initialValue, sequence) => {
2 | return sequence.reduce((prevPromise, fn) => {
3 | const result = prevPromise.then(fn)
4 | console.log(result);
5 | return result;
6 | }, Promise.resolve(initialValue));
7 | };
8 |
9 | const pipeline2 = async (initialValue, sequence) => {
10 | return sequence.reduce(async (prevPromise, fn) => {
11 | let result = await prevPromise;
12 | return fn(result);
13 | }, Promise.resolve(initialValue));
14 | };
15 |
16 |
--------------------------------------------------------------------------------
/snippets/Vue3/GithubDesktop.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 辅助函数,用于深拷贝对象
3 | * @param {Object} obj
4 | * @returns
5 | */
6 | function deepClone(obj) {
7 | if (Array.isArray(obj)) {
8 | return obj.map((it) => deepClone(it));
9 | } else if (typeof obj === "object") {
10 | const result = {};
11 | for (let key in obj) {
12 | result[key] = deepClone(obj[key]);
13 | }
14 | return result;
15 | } else {
16 | return obj;
17 | }
18 | }
19 |
20 |
21 | /**
22 | * TODO: 待补充代码 目标 4
23 | * @param {Ref} someRef Vue的某个ref对象,需要对传入的ref对象的历史状态做记录
24 | * @returns 返回一个对象,其中包含函数 undo 和 redo; undo 表示撤销,比如给 someRef 设置一个新状态后,调用 undo 可以将 someRef 还原为上一个旧状态;同理,在旧状态调用 redo 可以将 someRef 恢复为新状态
25 | */
26 | function useRefHistory(someRef) {
27 | const { ref, watch } = Vue;
28 | const history = [someRef.value]
29 | let idx = 0
30 | let flag = true // 手动更新state而不是调用Undo redo
31 |
32 | watch(someRef, (newValue, _) => {
33 | if (flag) {
34 | history.push(deepClone(newValue))
35 | idx = history.length - 1
36 | } else {
37 | flag = true
38 | }
39 | }, {deep: true})
40 |
41 | function undo() {
42 | /**
43 | * 调用 undo 时,将 someRef 撤销为上一个状态
44 | */
45 | console.log("undo", someRef.value, history);
46 | flag = false;
47 | idx -= 1
48 | someRef.value = deepClone(history[idx])
49 | console.log("undo", someRef.value, history);
50 | }
51 |
52 | function redo() {
53 | /**
54 | * 调用 redo 时,将 someRef 恢复为下一个状态
55 | */
56 | console.log("redo", someRef.value);
57 | flag = false;
58 | idx += 1
59 | someRef.value = deepClone(history[idx])
60 | console.log("redo", someRef.value);
61 | }
62 |
63 | return {
64 | undo,
65 | redo,
66 | };
67 | }
68 |
--------------------------------------------------------------------------------
/snippets/Vue3/Github明星项目统计.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Github 明星项目统计
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
30 |
31 |
32 |
125 |
126 |
--------------------------------------------------------------------------------
/snippets/Vue3/个性化桌面.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 个性化桌面
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
43 |
44 |
45 |
46 |
.svg)
47 |
垃圾桶
48 |
49 |
50 |
51 |
52 |
54 |
.svg)
55 |
56 |
57 |
63 |
64 |
65 |
66 | {{ item.name }}
67 |
68 |
69 |
70 |
71 |
74 |
75 |
78 |
81 |
82 |
85 |
88 |
91 |
92 |
93 |
94 |
95 |
96 |
113 |
114 |
115 |
116 |
117 |
118 |

119 |
{{ file.changeFile.name }}
120 |
121 |
122 |

123 |
124 |
125 |
126 |
File
127 |
Edit
128 |
Tools
129 |
Format
130 |
View
131 |
Help
132 |
133 |
134 |
135 |
136 |
137 |
140 |
141 |
142 |
143 |
144 |
146 |
147 |

148 |
Do you want to save changes you made?
149 |
Save
150 |
Don't Save
151 |
Cancel
152 |
153 |
154 |
155 |
156 |
157 |
348 |
349 |
--------------------------------------------------------------------------------
/snippets/Vue3/个性化桌面.js:
--------------------------------------------------------------------------------
1 | /**
2 | * https://www.lanqiao.cn/problems/19912/learning/?contest_id=248
3 | */
4 |
5 |
6 | //注册pinia仓库
7 | const { defineStore } = Pinia;
8 | const { ref, computed } = Vue;
9 | const { ElMessage } = ElementPlus;
10 | const useFileStore = defineStore("file", () => {
11 | const fileList = ref(JSON.parse(localStorage.getItem("fileList")) || []);
12 | const bgcList = ref([
13 | {
14 | url: "./images/bgc1.jpg",
15 | key: 1,
16 | },
17 | {
18 | url: "./images/bgc2.jpg",
19 | key: 2,
20 | },
21 | {
22 | url: "./images/bgc3.jpg",
23 | key: 3,
24 | },
25 | {
26 | url: "./images/bgc4.jpg",
27 | key: 4,
28 | },
29 | {
30 | url: "./images/bgc5.jpg",
31 | key: 5,
32 | },
33 | {
34 | url: "./images/bgc6.jpg",
35 | key: 6,
36 | },
37 | ]);
38 | const currentUrl = ref(localStorage.getItem("bgcUrl") || "./images/bgc1.jpg");
39 |
40 | // 使用 computed 计算容器的样式
41 | const containerStyle = computed(() => ({
42 | backgroundImage: `url(${currentUrl.value})`,
43 | backgroundSize: "cover",
44 | backgroundPosition: "center",
45 | backgroundRepeat: "no-repeat",
46 | }));
47 |
48 | const editorVisible = ref(false);
49 | const changeFile = ref({});
50 | const changeFileIndnx = ref(0);
51 | const changeFileText = ref("");
52 | const isSaveVisible = ref(false);
53 |
54 | // 编辑文件名
55 | const renameFile = (index) => {
56 | fileList.value[index].isEditing = true;
57 | };
58 |
59 | // 保存文件名
60 | const saveEdit = (item, index) => {
61 | const formattedFilename = (filename) =>
62 | filename
63 | .split(".")
64 | .map((part, i) => (i === 0 ? part.slice(0, 8) : part.slice(0, 3)))
65 | .join(".");
66 | fileList.value[index].name = formattedFilename(item.name);
67 |
68 | fileList.value[index].isEditing = false;
69 | localStorage.setItem("fileList", JSON.stringify(fileList.value));
70 | };
71 |
72 | //关闭文件的按钮事件
73 | const closeEditorBtn = () => {
74 | isSaveVisible.value = true;
75 | };
76 |
77 | /**
78 | * 打开文件
79 | * @param {*} file
80 | * @param {*} index
81 | */
82 |
83 | const openFile = (file, index) => {
84 | editorVisible.value = true;
85 | changeFile.value = file;
86 | changeFileIndnx.value = index;
87 | changeFileText.value = file.content;
88 | };
89 |
90 | /**
91 | * 保存文件按钮事件
92 | */
93 | const saveText = () => {
94 | changeFile.value.content = changeFileText.value;
95 | fileList.value[changeFileIndnx.value] = changeFile.value;
96 | localStorage.setItem("fileList", JSON.stringify(fileList.value));
97 |
98 | editorVisible.value = false;
99 | isSaveVisible.value = false;
100 |
101 | ElMessage({
102 | message: "Save Successfully!",
103 | type: "success",
104 | });
105 | };
106 |
107 | // 不保存文件的按钮事件
108 | const notSaveText = () => {
109 | editorVisible.value = false;
110 | isSaveVisible.value = false;
111 | ElMessage({
112 | message: "Not Save!",
113 | type: "warning",
114 | });
115 | };
116 |
117 |
118 | /**
119 | * 切换背景图
120 | * @param {*} url
121 | */
122 | const changeBgc = (url) => {
123 | // TODO1 :待补充代码
124 | currentUrl.value = url;
125 | localStorage.setItem("bgcUrl", url);
126 | // 提示:window.addEventListener('storage', ...) 可以监听本地存储的值变化
127 | };
128 |
129 | /**
130 | * 生成随机的3个字符的字符串
131 | * @param {*} length
132 | * @returns string
133 | */
134 | function generateRandomString(length = 3) {
135 | // TODO2 : 待补充代码
136 | // 生成a-z的数组
137 | let mapping = Array.from({ length: 26 },
138 | (_, i) => String.fromCharCode("a".charCodeAt(0) + i));
139 | // 生成0-25的整数数组
140 | return Array.from({ length }, () => Math.floor(Math.random() * 26))
141 | // 映射到a-z
142 | .map(num => mapping[num])
143 | .join("")
144 | }
145 |
146 | /**
147 | * 新增默认文件
148 | */
149 |
150 | const addFile = () => {
151 | //TODO2 : 待补充代码
152 | let d = new Date();
153 | const p = (num) => String(num).padStart(2, "0")
154 | fileList.value.push({
155 | name: generateRandomString() + ".txt",
156 | content: "",
157 | createdDate: `${d.getFullYear()}/${p(d.getMonth() + 1)}/${p(d.getDate())}`
158 | + ` ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`
159 | })
160 | console.log(fileList.value);
161 | localStorage.setItem("fileList", JSON.stringify(fileList.value)); // 从上面抄
162 | };
163 |
164 |
165 | /**
166 | * 文件大小排序
167 | */
168 | const sizeSort = () => {
169 | // TODO3 : 待补充代码
170 | fileList.value.sort((a, b) => a.content.length - b.content.length)
171 | // 提示:一定要观察一下他让你定义的函数在哪里被使用,比如这里就需要我们更新本地存储
172 | localStorage.fileList = JSON.stringify(fileList.value);
173 | };
174 |
175 | /**
176 | * 文件日期排序
177 | */
178 |
179 | const dateSort = () => {
180 | // TODO3 : 待补充代码
181 | fileList.value.sort((a, b) =>
182 | new Date(a.createdDate).getTime() - new Date(b.createdDate).getTime())
183 | localStorage.fileList = JSON.stringify(fileList.value);
184 | };
185 |
186 |
187 | return {
188 | fileList,
189 | addFile,
190 | saveEdit,
191 | openFile,
192 | editorVisible,
193 | changeFile,
194 | isSaveVisible,
195 | changeFileText,
196 | closeEditorBtn,
197 | saveText,
198 | notSaveText,
199 | renameFile,
200 | bgcList,
201 | changeBgc,
202 | containerStyle,
203 | currentUrl,
204 | sizeSort,
205 | dateSort,
206 | };
207 | });
208 |
--------------------------------------------------------------------------------
/snippets/Vue3/多表单校验.js:
--------------------------------------------------------------------------------
1 | const validateName = (rule, value, callback) => {
2 | if (/[^\u4e00-\u9fa5]/g.test(value)) { // 检查非汉字要比检查为汉字要简单
3 | // if (!/^[\u4e00-\u9fa5]+$/.test(value)) { // 检查汉字
4 | callback(new Error("只能输入汉字"))
5 | } else if (value.trim() == "") {
6 | callback(new Error("请输入姓名"))
7 | } else {
8 | callback()
9 | }
10 | }
11 |
12 | // TODO:待补充代码
13 | const rules = reactive({
14 | name: {
15 | required: true,
16 | validator: validateName
17 | },
18 | sex: { required: true, message: '请选择性别' },
19 | age: { required: true, message: '请输入年龄' },
20 | isCompetition: { required: true, message: '请选择是否参加过编程比赛' },
21 | isEntrepreneurship: { required: true, message: '请选择是否有过创业经历' },
22 | })
--------------------------------------------------------------------------------
/snippets/Vue3/学生探览.js:
--------------------------------------------------------------------------------
1 | /**
2 | * https://www.lanqiao.cn/problems/19909/learning/?contest_id=248
3 | *
4 | * 这个题的模板代码质量很高,可以学一下,巩固一下Vue3的写法。
5 | *
6 | */
7 |
8 | import { ref, computed } from "vue";
9 |
10 | const mockUrl = "./mock/data.json";
11 |
12 | export default {
13 | setup() {
14 | const historyPrompts = [
15 | "入营前是否有 Offer",
16 | "是否党员",
17 | "是否参赛过蓝桥杯",
18 | "是否学生会",
19 | "是否学生干部",
20 | "是否是计算机及相关专业",
21 | "是否签订合同",
22 | "是否考研",
23 | "是否面试过",
24 | ];
25 | const searchText = ref("");
26 | const itemIndex = ref(-1);
27 | const items = ref([]);
28 | const chartDiv = ref();
29 | const names = computed(() => items.value.map((it) => it.name));
30 | const item = computed(() =>
31 | itemIndex.value >= 0 ? items.value[itemIndex.value] : undefined
32 | );
33 | const itemTimestamp = computed(() => {
34 | if (item.value === undefined) return undefined;
35 | const dt = new Date(item.value.timestamp);
36 | const year = dt.getFullYear();
37 | let month = dt.getMonth() + 1;
38 | let day = dt.getDate();
39 | month = month < 10 ? "0" + month : month;
40 | day = day < 10 ? "0" + day : day;
41 | return year + "-" + month + "-" + day;
42 | });
43 | const itemSynthScore = computed(() =>
44 | item.value === undefined
45 | ? undefined
46 | : Math.round(
47 | item.value.scores.reduce((a, b) => a + b) /
48 | item.value.scores.length
49 | )
50 | );
51 | // TODO2 START 请在下面补充代码
52 | // const classAverageScores = ref([0, 0, 0]); // 请将此变量重写为计算属性
53 | const classAverageScores = computed(() =>
54 | items.value.reduce((acc, item) =>
55 | // map: Array(3) => Array(3)
56 | item.scores.map((score, index) => acc[index] + score),
57 | Array.from({ length: 3 }, () => 0)) // or: [0, 0, 0]
58 | .map(score => Math.round(score / items.value.length))
59 | );
60 |
61 | // TODO2 END
62 |
63 | function queryNames(text, cb) {
64 | cb(
65 | names.value
66 | .filter((n) => n.includes(text))
67 | .map((s) => ({ value: s }))
68 | );
69 | }
70 |
71 | function onSearch() {
72 | itemIndex.value = items.value.findIndex(
73 | (it) => it.name === searchText.value
74 | );
75 |
76 | if (item.value === undefined) return;
77 | setTimeout(function () {
78 | const chart = echarts.init(chartDiv.value);
79 | const option = {
80 | tooltip: {},
81 | legend: {
82 | data: ["我的表现", "班级平均表现"],
83 | },
84 | radar: {
85 | // TODO3 START 请在下面补充代码
86 | shape:'circle',
87 | startAngle: 0,
88 | indicator:[
89 | { name: '技术能力得分', max: 100, min: 0 },
90 | { name: '硬实力得分', max: 100, min: 0 },
91 | { name: '软技能得分', max: 100, min: 0 }
92 | ]
93 | // TODO3 END
94 | },
95 | series: [
96 | {
97 | type: "radar",
98 | areaStyle: {},
99 | data: [
100 | {
101 | value: item.value.scores,
102 | name: "我的表现",
103 | },
104 | {
105 | value: classAverageScores.value,
106 | name: "班级平均表现",
107 | },
108 | ],
109 | },
110 | ],
111 | };
112 | window.chartOption = option;
113 | try {
114 | chart.setOption(option);
115 | } catch {}
116 | }, 0);
117 | }
118 |
119 | // TODO1 START 请在下面补充代码
120 | axios.get(mockUrl).then(res => {
121 | items.value = res.data
122 | })
123 | // TODO1 END
124 |
125 | window.getData = function () {
126 | return items.value;
127 | };
128 | window.getCA = function () {
129 | return classAverageScores.value;
130 | };
131 | window.searchStudent = function (name) {
132 | searchText.value = name;
133 | onSearch();
134 | };
135 |
136 | return {
137 | historyPrompts,
138 | searchText,
139 | itemIndex,
140 | items,
141 | chartDiv,
142 | item,
143 | itemTimestamp,
144 | itemSynthScore,
145 | queryNames,
146 | onSearch,
147 | };
148 | },
149 | };
150 |
--------------------------------------------------------------------------------
/snippets/Vue3/小蓝驿站.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 小蓝驿站
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
27 |
28 |
29 |
30 |
41 |
42 |
43 |
通讯录
44 |
45 |
56 |
57 |
70 |
71 |
72 |
73 |
74 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/snippets/Vue3/需求管理/DemandList.js:
--------------------------------------------------------------------------------
1 | let DemandList = {
2 | // TODO:待补充代码 目标 1
3 | props: ["items"],
4 | emits: ['update-child', 'update-parent', 'toggle-item'],
5 | setup(props, { emit }) {
6 | const toggleItem = (item) => {
7 | emit('toggle-item', item);
8 | };
9 |
10 | const updateChild = (parent) => {
11 | emit('update-child', parent);
12 | };
13 |
14 | const updateParent = (item) => {
15 | emit('update-parent', item);
16 | };
17 |
18 | return {
19 | toggleItem,
20 | updateChild,
21 | updateParent,
22 | };
23 | },
24 | template: `
25 |
26 |
27 | -
32 |
33 |
34 |
35 | {{ item.expanded ? '▼' : '▶' }}
40 |
45 | {{ item.title }}
46 |
47 |
55 | {{ item.priority }}
56 |
57 |
{{ item.assignee }}
58 |
59 |
60 |
77 |
78 |
79 | `,
80 | };
81 |
--------------------------------------------------------------------------------
/snippets/Vue3/需求管理/FilterGroup.js:
--------------------------------------------------------------------------------
1 | // TODO:待补充代码
2 | const {watchEffect} = Vue;
3 | const FilterGroup = {
4 | emit: ["change"],
5 | setup(props, { emit }) {
6 | const [demand, bug, task] = [ref(true), ref(true), ref(true)]
7 | watchEffect(() => {
8 | emit( "change",
9 | Object.entries({demand: demand.value, bug: bug.value, task: task.value})
10 | .filter(([k, v]) => v)
11 | .map(([k, v]) => k))
12 | })
13 | return { demand, bug, task }
14 | },
15 | template: `
16 |
17 | 过滤:
18 |
21 |
24 |
27 |
28 | `,
29 | };
30 |
--------------------------------------------------------------------------------
/snippets/Vue3/需求管理/Stats.js:
--------------------------------------------------------------------------------
1 | // TODO:待补充代码
2 | const Stats = {
3 | props: ["items"],
4 | setup(props) {
5 | const [demand, bug, task]
6 | = ["demand", "bug", "task"].map(type =>
7 | computed(() => [
8 | props.items.filter(it => it.type == type).length,
9 | props.items.filter(it => it.type == type && it.completed).length
10 | ])
11 | )
12 | return {
13 | demand, bug, task
14 | }
15 | },
16 | template: `
17 |
18 |
19 | 需求:{{demand[1]}}/{{demand[0]}}
20 |
21 |
22 | 缺陷:{{bug[1]}}/{{bug[0]}}
23 |
24 |
25 | 任务:{{task[1]}}/{{task[0]}}
26 |
27 |
28 | `,
29 | };
30 |
--------------------------------------------------------------------------------
/snippets/Vue3/需求管理/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 需求管理
9 |
10 |
11 |
12 |
16 |
17 |
23 |
24 |
25 |
26 |
27 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/snippets/模拟1/2.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 待完成单词复习,跳到下一个单词
3 | */
4 | function waitReview() {
5 | // TODO: 请补充代码
6 | // index == 10时回到1
7 | if (index == words.length) {
8 | index = 1
9 | } else {
10 | index++;
11 | }
12 | renderData(index)
13 | }
14 |
15 | /**
16 | * @description 完成单词复习,删除当前单词,跳到下一个单词
17 | */
18 | function completeReview() {
19 | // TODO: 请补充代码
20 | if (words.length >= 2) {
21 | words = words.filter((_, idx) => idx !== index - 1)
22 | // 0 1 2 3 4
23 | // index = 2
24 | // ^
25 | // 0 2 3 4
26 | console.log(words);
27 | if (index == words.length + 1) {
28 | index = 1;
29 | }
30 | renderData(index)
31 | // TODO: END
32 | waitTaskBox.innerText = `还需复习:${words.length}`;
33 | } else {
34 | waitTaskBox.innerText = `还需复习:0`;
35 | document.querySelector("#word").innerHTML = `
36 | 今日复习已完成
37 | `
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/snippets/模拟1/4.js:
--------------------------------------------------------------------------------
1 | // 初始化 ECharts 实例
2 | const chart = echarts.init(document.getElementById('main'));
3 |
4 | /**人口数据增长推算函数
5 | * @param {number} initialPopulation 基础人口数,假设有 10 亿
6 | * @param {number} growthRate 年增长率,假设为 3%
7 | * @param {number} startYear 起始年份,假设为 200000
8 | */
9 | function calculatePopulation(initialPopulation, growthRate, startYear){
10 | const years = Array.from({ length: 31 }, (_, idx) => startYear + idx);
11 | // const population = Array.from({ length: 30 });
12 | // 这么写判题过不了😭
13 | // population.forEach((_, idx) =>
14 | // population[idx] = (population[idx - 1] ?? initialPopulation) * (1 + growthRate)
15 | // )
16 | const population = Array.from({ length: 31 }, (_, idx) =>
17 | initialPopulation * (Math.pow(1 + growthRate, idx))
18 | );
19 | // TODO:待补充代码 目标 1
20 | console.log(years, population);
21 | return {years,population};
22 | }
23 |
24 | const pData = calculatePopulation(1000000000, 0.03, 200000);
25 |
26 | // 配置 ECharts 选项
27 | const option = {
28 | title: {
29 | text: '未来30年人口增长趋势图',
30 | subtext: '假设年增长率为3%',
31 | left: 'center'
32 | },
33 | tooltip: {
34 | trigger: 'axis',
35 | // TODO:待补充代码 目标 3
36 | formatter: (params) => {
37 | // console.log(params);
38 | return `${params[0].name}
人口:${Math.round(params[0].data / 1000000)}M
`
39 | }
40 | },
41 | xAxis: {
42 | type: 'category',
43 | // TODO:待修改代码 目标 2
44 | // 年份
45 | data: pData.years,
46 | name: '年份',
47 | boundaryGap: false
48 | },
49 | yAxis: {
50 | type: 'value',
51 | name: '人口数',
52 | axisLabel: {
53 | // TODO:待补充代码 目标 4
54 | formatter: (value) => {
55 | return `${Math.round(value / 1000000)}M`
56 | }
57 | }
58 | },
59 | series: [
60 | {
61 | name: '人口',
62 | type: 'line',
63 | // TODO:待修改代码 目标 2
64 | // 人口数量
65 | data: pData.population,
66 | smooth: true,
67 | lineStyle: {
68 | color: '#3398DB'
69 | },
70 | itemStyle: {
71 | color: '#3398DB'
72 | }
73 | }
74 | ]
75 | };
76 |
77 | // 使用刚指定的配置项和数据显示图表
78 | chart.setOption(option);
79 |
--------------------------------------------------------------------------------
/snippets/模拟1/5.js:
--------------------------------------------------------------------------------
1 | const List = {
2 | template: `
3 |
4 |
5 |
6 |
7 |
8 |
9 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
{{ photo.title }}
21 |
22 |
23 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | `,
37 | props:['photos'],
38 | setup(props) {
39 | let photos = props.photos
40 | return {
41 | photos
42 | };
43 | },
44 | };
45 |
--------------------------------------------------------------------------------
/snippets/模拟1/6.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | async function streamMergeRecursive(imgNameList, writeStream, deleFileList = []) {
5 | console.log(imgNameList, writeStream, deleFileList);
6 |
7 | for (const name of imgNameList) {
8 | await new Promise((resolve, reject) => {
9 | const readStream = fs.createReadStream(path.resolve(baseDir, name));
10 | readStream.pipe(writeStream, { end: false });
11 | // 这个api没给,如果想实现异步(等待流传输完成)还不用这个api,只能手动sleep
12 | readStream.on('end', () => {
13 | deleFileList.push(name);
14 | resolve();
15 | });
16 | // readStream.on('error', reject);
17 | });
18 | }
19 |
20 | writeStream.end();
21 |
22 | await deleFile(imgNameList);
23 | }
24 |
--------------------------------------------------------------------------------
/snippets/模拟1/7.js:
--------------------------------------------------------------------------------
1 | const handleSubmit = () => {
2 | // TODO 目标1 待补充代码
3 | emit("submit-add")
4 | };
5 |
6 | const handleCancel = () => {
7 | // TODO 目标1 待补充代码
8 | emit("close-add")
9 | };
10 |
11 |
12 | /**
13 | * 处理保存事件
14 | */
15 | const handleSave = () => {
16 | if (checkBlank()) return;
17 | if (addType.value === 0) {
18 | // 新增员工
19 | let worker_one = { checked: false, name: form.name, phone: form.phone }
20 | worker.value.push(worker_one)
21 | } else {
22 | // TODO:待补代码目标 2
23 | worker.value[editIndex.value] = { ...worker.value[editIndex], ...form }
24 | }
25 | showAddPop.value = false;
26 | }
27 |
28 |
29 | /**
30 | * 批量删除
31 | */
32 | const deleteItems = () => {
33 | // TODO:待补充代码 目标 3
34 | worker.value = worker.value.filter(it => !it.checked)
35 | }
36 |
37 | /**
38 | * 删除单个员工
39 | * @param item 员工信息
40 | */
41 | const deleteItem = item => {
42 | // TODO:待补充代码 目标 3
43 | worker.value = worker.value.filter(it => it != item)
44 | }
45 |
--------------------------------------------------------------------------------
/snippets/模拟1/第9、10题都在前面讲过.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/snippets/模拟2/2.js:
--------------------------------------------------------------------------------
1 | document.querySelector("#sign_in_btn").addEventListener("click", () => {
2 | const today = new Date().getDate();
3 | [...document.querySelector("#days").children].find(item =>
4 | item.textContent === String(today)).classList.add("active")
5 | document.querySelector("#sign_in_btn").classList.add("no-active")
6 | document.querySelector("#sign_in_btn").textContent = "明天也要记得来哦"
7 | })
8 |
--------------------------------------------------------------------------------
/snippets/模拟2/3.js:
--------------------------------------------------------------------------------
1 | class Storage {
2 | constructor() {
3 | this.storage2 = window.localStorage;
4 | }
5 |
6 | setItem(key, value, expired) {
7 | // TODO 待补充代码
8 | this.storage2.setItem(key, value);
9 | this.storage2.setItem(key + "!", new Date(new Date().getTime() + expired))
10 | }
11 |
12 | getItem(key) {
13 | // TODO 待补充代码
14 | if (new Date(localStorage.getItem(key + "!")) < new Date()) {
15 | this.storage2.removeItem(key)
16 | }
17 | return this.storage2.getItem(key)
18 |
19 | }
20 |
21 | removeItem(key) {
22 | // TODO 待补充代码
23 | this.storage2.removeItem(key)
24 | }
25 | }
26 |
27 | const storage = new Storage();
28 |
29 | // 为了检测时使用,请勿删除
30 | if (window) {
31 | window.storage = storage;
32 | }
33 |
--------------------------------------------------------------------------------
/snippets/模拟2/4.js:
--------------------------------------------------------------------------------
1 | function parseContactInfo(text) {
2 | // TODO: 请补充代码
3 | const phoneRe = /(\d{11})\s/
4 | const nameRe = /([\u4e00-\u9fa5]{2,4})\s/
5 | const addressRe = /([\u4e00-\u9fa5][\u4e00-\u9fa5a-zA-Z0-9]{3,100})\s/
6 | text = text.replace(",", "").replace(",", "")
7 | text += " "
8 | return {
9 | phone: +(phoneRe.exec(text)?.[1] ?? 0),
10 | name: nameRe.exec(text)?.[1] ?? "",
11 | address: addressRe.exec(text)?.[1] ?? ""
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/snippets/模拟2/5.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 组合多个中间件函数,形成一个新的中间件函数。
3 | * @param {...Function} middlewares - 要组合的中间件函数。
4 | * @return {Function} - 组合后的中间件函数。
5 | */
6 | function compose(...middlewares) { // 形参有函数
7 | return (init, callback) => { // 形参有函数
8 | let curr = init;
9 |
10 | function next(value) {
11 | curr = value;
12 | }
13 |
14 | middlewares.forEach(fn => {
15 | fn(curr, next); // 调用传参数
16 | });
17 |
18 | callback(curr); // 调用传参数
19 | }
20 | }
21 |
22 | function add(str, next) { // 形参有函数
23 | str += '2'
24 | next(str); // 调用传参数
25 | }
26 | function add2(str, next) {
27 | str += '3'
28 | next(str);
29 | }
30 | const processdemo = compose(add, add2);
31 | processdemo("1", (finalValue) => { // 实参有函数
32 | console.log(finalValue) // 123 // 直接拿参数
33 | });
34 |
--------------------------------------------------------------------------------
/snippets/模拟2/6.js:
--------------------------------------------------------------------------------
1 | function generateStaticFilesMap(dir) {
2 | // TODO:待补充代码
3 | const stack = [{
4 | filePath: dir
5 | }]
6 | let result = []
7 | while (stack.length > 0) {
8 | const item = stack.pop()
9 | fs.readdirSync(item.filePath).forEach((sub) => {
10 | const subPath = path.join(item.filePath, sub)
11 | // console.log(subPath);
12 | const isDir = fs.statSync(subPath).isDirectory()
13 | if (isDir) {
14 | stack.push({
15 | filePath: subPath
16 | })
17 | } else {
18 | result.push({
19 | filePath: subPath,
20 | contentType: getContentType(subPath)
21 | })
22 | }
23 | })
24 | }
25 | result = Object.fromEntries(result.map((res) => ["/" + path.relative(dir, res.filePath), res]))
26 | console.log(result);
27 | return result
28 | }
29 |
--------------------------------------------------------------------------------
/snippets/模拟2/7.js:
--------------------------------------------------------------------------------
1 | const fetchMeetingData = async () => {
2 | // TODO: 待补充代码
3 | const res = await fetch("./js/meetings.json");
4 | const json = await res.json();
5 | participants.value = json["participants"];
6 | delete json["participants"];
7 | meeting.value = json;
8 | console.log(participants.value);
9 | console.log(meeting.value);
10 | };
11 |
12 | const formatDate = (dateStr) => {
13 | // TODO: 待补充代码
14 | if (dateStr) {
15 | let [year, month, date] = dateStr.split("-")
16 | month = month.padStart(2, "0")
17 | date = date.padStart(2, "0")
18 | return year + "-" + month + "-" + date
19 | }
20 | };
21 |
22 | onMounted(async () => {
23 | // 获取会议数据
24 | await fetchMeetingData();
25 |
26 | // 模拟参与者加入会议
27 | await simulateParticipantJoin();
28 | });
29 |
30 | function myAllSettled(promises) {
31 | // TODO: 待补充代码
32 | // 不能使用 Promise.allSettled, Promise.all,Promise.race 等方法
33 | return new Promise((resolve, reject) => {
34 | const results = []
35 | promises.forEach((p, idx) => {
36 | Promise.resolve(p).then((res) => {
37 | results[idx] = res
38 | if (Object.keys(results).length == promises.length) {
39 | resolve(results)
40 | }
41 | }, (rej) => {
42 | results[idx] = rej
43 | if (Object.keys(results).length == promises.length) {
44 | resolve(results)
45 | }
46 | })
47 | })
48 | });
49 | }
50 |
--------------------------------------------------------------------------------
/snippets/浏览器/冬奥大抽奖.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2022年省赛。JQuery改的浏览器原生题目。中等难度。
3 | */
4 |
5 | let rollTime; // 定义定时器变量用来清除定时器
6 | let time = 0; // 转动次数
7 | let speed = 300; // 转动时间间隔
8 | let times; // 总转动次数
9 |
10 | /**
11 | * @type {(selector: String) => HTMLElement}
12 | */
13 | const $$ = document.querySelector.bind(document)
14 |
15 |
16 | // 开始按钮点击事件后开始抽奖
17 | $("#start").on("click", function () {
18 | $("#award").text(""); //清空中奖信息
19 | times = parseInt(Math.random() * (20 - 30 + 1) + 20, 10); // 定义总转动次数,随机20-30次
20 | rolling();
21 | });
22 |
23 | // TODO:请完善此函数
24 | function rolling() {
25 | time++; // 转动次数加1
26 | clearTimeout(rollTime);
27 | rollTime = setTimeout(() => {
28 | window.requestAnimationFrame(rolling); // 进行递归动画
29 | }, speed);
30 |
31 | // HERE
32 | let curr = time % 8 == 0 ? 8 : time % 8
33 | $$(`.li${curr}`).classList.add("active")
34 | Array.from({ length: 8 }, (_, i) => i + 1)
35 | .filter(i => i != curr)
36 | .forEach(i => $$(`.li${i}`).classList.remove("active"))
37 | // OR
38 | // let licur = $$(`.li${curr}`)
39 | // ([...licur.parentElement.children]).forEach(e => e.classList.remove("active"))
40 | // HTML Collection是一个类数组对象,没有定义forEach,要转成数组
41 | // licur.classList.add("active")
42 |
43 |
44 | // time > times 转动停止
45 | if (time > times) {
46 | clearInterval(rollTime);
47 | time = 0;
48 | $$("#award").textContent = "恭喜您抽中了" + $$(`.li${curr}`).textContent + "!!!"
49 | return;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/snippets/浏览器/图片水印生成.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2023年省赛。低难度。
3 | */
4 |
5 | /**
6 | * 创建一个文字水印的div
7 | * @param {string} text - 水印文字
8 | * @param {string} color - 水印颜色
9 | * @param {number} deg - 水印旋转角度
10 | * @param {number} opacity - 水印透明度
11 | * @param {number} count - 水印数量
12 | */
13 | function createWatermark(text, color, deg, opacity, count) {
14 | // 创建水印容器
15 | const container = document.createElement("div");
16 | container.className = "watermark";
17 |
18 | // TODO: 根据输入参数创建文字水印
19 | Array.from({ length: count }).forEach(() => {
20 | let span = document.createElement('span')
21 | span.innerHTML = text
22 | span.style.color = color
23 | span.style.opacity = opacity
24 | span.style.transform = `rotate(${deg}deg)`
25 | // 这个不会用可以去css文件里试一下,有补全
26 | container.append(span)
27 | })
28 | return container;
29 | }
--------------------------------------------------------------------------------
/snippets/浏览器/布局切换.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2024年省赛。签到题。
3 | */
4 | document.querySelector('.active').classList.remove('active')
5 | this.classList.add('active')
--------------------------------------------------------------------------------
/snippets/浏览器/年度明星项目.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2023年省赛。中等难度。
3 | */
4 |
5 | // 翻译数据
6 | let translation = {};
7 | // 当前语言
8 | let currLang = "zh-cn";
9 | // 对象数据
10 | let data = []
11 | // 第几页,idx
12 | let page = 0
13 |
14 | // 请求数据和初始展示
15 | window.onload = async () => {
16 | const res1 = await fetch("./js/all-data.json")
17 | data = await res1.json()
18 | const res2 = await fetch("./js/translation.json")
19 | translation = await res2.json()
20 | data.slice(0, 15).forEach(item => $(".list > ul").append(createProjectItem(
21 | {...item, description: currLang == "zh-cn" ? item.descriptionCN : item.descriptionEN}
22 | )))
23 | }
24 |
25 | // 加载更多的回调
26 | document.querySelector(".load-more").addEventListener("click", function() {
27 | // 注意:page是更新前的索引
28 | // page == 0
29 | // idx == 15
30 | data.slice((page + 1) * 15, (page + 2)* 15).forEach(item => $(".list > ul").append(createProjectItem(
31 | {...item, description: currLang == "zh-cn" ? item.descriptionCN : item.descriptionEN}
32 | )))
33 |
34 | // 比如一共4页时,第3页(page == 2)就该隐藏
35 | if (page == Math.ceil(data.length) / 15 - 2) {
36 | this.style.display = "none"
37 | }
38 |
39 | // 状态最后更新
40 | page++
41 | })
42 |
43 |
44 |
45 | // TODO-END
46 |
47 | // 用户点击切换语言的回调
48 | $(".lang").click(() => {
49 | // 切换页面文字的中英文
50 | if (currLang === "en") {
51 | $(".lang").text("English");
52 | currLang = "zh-cn";
53 | } else {
54 | $(".lang").text("中文");
55 | currLang = "en";
56 | }
57 | $("body")
58 | .find("*")
59 | .each(function () {
60 | const text = $(this).text().trim();
61 | if (translation[text]) {
62 | $(this).text(translation[text]);
63 | }
64 | });
65 | // TODO: 请在此补充代码实现项目描述的语言切换
66 | // 从浏览器中复制出选择器,去掉伪类选择器
67 | document.querySelectorAll("body > div > div.list > ul > li > div.desc > p").forEach(item => {
68 | // console.log(item);
69 | item.textContent =
70 | currLang == "zh-cn"
71 | ? data.find(i => i.descriptionEN == item.textContent).descriptionCN
72 | : data.find(i => i.descriptionCN == item.textContent).descriptionEN
73 | })
74 | // 或者:清空,重新插入
75 | });
76 |
77 | // 生成列表DOM元素的函数,将该元素的返回值append至列表中即可生成一行项目数据
78 | /**
79 | * @param {string} name - 项目名称
80 | * @param {string} description - 项目描述
81 | * @param {string[]} tags - 项目标签
82 | * @param {number} stars - 项目star数量
83 | * @param {string} icon - 项目icon路径
84 | */
85 | function createProjectItem({ name, description, tags, stars, icon }) {
86 | return `
87 |
88 |
89 |
90 |
${name}
91 |
${description}
92 |
93 | ${tags.map((tag) => `- ${tag}
`).join("")}
94 |
95 |
96 |
97 | +${stars} 🌟
98 |
99 |
100 | `;
101 | }
102 |
--------------------------------------------------------------------------------
/snippets/浏览器/新增地址.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2022年国赛。中高难度。
3 | */
4 |
5 | // 初始化省份下拉列表内容
6 | function provinceInit() {
7 | var province = document.getElementById("param_province");
8 | province.length = provinces.length;
9 | for (var i = 0; i < provinces.length; i++) {
10 | province.options[i].text = provinces[i];
11 | province.options[i].value = provinces[i];
12 | }
13 | }
14 |
15 | // 选择省份后对应城市下拉列表内容渲染
16 | function provincechange() {
17 | // TODO:请补充代码实现功能
18 | let province = document.querySelector("#param_province").value
19 | let idx = provinces.findIndex(i => i === province) // 有一个方法叫indexOf
20 | // 思考:需不需要考虑“请选择城市”?
21 | // 删除
22 |
23 | // 注意:children是一个只读属性
24 | // document.querySelector("#param_city").children = ["请选择城市"]
25 | document.querySelector("#param_city").innerHTML = ""
26 |
27 | // 添加
28 | let choices = citys[idx]
29 | choices.forEach(city => {
30 | let option = document.createElement("option")
31 | option.textContent = city
32 | document.querySelector("#param_city").append(option)
33 | })
34 | }
35 |
36 | /**
37 | * 为标签绑定单击事件。
38 | * 事件效果为:
39 | * 1、鼠标点击该标签后该标签样式显示 class=active;
40 | * 2、其他已选标签的 active 样式被移除;
41 | * 3、将选中的标签对应下标(即选择器为 “mark a” 选中的标签对应的下标)更新到 id=param_mark 的隐藏的 input 中。
42 | */
43 | function addClick() {
44 | // TODO:请补充代码实现功能
45 | const spans = document.querySelectorAll(".mark a");
46 | spans.forEach((el) => {
47 | el.addEventListener("click", () => {
48 | document.querySelector('.active').classList.remove('active')
49 | el.classList.add("active");
50 | });
51 | });
52 | }
53 |
54 |
55 | // 提交信息后,读取并显示在页面中,第二题和第四题的任务
56 | function saveInfo() {
57 | // TODO:请补充代码实现功能
58 | // 姓名,电话,地址
59 | const param_phone = document.getElementById("param_phone").value;
60 | const param_name = document.getElementById("param_name").value;
61 | const param_address = document.getElementById("param_address").value;
62 | // 警告框
63 | const warning = document.querySelector(".warning-dialog");
64 | // 地址列表
65 | const addresslist = document.querySelector(".address-list");
66 | // 联系人信息
67 | const user = document.querySelector(".user-info");
68 | // 地址
69 | const address = document.querySelector(".address");
70 | // 选择的省,城市
71 | const param_province = document.getElementById("param_province");
72 | const city = document.getElementById("param_city");
73 | // 标签
74 | const span = document.querySelector(".active");
75 | const obj = { 家: "home", 公司: "company", 学校: "school" };
76 | const template = `
77 |
78 |
79 |
80 | ${param_province.value+city.value}
81 |
82 |
83 |
${param_address}
84 |

85 |
86 |
87 | ${param_name}
88 | ${param_phone}
89 |
90 |
91 | `;
92 | if (param_address && param_name && param_phone) {
93 | addresslist.style.display = "block";
94 | user.style.display = "none";
95 | address.style.display = "none";
96 | addresslist.innerHTML = template + addresslist.innerHTML;
97 | } else { // 显示弹框
98 | warning.style.display = "block";
99 | }
100 | }
101 |
102 | // 切换新增地址和地址管理的显隐
103 | function back() {
104 | if (document.getElementById("main_title").innerHTML == "地址管理") {
105 | document.getElementById("main_title").innerHTML = "新增地址";
106 | document.querySelector(".address-list").style.display = "none";
107 | document.querySelector(".address").style.display = "block";
108 | document.querySelector(".user-info").style.display = "block";
109 | }
110 | }
111 | // 页面加载后的初始化操作
112 | function init() {
113 | // 初始化省份下拉列表内容
114 | provinceInit();
115 | // 为标签绑定单击事件
116 | addClick();
117 | }
118 |
119 | window.onload = function () {
120 | // 初始化
121 | init();
122 | };
123 |
--------------------------------------------------------------------------------
/snippets/浏览器/水果消消乐.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2022年国赛。中高难度。
3 | */
4 |
5 | const $ = document.querySelector.bind(document)
6 | const $a = document.querySelectorAll.bind(document)
7 |
8 | const flipped = new Set() // 放Set,避免两次点击同一个。
9 | // TODO:请补充代码
10 | function startGame() {
11 | // 按下开始后隐藏开始键
12 | $("#start").style.display = "none"
13 | document.querySelectorAll(".img-box").forEach(box => {
14 | box.addEventListener("click", function (){
15 | this.firstElementChild.style.display = "block" // 显示图片
16 | flipped.add(this.id);
17 | if (flipped.size == 2) {
18 | let first = $(`#${[...flipped][0]}`);
19 | let second = $(`#${[...flipped][1]}`);
20 | if (first.firstElementChild.src === second.firstElementChild.src) {
21 | // 相同,格子消失
22 | first.style.visibility = "hidden";
23 | second.style.visibility = "hidden";
24 | first.firstElementChild.style.display = "none"
25 | second.firstElementChild.style.display = "none"
26 | $("#score").textContent = String(+scoreNode.textContent + 2)
27 | } else {
28 | // 不同,格子里的图片消失
29 | first.firstElementChild.style.display = "none"
30 | second.firstElementChild.style.display = "none"
31 | $("#score").textContent = String(+scoreNode.textContent - 2)
32 | }
33 | flipped.clear() // 清空
34 | }
35 | })
36 | })
37 | }
--------------------------------------------------------------------------------
/snippets/浏览器/灯的颜色变化.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2022年省赛。签到题。
3 | */
4 |
5 | /**
6 | * @type {(selector: String) => HTMLElement}
7 | */
8 | const $ = document.querySelector.bind(document)
9 |
10 | // TODO:完善此函数 显示红色颜色的灯
11 | function red() {
12 | $("#defaultlight").style.display = "none"
13 | $("#greenlight").style.display = "none"
14 | $("#redlight").style.display = "inline-block"
15 | }
16 |
17 | // TODO:完善此函数 显示绿色颜色的灯
18 | function green() {
19 | $("#defaultlight").style.display = "none"
20 | $("#redlight").style.display = "none"
21 | $("#greenlight").style.display = "inline-block"
22 | }
23 |
24 | // TODO:完善此函数
25 | function trafficlights() {
26 | setTimeout(red, 3000)
27 | setTimeout(green, 6000)
28 | }
29 |
30 | function trafficlights2() {
31 | setTimeout(() => {
32 | red()
33 | setTimeout(green, 3000)
34 | }, 3000)
35 | }
36 |
37 |
38 | trafficlights();
--------------------------------------------------------------------------------
/snippets/浏览器/真人鉴定器.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2024年国赛。签到题。
3 | */
4 |
5 | if (isPre) {
6 | if (thisIndex == 0) {
7 | thisIndex = 3
8 | } else {
9 | thisIndex -= 1
10 | }
11 | } else {
12 | if (thisIndex == 3) {
13 | thisIndex = 0
14 | } else {
15 | thisIndex += 1
16 | }
17 | }
--------------------------------------------------------------------------------
/snippets/浏览器/神奇的滤镜.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 中等难度。
3 | *
4 | * https://www.lanqiao.cn/problems/19905/learning/?contest_id=248.
5 | */
6 |
7 |
8 | // Solution1
9 | document.querySelector('header').onchange = ({ target }) =>
10 | filterTrigger.forEach((trigger, idx) => {
11 | filters[idx].style.display = trigger.checked ? 'block' : 'none'
12 | trigger == target && updateZIndex(filters[idx])
13 | });
14 |
15 | // Solution2
16 | filterTrigger.forEach(trigger => {
17 | // TODO:待补充代码
18 | trigger.addEventListener('change', ({ target }) => {
19 | console.log(target);
20 | const filterName = target.dataset.filterName;
21 | // const filter = document.querySelector(`.Filter[data-filter-name="${filterName}"]`)
22 | const filter = Array.from(filters).find(item => item.dataset.filterName === filterName)
23 | if (target.checked) {
24 | filter.style.display = "block"
25 | updateZIndex(filter)
26 | } else {
27 | filter.style.display = "none"
28 | }
29 | })
30 | });
--------------------------------------------------------------------------------
/snippets/浏览器/简易JSX解析器.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaojunran/lanqiao-web-slides/ab4a21d2b387ee1a6e1f7514571d3760d65c4016/snippets/浏览器/简易JSX解析器.js
--------------------------------------------------------------------------------
/snippets/浏览器/请到下一步.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2024年省赛。
3 | */
4 |
5 | /*TODO:请补充代码*/
6 | var current_form, next_form, previous_form; // 表单域
7 | const forms = document.querySelectorAll('fieldset')
8 | const list = document.querySelectorAll('ul li')
9 | let index = 0
10 | // 点击下一页的按钮
11 | $(".next").click(function () {
12 | current_form = $(this).parent();
13 | forms[index].style.display = 'none'
14 | forms[index].nextElementSibling.style.display = "block" // 相对定位
15 | // forms[index + 1].style.display = 'block' // 绝对定位
16 | list[index + 1].classList.add('active') // 思考:会越界吗?
17 | index++; // 比较好的实践是最后更新状态
18 | });
19 | // 点击返回按钮
20 | $(".previous").click(function () {
21 | current_form = $(this).parent();
22 | forms[index].style.display = 'none'
23 | forms[index - 1].style.display = 'block' // 思考:会越界吗?
24 | list[index].classList.remove('active')
25 | index--;
26 | });
27 | // 点击提交按钮
28 | $(".submit").click(function () {
29 | alert("提交成功");
30 | });
--------------------------------------------------------------------------------
/snippets/浏览器/课程列表.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 2022年省赛。中等难度。
3 | */
4 |
5 | let pageNum = 1; // 当前页码,默认页码1
6 | let maxPage; // 最大页数
7 | let data;
8 | const container = document.getElementById("list")
9 |
10 |
11 | // TODO:待补充代码
12 | window.onload = async () => {
13 | const res = await fetch("./js/carlist.json")
14 | data = await res.json()
15 | maxPage = Math.ceil(data.length / 5)
16 | container.innerHTML = ""
17 | data.slice((1 - 1) * 5, (1 - 1) * 5 + 5).forEach(show)
18 | }
19 |
20 | function show(item) {
21 | container.innerHTML += `
22 |
23 |
34 |
35 | `
36 | }
37 |
38 | // 点击上一页
39 | let prev = document.getElementById("prev");
40 | prev.onclick = function () {
41 | // 1 => 0 - 4
42 | // 2 => 5 - 9
43 | if (this.classList.contains("disabled")) {
44 | return // do nothing
45 | }
46 | pageNum--;
47 | container.innerHTML = ""
48 | data.slice((pageNum - 1) * 5, (pageNum) * 5).forEach(show)
49 | pageNum == 1 ? prev.classList.add("disabled") : prev.classList.remove("disabled")
50 | pageNum == maxPage ? next.classList.add("disabled") : next.classList.remove("disabled")
51 | // TODO:待补充代码
52 | };
53 |
54 | // 点击下一页
55 | let next = document.getElementById("next");
56 | next.onclick = function () {
57 | if (this.classList.contains("disabled")) {
58 | return // do nothing
59 | }
60 | pageNum++;
61 | container.innerHTML = ""
62 | data.slice((pageNum - 1) * 5, (pageNum) * 5).forEach(show)
63 | pageNum == 1 ? prev.classList.add("disabled") : prev.classList.remove("disabled")
64 | pageNum == maxPage ? next.classList.add("disabled") : next.classList.remove("disabled")
65 | };
66 |
--------------------------------------------------------------------------------
/theme/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 0.25.0 (2024-02-02)
2 |
3 |
4 |
5 | # 0.22.0 (2024-02-02)
6 |
7 |
8 | ### Bug Fixes
9 |
10 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
11 | * handleBackground helper colors all themes ([#6](https://github.com/slidevjs/themes/issues/6)) ([60a38f2](https://github.com/slidevjs/themes/commit/60a38f253b05df7c8408de84289867914411345a))
12 | * handleBackground helper to work with colors ([#5](https://github.com/slidevjs/themes/issues/5)) ([7a1e9b7](https://github.com/slidevjs/themes/commit/7a1e9b72347591eee1c535e6467efdb69f74726a))
13 | * imports ([4882aec](https://github.com/slidevjs/themes/commit/4882aec23109afcf0e715e2e3c28505912e7abcf))
14 | * remove `defineProps` imports ([9c02acf](https://github.com/slidevjs/themes/commit/9c02acf6353edfc81d88a5255306f126d06f148d))
15 | * subheader style ([25491db](https://github.com/slidevjs/themes/commit/25491db815ac89f1cc1a952751e918e808164a48))
16 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
17 |
18 |
19 | ### Features
20 |
21 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
22 | * new themes ([#3](https://github.com/slidevjs/themes/issues/3)) ([cbd0a1a](https://github.com/slidevjs/themes/commit/cbd0a1ac29ab4e7c5d5be24c55ee90a3945245d3))
23 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
24 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
25 | * update theme ([5ce60c6](https://github.com/slidevjs/themes/commit/5ce60c6a139a497acde5f4cbceb456854599f4f9))
26 | * upgrade to slidev 0.14 ([5b97e29](https://github.com/slidevjs/themes/commit/5b97e29c45c51ef724252df2b711d1b30c7208cd))
27 | * use webfonts auto importing ([f3091e2](https://github.com/slidevjs/themes/commit/f3091e2f4fac1137bafaa841050690b956f607a4))
28 |
29 |
30 |
31 | # 0.24.0 (2024-02-02)
32 |
33 |
34 |
35 | # 0.22.0 (2024-02-02)
36 |
37 |
38 | ### Bug Fixes
39 |
40 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
41 | * handleBackground helper colors all themes ([#6](https://github.com/slidevjs/themes/issues/6)) ([60a38f2](https://github.com/slidevjs/themes/commit/60a38f253b05df7c8408de84289867914411345a))
42 | * handleBackground helper to work with colors ([#5](https://github.com/slidevjs/themes/issues/5)) ([7a1e9b7](https://github.com/slidevjs/themes/commit/7a1e9b72347591eee1c535e6467efdb69f74726a))
43 | * imports ([4882aec](https://github.com/slidevjs/themes/commit/4882aec23109afcf0e715e2e3c28505912e7abcf))
44 | * remove `defineProps` imports ([9c02acf](https://github.com/slidevjs/themes/commit/9c02acf6353edfc81d88a5255306f126d06f148d))
45 | * subheader style ([25491db](https://github.com/slidevjs/themes/commit/25491db815ac89f1cc1a952751e918e808164a48))
46 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
47 |
48 |
49 | ### Features
50 |
51 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
52 | * new themes ([#3](https://github.com/slidevjs/themes/issues/3)) ([cbd0a1a](https://github.com/slidevjs/themes/commit/cbd0a1ac29ab4e7c5d5be24c55ee90a3945245d3))
53 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
54 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
55 | * update theme ([5ce60c6](https://github.com/slidevjs/themes/commit/5ce60c6a139a497acde5f4cbceb456854599f4f9))
56 | * upgrade to slidev 0.14 ([5b97e29](https://github.com/slidevjs/themes/commit/5b97e29c45c51ef724252df2b711d1b30c7208cd))
57 | * use webfonts auto importing ([f3091e2](https://github.com/slidevjs/themes/commit/f3091e2f4fac1137bafaa841050690b956f607a4))
58 |
59 |
60 |
61 | ## 0.22.1 (2024-02-02)
62 |
63 |
64 |
65 | # 0.22.0 (2024-02-02)
66 |
67 |
68 | ### Bug Fixes
69 |
70 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
71 | * handleBackground helper colors all themes ([#6](https://github.com/slidevjs/themes/issues/6)) ([60a38f2](https://github.com/slidevjs/themes/commit/60a38f253b05df7c8408de84289867914411345a))
72 | * handleBackground helper to work with colors ([#5](https://github.com/slidevjs/themes/issues/5)) ([7a1e9b7](https://github.com/slidevjs/themes/commit/7a1e9b72347591eee1c535e6467efdb69f74726a))
73 | * imports ([4882aec](https://github.com/slidevjs/themes/commit/4882aec23109afcf0e715e2e3c28505912e7abcf))
74 | * remove `defineProps` imports ([9c02acf](https://github.com/slidevjs/themes/commit/9c02acf6353edfc81d88a5255306f126d06f148d))
75 | * subheader style ([25491db](https://github.com/slidevjs/themes/commit/25491db815ac89f1cc1a952751e918e808164a48))
76 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
77 |
78 |
79 | ### Features
80 |
81 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
82 | * new themes ([#3](https://github.com/slidevjs/themes/issues/3)) ([cbd0a1a](https://github.com/slidevjs/themes/commit/cbd0a1ac29ab4e7c5d5be24c55ee90a3945245d3))
83 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
84 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
85 | * update theme ([5ce60c6](https://github.com/slidevjs/themes/commit/5ce60c6a139a497acde5f4cbceb456854599f4f9))
86 | * upgrade to slidev 0.14 ([5b97e29](https://github.com/slidevjs/themes/commit/5b97e29c45c51ef724252df2b711d1b30c7208cd))
87 | * use webfonts auto importing ([f3091e2](https://github.com/slidevjs/themes/commit/f3091e2f4fac1137bafaa841050690b956f607a4))
88 |
89 |
90 |
91 | # 0.22.0 (2024-02-02)
92 |
93 |
94 |
95 | # 0.22.0 (2024-02-02)
96 |
97 |
98 | ### Bug Fixes
99 |
100 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
101 | * handleBackground helper colors all themes ([#6](https://github.com/slidevjs/themes/issues/6)) ([60a38f2](https://github.com/slidevjs/themes/commit/60a38f253b05df7c8408de84289867914411345a))
102 | * handleBackground helper to work with colors ([#5](https://github.com/slidevjs/themes/issues/5)) ([7a1e9b7](https://github.com/slidevjs/themes/commit/7a1e9b72347591eee1c535e6467efdb69f74726a))
103 | * imports ([4882aec](https://github.com/slidevjs/themes/commit/4882aec23109afcf0e715e2e3c28505912e7abcf))
104 | * remove `defineProps` imports ([9c02acf](https://github.com/slidevjs/themes/commit/9c02acf6353edfc81d88a5255306f126d06f148d))
105 | * subheader style ([25491db](https://github.com/slidevjs/themes/commit/25491db815ac89f1cc1a952751e918e808164a48))
106 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
107 |
108 |
109 | ### Features
110 |
111 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
112 | * new themes ([#3](https://github.com/slidevjs/themes/issues/3)) ([cbd0a1a](https://github.com/slidevjs/themes/commit/cbd0a1ac29ab4e7c5d5be24c55ee90a3945245d3))
113 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
114 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
115 | * update theme ([5ce60c6](https://github.com/slidevjs/themes/commit/5ce60c6a139a497acde5f4cbceb456854599f4f9))
116 | * upgrade to slidev 0.14 ([5b97e29](https://github.com/slidevjs/themes/commit/5b97e29c45c51ef724252df2b711d1b30c7208cd))
117 | * use webfonts auto importing ([f3091e2](https://github.com/slidevjs/themes/commit/f3091e2f4fac1137bafaa841050690b956f607a4))
118 |
119 |
120 |
121 | ## 0.21.2 (2022-03-05)
122 |
123 |
124 | ### Bug Fixes
125 |
126 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
127 | * handleBackground helper colors all themes ([#6](https://github.com/slidevjs/themes/issues/6)) ([60a38f2](https://github.com/slidevjs/themes/commit/60a38f253b05df7c8408de84289867914411345a))
128 | * handleBackground helper to work with colors ([#5](https://github.com/slidevjs/themes/issues/5)) ([7a1e9b7](https://github.com/slidevjs/themes/commit/7a1e9b72347591eee1c535e6467efdb69f74726a))
129 | * imports ([4882aec](https://github.com/slidevjs/themes/commit/4882aec23109afcf0e715e2e3c28505912e7abcf))
130 | * remove `defineProps` imports ([9c02acf](https://github.com/slidevjs/themes/commit/9c02acf6353edfc81d88a5255306f126d06f148d))
131 | * subheader style ([25491db](https://github.com/slidevjs/themes/commit/25491db815ac89f1cc1a952751e918e808164a48))
132 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
133 |
134 |
135 | ### Features
136 |
137 | * new themes ([#3](https://github.com/slidevjs/themes/issues/3)) ([cbd0a1a](https://github.com/slidevjs/themes/commit/cbd0a1ac29ab4e7c5d5be24c55ee90a3945245d3))
138 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
139 | * update theme ([5ce60c6](https://github.com/slidevjs/themes/commit/5ce60c6a139a497acde5f4cbceb456854599f4f9))
140 | * upgrade to slidev 0.14 ([5b97e29](https://github.com/slidevjs/themes/commit/5b97e29c45c51ef724252df2b711d1b30c7208cd))
141 | * use webfonts auto importing ([f3091e2](https://github.com/slidevjs/themes/commit/f3091e2f4fac1137bafaa841050690b956f607a4))
142 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
143 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
144 |
145 |
146 |
147 | ## 0.21.1 (2021-11-22)
148 |
149 |
150 | ### Bug Fixes
151 |
152 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
153 | * handleBackground helper to work with colors ([#5](https://github.com/slidevjs/themes/issues/5)) ([7a1e9b7](https://github.com/slidevjs/themes/commit/7a1e9b72347591eee1c535e6467efdb69f74726a))
154 | * imports ([4882aec](https://github.com/slidevjs/themes/commit/4882aec23109afcf0e715e2e3c28505912e7abcf))
155 | * remove `defineProps` imports ([9c02acf](https://github.com/slidevjs/themes/commit/9c02acf6353edfc81d88a5255306f126d06f148d))
156 | * subheader style ([25491db](https://github.com/slidevjs/themes/commit/25491db815ac89f1cc1a952751e918e808164a48))
157 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
158 |
159 |
160 | ### Features
161 |
162 | * new themes ([#3](https://github.com/slidevjs/themes/issues/3)) ([cbd0a1a](https://github.com/slidevjs/themes/commit/cbd0a1ac29ab4e7c5d5be24c55ee90a3945245d3))
163 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
164 | * update theme ([5ce60c6](https://github.com/slidevjs/themes/commit/5ce60c6a139a497acde5f4cbceb456854599f4f9))
165 | * upgrade to slidev 0.14 ([5b97e29](https://github.com/slidevjs/themes/commit/5b97e29c45c51ef724252df2b711d1b30c7208cd))
166 | * use webfonts auto importing ([f3091e2](https://github.com/slidevjs/themes/commit/f3091e2f4fac1137bafaa841050690b956f607a4))
167 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
168 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
169 |
170 |
171 |
172 | # 0.21.0 (2021-09-13)
173 |
174 |
175 | ### Bug Fixes
176 |
177 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
178 | * imports ([4882aec](https://github.com/slidevjs/themes/commit/4882aec23109afcf0e715e2e3c28505912e7abcf))
179 | * remove `defineProps` imports ([9c02acf](https://github.com/slidevjs/themes/commit/9c02acf6353edfc81d88a5255306f126d06f148d))
180 | * subheader style ([25491db](https://github.com/slidevjs/themes/commit/25491db815ac89f1cc1a952751e918e808164a48))
181 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
182 |
183 |
184 | ### Features
185 |
186 | * new themes ([#3](https://github.com/slidevjs/themes/issues/3)) ([cbd0a1a](https://github.com/slidevjs/themes/commit/cbd0a1ac29ab4e7c5d5be24c55ee90a3945245d3))
187 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
188 | * update theme ([5ce60c6](https://github.com/slidevjs/themes/commit/5ce60c6a139a497acde5f4cbceb456854599f4f9))
189 | * upgrade to slidev 0.14 ([5b97e29](https://github.com/slidevjs/themes/commit/5b97e29c45c51ef724252df2b711d1b30c7208cd))
190 | * use webfonts auto importing ([f3091e2](https://github.com/slidevjs/themes/commit/f3091e2f4fac1137bafaa841050690b956f607a4))
191 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
192 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
193 |
194 |
195 |
196 | # 0.20.0 (2021-09-07)
197 |
198 |
199 | ### Bug Fixes
200 |
201 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
202 | * imports ([4882aec](https://github.com/slidevjs/themes/commit/4882aec23109afcf0e715e2e3c28505912e7abcf))
203 | * subheader style ([25491db](https://github.com/slidevjs/themes/commit/25491db815ac89f1cc1a952751e918e808164a48))
204 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
205 |
206 |
207 | ### Features
208 |
209 | * new themes ([#3](https://github.com/slidevjs/themes/issues/3)) ([cbd0a1a](https://github.com/slidevjs/themes/commit/cbd0a1ac29ab4e7c5d5be24c55ee90a3945245d3))
210 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
211 | * update theme ([5ce60c6](https://github.com/slidevjs/themes/commit/5ce60c6a139a497acde5f4cbceb456854599f4f9))
212 | * upgrade to slidev 0.14 ([5b97e29](https://github.com/slidevjs/themes/commit/5b97e29c45c51ef724252df2b711d1b30c7208cd))
213 | * use webfonts auto importing ([f3091e2](https://github.com/slidevjs/themes/commit/f3091e2f4fac1137bafaa841050690b956f607a4))
214 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
215 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
216 |
217 |
218 |
219 | ## 0.19.1 (2021-06-06)
220 |
221 |
222 | ### Bug Fixes
223 |
224 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
225 | * imports ([4882aec](https://github.com/slidevjs/themes/commit/4882aec23109afcf0e715e2e3c28505912e7abcf))
226 | * subheader style ([25491db](https://github.com/slidevjs/themes/commit/25491db815ac89f1cc1a952751e918e808164a48))
227 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
228 |
229 |
230 | ### Features
231 |
232 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
233 | * update theme ([5ce60c6](https://github.com/slidevjs/themes/commit/5ce60c6a139a497acde5f4cbceb456854599f4f9))
234 | * upgrade to slidev 0.14 ([5b97e29](https://github.com/slidevjs/themes/commit/5b97e29c45c51ef724252df2b711d1b30c7208cd))
235 | * use webfonts auto importing ([f3091e2](https://github.com/slidevjs/themes/commit/f3091e2f4fac1137bafaa841050690b956f607a4))
236 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
237 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
238 |
239 |
240 |
241 | # 0.19.0 (2021-06-04)
242 |
243 |
244 | ### Bug Fixes
245 |
246 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
247 | * imports ([4882aec](https://github.com/slidevjs/themes/commit/4882aec23109afcf0e715e2e3c28505912e7abcf))
248 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
249 |
250 |
251 | ### Features
252 |
253 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
254 | * update theme ([5ce60c6](https://github.com/slidevjs/themes/commit/5ce60c6a139a497acde5f4cbceb456854599f4f9))
255 | * upgrade to slidev 0.14 ([5b97e29](https://github.com/slidevjs/themes/commit/5b97e29c45c51ef724252df2b711d1b30c7208cd))
256 | * use webfonts auto importing ([f3091e2](https://github.com/slidevjs/themes/commit/f3091e2f4fac1137bafaa841050690b956f607a4))
257 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
258 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
259 |
260 |
261 |
262 | # 0.17.0 (2021-06-03)
263 |
264 |
265 | ### Bug Fixes
266 |
267 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
268 | * imports ([4882aec](https://github.com/slidevjs/themes/commit/4882aec23109afcf0e715e2e3c28505912e7abcf))
269 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
270 |
271 |
272 | ### Features
273 |
274 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
275 | * update theme ([5ce60c6](https://github.com/slidevjs/themes/commit/5ce60c6a139a497acde5f4cbceb456854599f4f9))
276 | * upgrade to slidev 0.14 ([5b97e29](https://github.com/slidevjs/themes/commit/5b97e29c45c51ef724252df2b711d1b30c7208cd))
277 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
278 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
279 |
280 |
281 |
282 | ## 0.14.2 (2021-05-27)
283 |
284 |
285 | ### Bug Fixes
286 |
287 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
288 | * imports ([4882aec](https://github.com/slidevjs/themes/commit/4882aec23109afcf0e715e2e3c28505912e7abcf))
289 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
290 |
291 |
292 | ### Features
293 |
294 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
295 | * upgrade to slidev 0.14 ([5b97e29](https://github.com/slidevjs/themes/commit/5b97e29c45c51ef724252df2b711d1b30c7208cd))
296 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
297 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
298 |
299 |
300 |
301 | ## 0.14.1 (2021-05-27)
302 |
303 |
304 | ### Bug Fixes
305 |
306 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
307 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
308 |
309 |
310 | ### Features
311 |
312 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
313 | * upgrade to slidev 0.14 ([5b97e29](https://github.com/slidevjs/themes/commit/5b97e29c45c51ef724252df2b711d1b30c7208cd))
314 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
315 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
316 |
317 |
318 |
319 | # 0.14.0 (2021-05-27)
320 |
321 |
322 | ### Bug Fixes
323 |
324 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
325 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
326 |
327 |
328 | ### Features
329 |
330 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
331 | * upgrade to slidev 0.14 ([5b97e29](https://github.com/slidevjs/themes/commit/5b97e29c45c51ef724252df2b711d1b30c7208cd))
332 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
333 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
334 |
335 |
336 |
337 | ## 0.7.9 (2021-05-27)
338 |
339 |
340 | ### Bug Fixes
341 |
342 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
343 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
344 |
345 |
346 | ### Features
347 |
348 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
349 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
350 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
351 |
352 |
353 |
354 | ## 0.7.8 (2021-05-27)
355 |
356 |
357 | ### Bug Fixes
358 |
359 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
360 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
361 |
362 |
363 | ### Features
364 |
365 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
366 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
367 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
368 |
369 |
370 |
371 | ## 0.7.7 (2021-05-26)
372 |
373 |
374 | ### Bug Fixes
375 |
376 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
377 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
378 |
379 |
380 | ### Features
381 |
382 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
383 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
384 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
385 |
386 |
387 |
388 | ## 0.7.6 (2021-05-22)
389 |
390 |
391 | ### Bug Fixes
392 |
393 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
394 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
395 |
396 |
397 | ### Features
398 |
399 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
400 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
401 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
402 |
403 |
404 |
405 | ## 0.7.5 (2021-05-22)
406 |
407 |
408 | ### Bug Fixes
409 |
410 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
411 | * windicss extract glob not valid ([#1](https://github.com/slidevjs/themes/issues/1)) ([875fcc9](https://github.com/slidevjs/themes/commit/875fcc9c8ac6edf75fc49f7640a56acb9d438d2f))
412 |
413 |
414 | ### Features
415 |
416 | * primary color customization ([df299cf](https://github.com/slidevjs/themes/commit/df299cf7c06fbc556fead0e11feeaf58142d5a20))
417 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
418 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
419 |
420 |
421 |
422 | ## 0.7.4 (2021-05-20)
423 |
424 |
425 | ### Bug Fixes
426 |
427 | * embbed layout helper ([ee4cd9a](https://github.com/slidevjs/themes/commit/ee4cd9a1456da59ddb8baafb6a4783f94200f42c))
428 |
429 |
430 | ### Features
431 |
432 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
433 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
434 |
435 |
436 |
437 | ## 0.7.3 (2021-05-15)
438 |
439 |
440 | ### Features
441 |
442 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
443 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
444 |
445 |
446 |
447 | ## 0.7.3 (2021-05-13)
448 |
449 |
450 | ### Features
451 |
452 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
453 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
454 |
455 |
456 |
457 | ## 0.7.3 (2021-05-13)
458 |
459 |
460 | ### Features
461 |
462 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
463 | * **style:** update slidev code background using prism values ([3c9da06](https://github.com/slidevjs/themes/commit/3c9da061865d15ea40efffd550b8c1ccbcd95c61))
464 |
465 |
466 |
467 | ## 0.7.2 (2021-05-11)
468 |
469 |
470 | ### Features
471 |
472 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
473 |
474 |
475 |
476 | ## 0.7.2 (2021-05-11)
477 |
478 |
479 | ### Features
480 |
481 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
482 |
483 |
484 |
485 | ## 0.7.2 (2021-05-11)
486 |
487 |
488 | ### Features
489 |
490 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
491 |
492 |
493 |
494 | ## 0.7.2 (2021-05-10)
495 |
496 |
497 | ### Features
498 |
499 | * **default:** new layouts ([63a9dc9](https://github.com/slidevjs/themes/commit/63a9dc971c3c7e4207d32832e534ddc1063328e1))
500 |
501 |
502 |
503 | ## 0.6.7 (2021-05-10)
504 |
505 |
506 |
507 | ## 0.6.7 (2021-05-10)
508 |
509 |
510 |
511 |
--------------------------------------------------------------------------------
/theme/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Slidev.js Team
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/theme/README.md:
--------------------------------------------------------------------------------
1 | # @slidev/theme-default
2 |
3 | [](https://www.npmjs.com/package/@slidev/theme-default)
4 |
5 | The default theme for [Slidev](https://github.com/slidevjs/slidev).
6 |
7 | ## Install
8 |
9 | Add the following frontmatter to your `slides.md`. Start Slidev then it will prompt you to install the theme automatically.
10 |
11 | ---
12 | theme: default
13 | ---
14 |
15 | Learn more about [how to use a theme](https://sli.dev/themes/use).
16 |
17 | ## License
18 |
19 | MIT License © 2021 [Anthony Fu](https://github.com/antfu)
20 |
--------------------------------------------------------------------------------
/theme/example.md:
--------------------------------------------------------------------------------
1 | ---
2 | theme: ./
3 | layout: cover
4 | background: https://images.unsplash.com/photo-1502189562704-87e622a34c85?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=2100&q=80
5 | ---
6 |
7 | # Presentation title
8 |
9 | Presentation subtitle
10 |
11 | ---
12 |
13 | # Slide Title
14 |
15 | Slide Subtitle
16 |
17 | * Slide bullet text
18 | * Slide bullet text
19 | * Slide bullet text
20 | * Slide bullet text
21 | * Slide bullet text
22 |
23 | ---
24 | layout: image-right
25 | image: https://source.unsplash.com/collection/94734566/1920x1080
26 | ---
27 |
28 | # Slide Title
29 |
30 | Colons can be used to align columns.
31 |
32 | | Tables | Are | Cool |
33 | | ------------- |:-------------:| -----:|
34 | | col 3 is | right-aligned | $1600 |
35 | | col 2 is | centered | $12 |
36 | | zebra stripes | are neat | $1 |
37 |
38 | ---
39 | layout: section
40 | ---
41 |
42 | # Section Title
43 |
44 | ---
45 | layout: statement
46 | ---
47 |
48 | # Statement
49 |
50 | ---
51 | layout: fact
52 | ---
53 |
54 | # 100%
55 | Fact information
56 |
57 | ---
58 | layout: quote
59 | ---
60 |
61 | # "Notable quote"
62 | Attribution
63 |
64 | ---
65 | layout: image-left
66 | image: https://source.unsplash.com/collection/94734566/1920x1080
67 | ---
68 |
69 | # Code
70 |
71 | ```ts {all|2|1-6|all}
72 | interface User {
73 | id: number
74 | firstName: string
75 | lastName: string
76 | role: string
77 | }
78 |
79 | function updateUser(id: number, update: Partial) {
80 | const user = getUser(id)
81 | const newUser = { ...user, ...update }
82 | saveUser(id, newUser)
83 | }
84 | ```
85 |
86 | ---
87 | layout: center
88 | class: "text-center"
89 | ---
90 |
91 | # Learn More
92 |
93 | [Documentations](https://sli.dev) / [GitHub Repo](https://github.com/slidevjs/slidev)
94 |
95 | ---
96 |
97 | # H1
98 | ## H2
99 | ### H3
100 | #### H4
101 | ##### H5
102 | ###### H6
103 |
--------------------------------------------------------------------------------
/theme/layoutHelper.ts:
--------------------------------------------------------------------------------
1 | import type { CSSProperties } from 'vue'
2 |
3 | /**
4 | * Resolve urls from frontmatter and append with the base url
5 | */
6 | export function resolveAssetUrl(url: string) {
7 | if (url.startsWith('/'))
8 | return import.meta.env.BASE_URL + url.slice(1)
9 | return url
10 | }
11 |
12 | export function handleBackground(background?: string, dim = false): CSSProperties {
13 | const isColor = background && ['#', 'rgb', 'hsl'].some(v => background.indexOf(v) === 0)
14 |
15 | const style = {
16 | background: isColor
17 | ? background
18 | : undefined,
19 | color: (background && !isColor)
20 | ? 'white'
21 | : undefined,
22 | backgroundImage: isColor
23 | ? undefined
24 | : background
25 | ? dim
26 | ? `linear-gradient(#0005, #0008), url(${resolveAssetUrl(background)})`
27 | : `url("${resolveAssetUrl(background)}")`
28 | : undefined,
29 | backgroundRepeat: 'no-repeat',
30 | backgroundPosition: 'center',
31 | backgroundSize: 'cover',
32 | }
33 |
34 | if (!style.background)
35 | delete style.background
36 |
37 | return style
38 | }
39 |
--------------------------------------------------------------------------------
/theme/layouts/cover.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
20 |
21 |
--------------------------------------------------------------------------------
/theme/layouts/fact.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/theme/layouts/intro.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/theme/layouts/quote.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/theme/layouts/section.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/theme/layouts/statement.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/theme/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@slidev/theme-default",
3 | "version": "0.25.0",
4 | "description": "Default theme for Slidev",
5 | "author": "antfu ",
6 | "license": "MIT",
7 | "funding": "https://github.com/sponsors/antfu",
8 | "homepage": "https://sli.dev",
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/slidevjs/themes"
12 | },
13 | "bugs": "https://github.com/slidevjs/themes/issues",
14 | "keywords": [
15 | "slidev-theme",
16 | "slidev"
17 | ],
18 | "engines": {
19 | "node": ">=14.0.0",
20 | "slidev": ">=v0.47.0"
21 | },
22 | "slidev": {
23 | "defaults": {
24 | "fonts": {
25 | "mono": "Fira Code",
26 | "sans": "Avenir Next,Nunito Sans",
27 | "local": "Avenir Next"
28 | }
29 | }
30 | },
31 | "dependencies": {
32 | "@slidev/types": "^0.47.0",
33 | "codemirror-theme-vars": "^0.1.2",
34 | "prism-theme-vars": "^0.2.4"
35 | }
36 | }
--------------------------------------------------------------------------------
/theme/styles/index.ts:
--------------------------------------------------------------------------------
1 | import '@slidev/client/styles/layouts-base.css'
2 | import './layouts.css'
3 | import './prism.css'
4 |
--------------------------------------------------------------------------------
/theme/styles/layouts.css:
--------------------------------------------------------------------------------
1 | .slidev-layout {
2 | h1 + p {
3 | @apply -mt-2 opacity-50 mb-4;
4 | }
5 |
6 | p + h2, ul + h2, table + h2 {
7 | @apply mt-10;
8 | }
9 |
10 | h1 {
11 | @apply text-4xl mb-4 -ml-[0.05em];
12 | }
13 |
14 | h2 {
15 | @apply text-3xl;
16 | }
17 |
18 | h3 {
19 | @apply text-2xl;
20 | }
21 |
22 | h4 {
23 | @apply text-xl;
24 | }
25 |
26 | h5 {
27 | @apply text-base;
28 | }
29 |
30 | h6 {
31 | @apply text-sm pt-1 uppercase tracking-widest font-500 -ml-[0.05em];
32 | }
33 |
34 | h6:not(.opacity-100) {
35 | @apply opacity-40;
36 | }
37 |
38 | }
39 |
40 | .slidev-layout.cover,
41 | .slidev-layout.intro {
42 | @apply h-full grid;
43 |
44 | h1 {
45 | @apply text-6xl leading-20;
46 | }
47 | }
48 |
49 |
50 | .slidev-layout.fact {
51 | @apply text-center grid h-full;
52 | h1 {
53 | @apply text-8xl font-700;
54 | }
55 | h1 + p {
56 | @apply font-700 text-2xl;
57 | }
58 | }
59 | .slidev-layout.statement {
60 | @apply text-center grid h-full;
61 |
62 | h1 {
63 | @apply text-6xl font-700;
64 | }
65 | }
66 | .slidev-layout.quote {
67 | @apply grid h-full;
68 |
69 | h1 + p {
70 | @apply mt-2;
71 | }
72 | }
73 | .slidev-layout.section {
74 | h1 {
75 | @apply text-6xl font-500 leading-20;
76 | }
77 | }
78 |
79 | html:not(.dark) {
80 | .slidev-layout {
81 | b, strong {
82 | @apply text-blue-700 underline underline-offset-5;
83 | }
84 | }
85 | }
86 |
87 | html.dark {
88 | .slidev-layout {
89 | b, strong {
90 | @apply text-yellow-100 underline underline-offset-5;
91 | }
92 | }
93 | }
94 |
95 |
96 |
--------------------------------------------------------------------------------
/theme/styles/prism.css:
--------------------------------------------------------------------------------
1 | @import 'prism-theme-vars/base.css';
2 | @import 'codemirror-theme-vars/base.css';
3 | @import 'prism-theme-vars/to-codemirror.css';
4 |
5 | :root {
6 | --prism-font-family: var(--slidev-code-font-family);
7 | --prism-background: var(--slidev-code-background);
8 | }
9 |
10 | html:not(.dark) {
11 | --prism-foreground: #393a34;
12 | --prism-comment: #a0ada0;
13 | --prism-string: #b56959;
14 | --prism-literal: #2f8a89;
15 | --prism-number: #296aa3;
16 | --prism-keyword: #1c6b48;
17 | --prism-function: #6c7834;
18 | --prism-boolean: #1c6b48;
19 | --prism-constant: #a65e2b;
20 | --prism-deleted: #a14f55;
21 | --prism-class: #2993a3;
22 | --prism-builtin: #ab5959;
23 | --prism-property: #b58451;
24 | --prism-namespace: #b05a78;
25 | --prism-punctuation: #8e8f8b;
26 | --prism-decorator: #bd8f8f;
27 | --prism-regex: #ab5e3f;
28 | --prism-json-property: #698c96;
29 | }
30 |
31 | html.dark {
32 | --prism-foreground: #d4cfbf;
33 | --prism-comment: #758575;
34 | --prism-string: #d48372;
35 | --prism-literal: #429988;
36 | --prism-keyword: #4d9375;
37 | --prism-boolean: #1c6b48;
38 | --prism-number: #6394bf;
39 | --prism-variable: #c2b36e;
40 | --prism-function: #a1b567;
41 | --prism-deleted: #a14f55;
42 | --prism-class: #54b1bf;
43 | --prism-builtin: #e0a569;
44 | --prism-property: #dd8e6e;
45 | --prism-namespace: #db889a;
46 | --prism-punctuation: #858585;
47 | --prism-decorator: #bd8f8f;
48 | --prism-regex: #ab5e3f;
49 | --prism-json-property: #6b8b9e;
50 | --prism-line-number: #888888;
51 | --prism-line-number-gutter: #eeeeee;
52 | --prism-line-highlight-background: #444444;
53 | --prism-selection-background: #444444;
54 | }
55 |
--------------------------------------------------------------------------------
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "rewrites": [
3 | { "source": "/(.*)", "destination": "/index.html" }
4 | ],
5 | "buildCommand": "npm run build",
6 | "outputDirectory": "dist"
7 | }
8 |
--------------------------------------------------------------------------------