├── .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 | ![Vue 3 Document](/Vue3-doc.png) {.!w-7/8} 71 | 72 | --- 73 | 74 | # 模板语法 75 | 76 | Vue 使用基于 HTML 的模板语法,允许您**声明性**地将渲染的 DOM 绑定到底层组件实例的数据。所有 Vue 模板都是语法有效的 HTML,可以被符合规范的浏览器和 HTML 解析器解析。 77 | 78 | 79 | 80 | 1. 数据绑定 81 | 82 | ```vue 83 | 86 | ``` 87 | 88 | 2. 属性绑定 89 | 90 | ```vue 91 | 94 | 95 | 98 | ``` 99 | 100 | 101 | 102 | --- 103 | 104 | 105 | 106 | 3. 多属性绑定 107 | 108 | ```vue 109 | 115 | 116 | 119 | ``` 120 | 121 | 4. 完整的指令语法 122 | 123 | ![指令语法](/指令语法.png) {.!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 | 140 | ``` 141 | 142 | 6. 条件渲染(控制`display`) 143 | 144 | ```vue 145 | 148 | ``` 149 | 150 | 详见:https://vuejs.org/guide/essentials/conditional.html 151 | 152 | 7. 列表渲染 153 | 154 | ```vue 155 | 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 | 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 | 208 | ``` 209 | 210 | ```vue 211 | 214 | ``` 215 | 216 | 10. `style`的绑定 217 | 218 | ```vue 219 | 223 | 224 | 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 | 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 | 402 | 403 | ``` 404 | 405 | --- 406 | 407 | # 组件API 408 | 409 | ![组件树](/组件树.png) {.!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 | -------------------------------------------------------------------------------- /slides/components/Ques.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | -------------------------------------------------------------------------------- /slides/components/Tag.vue: -------------------------------------------------------------------------------- 1 | 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 |
31 |
32 | 33 | 34 | 35 |
gaojunran
36 |
37 |
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 | ![js-doc](/js-doc.png) {.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 |
113 |

114 |

115 |

116 |
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 `` 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 `${match[1]}` 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, "\"$1\"") 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 |
15 | 16 |
17 |
18 |
19 | 筛选语言 20 | 21 | 25 |
26 |
展示第到第位的项目
28 |
29 |
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 |
22 |
23 | 24 | 27 |
28 | 29 | 32 |
33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
42 |
43 |
44 |
45 |
46 | 47 |

垃圾桶

48 |
49 | 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 | 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 |
46 | 47 |
添加联系人
48 | 49 |
50 | 51 | 52 | 53 | 54 |
55 |
56 | 57 |
    58 | 59 |
  • 60 |
    {{item.letter}}
    61 |
      62 |
    • 63 | {{ele.name}} 65 |
    • 66 | 67 |
    68 |
  • 69 |
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 | 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 |
13 | 14 | 15 |
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 | 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 |
    24 | 25 |
    26 |
    ${item.name}
    27 | ${(item.price / 100).toFixed(2)}元 28 |
    29 |

    30 | ${item.description} 31 |

    32 |
    33 |
    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 | [![NPM version](https://img.shields.io/npm/v/@slidev/theme-default?color=3AB9D4&label=)](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 | 21 | -------------------------------------------------------------------------------- /theme/layouts/fact.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /theme/layouts/intro.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /theme/layouts/quote.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /theme/layouts/section.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /theme/layouts/statement.vue: -------------------------------------------------------------------------------- 1 | 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 | --------------------------------------------------------------------------------