├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .github
└── workflows
│ ├── ci.yml
│ └── docsearch.yml.bak
├── .gitignore
├── .prettierrc
├── .stylelintrc.js
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── babel.config.js
├── blog
├── advice
│ ├── Api接口风格.md
│ └── 配置windows.md
├── authors.yml
├── develop
│ ├── vite学习.md
│ ├── webpack集合.md
│ ├── 为什么使用pnpm.md
│ ├── 去除typescript代码类型.md
│ └── 基础封装axios.md
├── lifestyle
│ ├── 世间再无击节客,左耳从此不听风.md
│ ├── 深谈个人对新技术的看法.md
│ └── 第一次离职.md
├── program
│ ├── HTTP请求配置客户端SSL证书.md
│ ├── Socket文本编辑实时共享.md
│ ├── Websocket心跳机制.md
│ ├── socket1对1聊天.md
│ ├── vite+vue3搭建uniapp开发环境.md
│ ├── 如何给博客增加评论效果.md
│ └── 搭建Electron+Vue3开发环境.md
├── project
│ ├── Vercel部署个人博客.md
│ ├── xx记账.md
│ ├── 使用Vite开发Chrome插件.md
│ ├── 第一个博客搭建之Vuepress.md
│ └── 第二个博客搭建之Docusaurus.md
└── reference
│ └── 记一次前端面试过程.md
├── data
├── friend.ts
├── project.ts
└── resource.ts
├── docs
├── skill
│ ├── algorithm
│ │ └── 数组中的第K个最大元素.md
│ ├── code-specification
│ │ ├── editorconfig.md
│ │ ├── eslint.md
│ │ ├── guides.md
│ │ ├── husky.md
│ │ ├── npmrc.md
│ │ ├── prettier.md
│ │ └── stylelint.md
│ ├── css
│ │ ├── Flex布局.md
│ │ ├── 一些CSS属性.md
│ │ ├── 如何实现toast垂直居中.md
│ │ ├── 实现毛玻璃效果.md
│ │ ├── 纯css实现固定宽高比.md
│ │ └── 记Tailwind CSS使用.md
│ ├── git
│ │ ├── git commit规范.md
│ │ ├── github actions示例.md
│ │ ├── github apps示例.md
│ │ ├── github 其他文件.md
│ │ └── git修改默认分支main.md
│ ├── introduction.md
│ ├── js
│ │ ├── ES2015常用语法特性.md
│ │ ├── JS变量提升.md
│ │ ├── JS如何获取当天零点时间戳.md
│ │ ├── JS实现函数缓存.md
│ │ ├── JS数组对象去重.md
│ │ ├── ajax学习.md
│ │ ├── js一行代码.md
│ │ ├── 常用util.js.md
│ │ └── 重构之对象映射类型.md
│ ├── node
│ │ ├── axios请求gbk页面乱码解决.md
│ │ ├── npm镜像配置.md
│ │ └── 使用 require.context 实现模块自动导入.md
│ ├── vue
│ │ ├── $router和$route的区别.md
│ │ ├── Pinia.md
│ │ ├── VueRouter导航守卫.md
│ │ ├── customRef实现敏感词替换.md
│ │ ├── h+render实现函数式组件调用.md
│ │ ├── v-if与v-show的区别.md
│ │ ├── 使用ShallowRef对接Redux到Vue.md
│ │ ├── 图片懒加载指令.md
│ │ ├── 对keep-alive组件的理解.md
│ │ └── 异步组件实现按需加载.md
│ └── web
│ │ ├── TCP三次握手.md
│ │ └── 跨域问题.md
└── tools
│ ├── Everything快速搜索本地文件.md
│ ├── Vite相关插件.md
│ ├── Wappalyzer识别网站上的技术.md
│ ├── introduction.md
│ └── 实用工具PowerToys.md
├── docsearch.json
├── docusaurus.config.js
├── i18n
└── zh
│ ├── code.json
│ ├── docusaurus-plugin-content-blog
│ └── options.json
│ ├── docusaurus-plugin-content-docs
│ └── current.json
│ └── docusaurus-theme-classic
│ ├── footer.json
│ └── navbar.json
├── package-lock.json
├── package.json
├── react-app-env.d.ts
├── renovate.json
├── sidebars.js
├── src
├── components
│ ├── BlogInfo
│ │ └── index.tsx
│ ├── BrowserWindow
│ │ ├── index.tsx
│ │ └── styles.module.css
│ ├── Button
│ │ └── index.tsx
│ ├── CodeSandBox
│ │ └── index.tsx
│ ├── Codepen
│ │ └── index.tsx
│ ├── Comment
│ │ └── index.tsx
│ ├── Hero
│ │ ├── img
│ │ │ └── hero_main.svg
│ │ ├── index.tsx
│ │ └── styles.module.scss
│ ├── Playground
│ │ └── index.tsx
│ ├── Svg
│ │ ├── index.tsx
│ │ └── styles.module.css
│ └── svgIcons
│ │ └── FavoriteIcon
│ │ └── index.tsx
├── css
│ └── custom.scss
├── pages
│ ├── about.mdx
│ ├── index.tsx.bak
│ ├── project
│ │ ├── _components
│ │ │ ├── ShowcaseCard
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── ShowcaseFilterToggle
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── ShowcaseTagSelect
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ └── ShowcaseTooltip
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ ├── index.tsx
│ │ └── styles.module.css
│ └── resource
│ │ ├── _components
│ │ └── ResourceCard
│ │ │ ├── index.tsx
│ │ │ └── styles.module.css
│ │ ├── index.tsx
│ │ └── resource.module.css
├── plugin
│ ├── plugin-baidu-push
│ │ └── index.js
│ ├── plugin-baidu-tongji
│ │ └── index.js
│ └── plugin-content-blog
│ │ ├── lib
│ │ ├── index.js
│ │ └── types.js
│ │ ├── package.json
│ │ ├── src
│ │ ├── index.ts
│ │ └── types.ts
│ │ ├── tsconfig.json
│ │ └── tsconfig.tsbuildinfo
├── sw.js
├── theme
│ ├── BlogArchivePage
│ │ ├── index.tsx
│ │ └── styles.module.css
│ ├── BlogListPage
│ │ ├── index.tsx
│ │ └── useViewType.ts
│ ├── BlogPostItem
│ │ ├── Container
│ │ │ ├── index.tsx
│ │ │ └── styles.module.css
│ │ ├── Content
│ │ │ └── index.tsx
│ │ ├── Footer
│ │ │ ├── ReadMoreLink
│ │ │ │ └── index.tsx
│ │ │ ├── index.tsx
│ │ │ └── styles.module.css
│ │ ├── Header
│ │ │ ├── Author
│ │ │ │ └── index.tsx
│ │ │ ├── Authors
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── Info
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ ├── Title
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.module.css
│ │ │ └── index.tsx
│ │ └── index.tsx
│ ├── BlogPostItems
│ │ └── index.tsx
│ ├── BlogPostPage
│ │ ├── Metadata
│ │ │ └── index.tsx
│ │ └── index.tsx
│ ├── BlogSidebar
│ │ ├── Desktop
│ │ │ ├── index.tsx
│ │ │ └── styles.module.css
│ │ ├── Mobile
│ │ │ └── index.tsx
│ │ └── index.tsx
│ ├── BlogTagsListPage
│ │ └── index.tsx
│ ├── DocTagsListPage
│ │ └── index.tsx
│ ├── MDXPage
│ │ ├── index.tsx
│ │ └── styles.module.css
│ ├── Navbar
│ │ └── MobileSidebar
│ │ │ └── Layout
│ │ │ └── index.tsx
│ └── TagsListByLetter
│ │ ├── index.tsx
│ │ └── styles.module.css
├── types.d.ts
└── utils
│ └── jsUtils.ts
├── static
├── .htaccess
├── img
│ ├── buildwith.png
│ ├── favicon.ico
│ ├── icons
│ │ ├── favicon.ico
│ │ ├── icon-128.png
│ │ ├── icon-16.png
│ │ ├── icon-256.png
│ │ ├── icon-32.png
│ │ ├── icon-48.png
│ │ └── icon-64.png
│ ├── logo.png
│ ├── logo.webp
│ └── resource
│ │ ├── antv.png
│ │ ├── any-rule.ico
│ │ ├── apifox.png
│ │ ├── atoolbox.ico
│ │ ├── axios.ico
│ │ ├── bilibili.ico
│ │ ├── bootcdn.png
│ │ ├── bun.svg
│ │ ├── coding.png
│ │ ├── component party.svg
│ │ ├── coolify.png
│ │ ├── coolors.png
│ │ ├── css-inspiration.png
│ │ ├── cssfx.png
│ │ ├── cypress.png
│ │ ├── dbyun.png
│ │ ├── digitalocean.png
│ │ ├── docusaurus.svg
│ │ ├── electron.ico
│ │ ├── es6.png
│ │ ├── figma.png
│ │ ├── fresh.ico
│ │ ├── gitee.ico
│ │ ├── github.ico
│ │ ├── github.png
│ │ ├── google_fonts.ico
│ │ ├── graphQL.svg
│ │ ├── hoppscotch.png
│ │ ├── igoutu.png
│ │ ├── javascript.svg
│ │ ├── jest.png
│ │ ├── jsdelivr.webp
│ │ ├── juejin.png
│ │ ├── loading.ico
│ │ ├── mdn.png
│ │ ├── naiveUI.svg
│ │ ├── namae.png
│ │ ├── netlify.png
│ │ ├── nuxt.svg
│ │ ├── ossinsight.png
│ │ ├── playwright.svg
│ │ ├── prisma.png
│ │ ├── quick reference.svg
│ │ ├── railway.png
│ │ ├── remix.png
│ │ ├── roadmap.png
│ │ ├── runoob.png
│ │ ├── rust-logo-blk.svg
│ │ ├── rust.svg
│ │ ├── shields.png
│ │ ├── stackblitz.png
│ │ ├── stateofjs.svg
│ │ ├── strapi.png
│ │ ├── supabase.png
│ │ ├── swc.png
│ │ ├── swr.png
│ │ ├── taro.png
│ │ ├── terminalgif.ico
│ │ ├── turbopack.svg
│ │ ├── turborepo.svg
│ │ ├── twind.svg
│ │ ├── typeorm.ico
│ │ ├── typescript.png
│ │ ├── typing-svg.png
│ │ ├── uiverse.png
│ │ ├── vben-admin.png
│ │ ├── vite.svg
│ │ ├── webgradients.png
│ │ ├── webpack.png
│ │ └── zhubai.png
├── manifest.json
└── svg
│ └── juejin.svg
├── tsconfig.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | end_of_line = lf
8 | insert_final_newline = true
9 | indent_style = space
10 | indent_size = 2
11 | max_line_length = 80
12 | trim_trailing_whitespace = true
13 |
14 | [*.md]
15 | insert_final_newline = false
16 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | __fixtures__
2 | __mocks__
3 | dist
4 | node_modules
5 | .yarn
6 | .history
7 | build
8 | coverage
9 | jest.config.js
10 | jest.transform.js
11 | docusaurus.config.js
12 | sidebars.js
13 | *.md
14 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parserOptions: {
3 | ecmaVersion: 7,
4 | sourceType: 'module',
5 | },
6 | plugins: ['@docusaurus', '@typescript-eslint'],
7 | extends: [
8 | 'plugin:@docusaurus/recommended',
9 | 'plugin:@typescript-eslint/recommended',
10 | ],
11 | }
12 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: ci
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | build-and-deploy:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v3
15 |
16 | - name: Use Node.js 16
17 | uses: actions/setup-node@v3
18 | with:
19 | node-version: '16.x'
20 |
21 | - name: Build Project
22 | run: |
23 | yarn install
24 | yarn run build
25 |
26 | - name: SSH Deploy
27 | uses: easingthemes/ssh-deploy@v2.2.11
28 | env:
29 | SSH_PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
30 | ARGS: '-avzr --delete'
31 | SOURCE: 'build'
32 | REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
33 | REMOTE_USER: 'root'
34 | TARGET: '/home/blog/'
35 |
--------------------------------------------------------------------------------
/.github/workflows/docsearch.yml.bak:
--------------------------------------------------------------------------------
1 | name: docsearch
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | algolia:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 |
14 | - name: Get the content of docsearch.json as config
15 | id: algolia_config
16 | run: echo "::set-output name=config::$(cat docsearch.json | jq -r tostring)"
17 |
18 | - name: Run algolia/docsearch-scraper image
19 | env:
20 | ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
21 | ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }}
22 | CONFIG: ${{ steps.algolia_config.outputs.config }}
23 | run: |
24 | docker run \
25 | --env APPLICATION_ID=${ALGOLIA_APP_ID} \
26 | --env API_KEY=${ALGOLIA_API_KEY} \
27 | --env "CONFIG=${CONFIG}" \
28 | algolia/docsearch-scraper
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | /node_modules
3 |
4 | # Production
5 | /build
6 | /drafts
7 | /tmp
8 | cloudbaserc.json
9 |
10 | # Generated files
11 | .docusaurus
12 | .cache-loader
13 |
14 | # Misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 | .env
21 |
22 | # Packages
23 | /packages
24 |
25 | npm-debug.log*
26 | yarn-debug.log*
27 | yarn-error.log*
28 | pnpm-error.log*
29 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "tabWidth": 2,
4 | "useTabs": false,
5 | "semi": false,
6 | "singleQuote": true,
7 | "quoteProps": "as-needed",
8 | "jsxSingleQuote": false,
9 | "trailingComma": "all",
10 | "bracketSpacing": true,
11 | "jsxBracketSameLine": false,
12 | "arrowParens": "avoid",
13 | "proseWrap": "never"
14 | }
15 |
--------------------------------------------------------------------------------
/.stylelintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['stylelint-config-standard-scss', 'stylelint-config-prettier-scss'],
3 | rules: {
4 | 'selector-pseudo-class-no-unknown': [
5 | true,
6 | {
7 | // :global is a CSS modules feature to escape from class name hashing
8 | ignorePseudoClasses: ['global'],
9 | },
10 | ],
11 | 'selector-class-pattern': null,
12 | 'custom-property-empty-line-before': null,
13 | 'selector-id-pattern': null,
14 | 'declaration-empty-line-before': null,
15 | 'no-descending-specificity': null,
16 | 'comment-empty-line-before': null,
17 | 'value-keyword-case': ['lower', { camelCaseSvgKeywords: true }],
18 | },
19 | }
20 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "prettier.enable": true,
3 | "editor.codeActionsOnSave": {
4 | "source.fixAll": "explicit",
5 | "source.organizeImports": "never"
6 | },
7 | "[typescript]": {
8 | "editor.defaultFormatter": "esbenp.prettier-vscode"
9 | },
10 | "[typescriptreact]": {
11 | "editor.defaultFormatter": "esbenp.prettier-vscode"
12 | },
13 | "stylelint.validate": ["css", "less", "postcss", "scss", "sass"]
14 | }
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 xxsoftware
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | 东云研究所的个人博客
3 |
4 |
5 |
6 | Build with 🦖Docusaurus
7 |
8 |
9 |
10 |
11 | 🖥 Online Preview
12 |
13 |
14 |
15 |
16 |
17 |
18 | ## 👋 Introduction
19 |
20 | 在这里我会分享各类技术栈所遇到问题与解决方案,带你了解最新的技术栈以及实际开发中如何应用,并希望我的开发经历对你有所启发。
21 |
22 | 如果你想要搭建一个类似的站点,可直接 [Fork](https://github.com/xxsoftware/blog/fork) 本仓库使用
23 |
24 | ## ✨ Features
25 |
26 | - ✍️ **Markdown** - 写作方便
27 | - 🎨 **Beautiful** - 整洁,美观
28 | - 🖥️ **PWA** - 支持 PWA,可安装,离线可用
29 | - 🏞️ **i18n** - 支持国际化
30 | - 💯 **SEO** - 搜索引擎优化,易于收录
31 | - 📊 **谷歌分析** - 支持 Google Analytics
32 | - 🔎 **全文搜索** - 支持 [Algolia DocSearch](https://github.com/algolia/docsearch)
33 | - 🗃️ **博文视图** - 不同的博文视图,列表、宫格、卡片
34 | - 🌈 **资源导航** - 收集并分享有用、有意思的资源
35 | - 📦 **项目展示** - 展示你的项目,可用作于作品集
36 |
37 | ## 📊 Catalogue
38 |
39 | ```bash
40 | ├── blog # 博客
41 | │ ├── first-blog.md
42 | ├── docs # 文档/笔记
43 | │ └── doc.md
44 | ├── data # 项目/导航
45 | │ ├── project.ts # 项目
46 | │ └── resource.ts # 资源导航
47 | ├── i18n # 国际化
48 | ├── src
49 | │ ├── components # 组件
50 | │ ├── css # 自定义CSS
51 | │ ├── pages # 自定义页面
52 | │ ├── plugin # 自定义插件
53 | │ └── theme # 自定义主题组件
54 | ├── static # 静态资源文件
55 | │ └── img # 静态图片
56 | ├── docusaurus.config.js # 站点的配置信息
57 | ├── sidebars.js # 文档的侧边栏
58 | ├── package.json
59 | ├── tsconfig.json
60 | └── yarn.lock
61 | ```
62 |
63 | ## 📥 Start
64 |
65 | ```sh
66 | git clone https://github.com/xxsoftware/blog.git
67 | cd blog
68 | yarn
69 | yarn start
70 | ```
71 |
72 | Build
73 |
74 | ```sh
75 | yarn run build
76 | ```
77 |
78 | ## 📝License
79 |
80 | [MIT](./LICENSE)
81 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | require.resolve('@docusaurus/core/lib/babel/preset'),
4 | [
5 | "@babel/preset-react",
6 | { "runtime": "automatic", "importSource": "@emotion/react" }
7 | ]
8 | ],
9 | plugins: ["@emotion/babel-plugin"],
10 | }
11 |
--------------------------------------------------------------------------------
/blog/advice/Api接口风格.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: api-style
3 | title: Api接口风格
4 | date: 2021-08-06
5 | authors: 東雲研究所
6 | tags: [随笔, api]
7 | keywords: [随笔, api]
8 | ---
9 |
10 |
11 |
12 | 前后端数据交互,经常要和 Api 打交道,于是关于 Api 接口的设计,有必要好好写一写
13 |
14 | ## Restful api 风格
15 |
16 | 首先还是得说一下**REST 是设计风格而不是标准**,也就是在写 api 接口的时候,喜欢就遵循。
17 |
18 | 这里举一个常见的 api 接口设计
19 |
20 | 常见的 CRUD 操作
21 |
22 | ```js
23 | POST /user/list // 获取列表
24 | POST /user/get // 获取用户
25 | POST /user/add // 添加用户
26 | POST /user/edit // 编辑用户
27 | POST /user/delete // 删除用户
28 | ```
29 |
30 | 与之对应 Restful Api 风格
31 |
32 | ```js
33 | GET / user // 获取列表
34 | GET / user / { id } // 获取用户
35 | POST / user // 添加用户
36 | PUT / user / { id } // 编辑用户
37 | DELETE / user / { id } // 删除用户
38 |
39 | // {id} 通过后端路由 参数Params可以获取到
40 | ```
41 |
42 | 可以看到 Restful 风格相比于正常的 POST 而言,少了请求的路径,而同时使用请求方法字段(GET,POST,PUT,DELETE) 要与之表明的意思也很明确(前提:在增删改查的时候),也就只是增删改查而已。
43 |
44 | ## 我何时使用 Restful
45 |
46 | 这里我要说说我个人使用情况下,如果单单只是增删改查的话,我会使用 Restful 风格,好用是一方面,不必在修改数据的还要在 body 中添加 id 这个字段。其次 restful 确实也算广泛,但也仅仅只是在增删改查中。
47 |
48 | 实际业务中复杂情况太多了,有的时候仅仅这四个请求方法不能很明确的表达所要的意思,例如下面一些业务逻辑
49 |
50 | ```js
51 | POST user/login 发送登录请求
52 |
53 | POST user/register 发送注册请求
54 |
55 | POST user/info 获取个人信息
56 |
57 | POST user/forget 忘记个人密码
58 |
59 | POST user/getCode 获取验证码
60 | ```
61 |
62 | 此外还有充值,获取消费记录、登录记录等等就不一一列举了,总之这时候我毫不犹豫会使用 POST,可能有人会好奇,为啥获取信息和获取验证码的时候要使用 POST 请求,用 GET 不好吗?好,但后文会说为什么。
63 |
64 | ## 易猜测 api 接口
65 |
66 | 实际上,采用了 Restful 风格,几乎一猜就能猜到对应的 api。比如商品管理,无非就是获取商品列表,添加商品,编辑商品,删除商品。同时又传入的是对应的 ID,这要是 Mysql,ID 基本都是按顺序的,万一 api 鉴权没做好,都不知道数据怎么变动的。当然这种情况一般都是比较少见的了。
67 |
68 | ## 不易加密
69 |
70 | 上文不是说到为啥都要使用 POST 请求,原因也挺简单的,就是加密,GET 请求一般都不会携带过多参数,针对数据效验的话最多也就一个 MD5 效验,然而是远远不够的,而 POST 所能携带的数据不仅仅是 MD5 效验,还能携带风控算法,二次效验,浏览器指纹算法等等,能保证一定的防破解性。一些看似用 GET 请求方便的接口,但实际都要考虑所包含的风险,就如上面那个发送验证码的接口,如果不加以加密,特别容易仿造出与之对应的协议请求,再次仿造发送也不难。当然,对于这种限制类的业务,还是得要后端进行限制,例如 1 分钟只能发送一条,一天一号只能发送 10 条。
71 |
72 | ## 最后
73 |
74 | 其实可以发现绝大多数的网站基本上都不是采用 Restful 风格(貌似用的最多的也就是管理系统了),因为所涉及的业务逻辑实在是太复杂了,不单单只能使用请求方法来表明意思,有时候 Url 路径更能表达明确意思。
75 |
76 | Restful 风格想的太美好了,然而实际业务中 很多时候并不能单纯的通过 get post put delete 这四种请求发送来表明真实意义,所以我在增删改查的时候才会使用 Restful api 风格。
77 |
78 | 在我写项目中遇到一些复杂业务逻辑,我是毫不犹豫使用 Post 请求的,然后通过 url 路径表明 api 所要请求的路径,同时编写 Swagger Api 文档。什么样的风格都因人而异,主要自己用的习惯就行,毕竟 api 接口只是风格,并不作为标准来衡量。
79 |
--------------------------------------------------------------------------------
/blog/authors.yml:
--------------------------------------------------------------------------------
1 | 東雲研究所:
2 | name: 東雲研究所
3 | title: 前端 / 社会人
4 | url: https://github.com/xxsoftware
5 | image_url: /img/logo.webp
6 |
--------------------------------------------------------------------------------
/blog/develop/为什么使用pnpm.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: why-use-pnpm
3 | title: 为什么使用pnpm
4 | date: 2022-01-08
5 | authors: 東雲研究所
6 | tags: [node, pnpm]
7 | keywords: [node, pnpm]
8 | ---
9 |
10 |
11 |
12 | [pnpm 文档](https://pnpm.io/zh/)
13 |
14 | ## 前言
15 |
16 | 在一个 node 项目中免不了 node_modules 依赖,假设项目 A 用的了 Express 依赖,同时项目 B 也用到了 Express,并且两者所存放的位置不同,那么磁盘空间将会多出两份 Express 依赖,假设有 100 个项目,那么将会有有 100 倍的空间被浪费。这些空间还可以用磁盘空间来弥补,但是这 100 个项目如果都使用 npm i 去下载同样版本依赖,则是实实在在耗费网络资源去下载。
17 |
18 | pnpm 能解决以下两点问题
19 |
20 | - 包安装速度极快;
21 | - 磁盘空间利用非常高效。
22 |
23 | 而这些问题是一个 node 项目中常有的。相信此时的你都有点蠢蠢欲动了,而安装也很简单
24 |
25 | ## 安装
26 |
27 | 请查阅你的 node 版本与 pnpm 是否匹配 [安装 | pnpm](https://pnpm.io/zh/installation#兼容性)
28 |
29 | ```
30 | npm install -g pnpm
31 | ```
32 |
33 | ### 升级
34 |
35 | ```
36 | pnpm add -g pnpm
37 | ```
38 |
39 | 此时 pnpm 就已经安装完了,与 yarn 安装一样,都感觉没安装似的。
40 |
41 | ## 使用
42 |
43 | pnpm 命令几乎与 npm 一样,设置配置的方式也与 npm 相同,几乎不需要任何学习成本。查看 node_modules 属性会发现显示的空间貌似和原始的链接所占用的空间一样,但其实是同一个位置,官方中常用问题中也有介绍到 [常见问题 | pnpm](https://pnpm.io/zh/faq#如果包存储在全局存储中为什么我的-node_modules-使用了磁盘空间),所以真不用担心磁盘空间的问题。
44 |
45 | 这时候去查看 `D:\.pnpm-store\v3\files` 会发现都是一堆数字与字母命名的文件夹,而依赖都存放至这些杂乱无章的文件名之中。同时.pnpm-store 是根据你所在驱动器(这里是 F 盘)下创建的,可以通过 `pnpm store path`查看,也就是上文为什么说不要在 C 盘路径(包括桌面)去安装依赖了,所以不用担心 C 盘空间会越来越小(如果你的代码是在 C 盘编写的话,那当我没说)。
46 |
47 | ## 最后
48 |
49 | 不过还是要提醒一句,即便 pnpm 能解决磁盘问题,但还是存在一定的兼容性,如果一个项目是用 npm 或者 yarn 进行构建的,使用 pnpm 是绝对免不了一些问题,小问题暂时想不到,大问题无法运行,所以请三思再考虑对已有项目是否尝试升级 pnpm。
50 |
51 | 但我认为还是有必要尝试尝试下,不尝试,怎么能发现新大陆呢。
52 |
53 | > 参考链接:[关于现代包管理器的深度思考——为什么现在我更推荐 pnpm 而不是 npm/yarn? - 掘金 (juejin.cn)](https://juejin.cn/post/6932046455733485575#heading-14)
54 |
--------------------------------------------------------------------------------
/blog/lifestyle/世间再无击节客,左耳从此不听风.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: rip-ch
3 | title: 世间再无击节客,左耳从此不听风
4 | date: 2023-05-15
5 | authors: 東雲研究所
6 | tags: ['杂谈']
7 | keywords: ['杂谈']
8 | description: rip
9 | toc_max_heading_level: 3
10 | ---
11 |
12 | ## 骨灰级编程大佬“左耳朵耗子”因心梗离世,一条建议值一个亿!
13 |
14 | 5 月 15 日,中国互联网届顶级程序员、知名技术专家“左耳朵耗子”-陈皓,在两天前突发心梗逝世,年仅 47 岁。在中国互联网风云跌宕起伏的 20 年发展史中,陈皓参与了多项重量级产品的演进,先后在亚马逊、阿里云、天猫任职,他的技术博客酷壳影响了无数程序员,饿了么 CTO 曾经发文说,陈皓当时给他们的一条建议价值 1 一个亿!
15 |
16 | 
17 |
18 | 看到还是蛮震惊的,之前也看过他的视频博客,像 35 岁中年危机的视频还有工作也只做有价值的事之类的感触颇深
19 |
20 | 无论一个人怎么样,都不应对 TA 上纲上线进行“批斗”。如今陈皓突然离我们而去,他的博客依然是宝贵的财富,他的技术精神也值得宣扬继续传承下去,rip......
21 |
--------------------------------------------------------------------------------
/blog/lifestyle/第一次离职.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: dimission
3 | title: 第一次离职
4 | date: 2023-04-26
5 | authors: 東雲研究所
6 | tags: [随笔, 杂谈]
7 | keywords: [随笔, 杂谈]
8 | ---
9 |
10 | 四月份离职了,本来想趁着金四银五再找一份工作的,但是手头也没有趁手的个人项目,回想起来现在的个人项目要么是新技术出来时写的demo,要不就是在刚毕业时做的老技术项目了,正好五月份出去玩玩,回来好好沉淀一下自己做点个人项目。
11 | 而且在我域名和服务器过期之后,我已经很久没更新过笔记了,甚至我都不记得我的博客还有笔记这个东西,我也需要更新一下自己的博客了。
12 |
13 | 做完个人项目之后打算去面试找一份工作吧,已经好久没有面试过了。
--------------------------------------------------------------------------------
/blog/program/HTTP请求配置客户端SSL证书.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: http-config-client-ssl-certificate
3 | title: HTTP配置客户端SSL证书
4 | date: 2022-02-17
5 | authors: 東雲研究所
6 | tags: [http, ssl]
7 | keywords: [http, ssl]
8 | ---
9 |
10 | HTTPS 协议是由 SSL/TLS+HTTP 协议构建的可进行加密传输、身份认证的网络协议,要比 http 协议安全,很多大型互联网网站都用了都用了 https,那么他们之间有什么具体的区别,又如何配置呢?
11 |
12 |
13 |
14 | ## HTTP 和 HTTPS
15 |
16 | ### HTTP
17 |
18 | HTTP 协议也就是超文本传输协议,是一种使用明文数据传输的网络协议。一直以来 HTTP 协议都是最主流的网页协议,HTTP 协议被用于在 Web 浏览器和网站服务器之间传递信息,以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了 Web 浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息。
19 |
20 | 
21 |
22 | ### HTTPS
23 |
24 | 为了解决 HTTP 协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议 HTTPS,为了数据传输的安全,HTTPS 在 HTTP 的基础上加入了 SSL/TLS 协议,SSL/TLS 依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。HTTPS 协议可以理解为 HTTP 协议的升级,就是在 HTTP 的基础上增加了数据加密。在数据进行传输之前,对数据进行加密,然后再发送到服务器。这样,就算数据被第三者所截获,但是由于数据是加密的,所以你的个人信息仍然是安全的。这就是 HTTP 和 HTTPS 的最大区别。
25 |
26 | ### HTTP 和 HTTPS 的区别
27 |
28 | 
29 |
30 | #### 1.安全性不同
31 |
32 | https:// 前缀表明是用 SSL (安全套接字)或 TSL 加密的,你的电脑与服务器之间收发的信息传输将更加安全。当你使用浏览器访问一个 HTTP 网站的时候,你会发现浏览器会对该 HTTP 网站显示“不安全”的安全警告,提示用户当前所访问的网站可能会存在风险。
33 |
34 | 而假如你访问的是一个HTTPS网站时,情况却是完全不一样。你会发现浏览器的地址栏会变成绿色,企业名称会展示在地址栏中,地址栏上面还会出现一把“安全锁”的图标。这些都会给予用户很大的视觉上的安全体验。
35 |
36 | #### 2.网站申请流程不同
37 |
38 | https协议需要到CA申请证书,一般免费证书很少,需要交费,Web服务器启用SSL需要获得一个服务器证书并将该证书与要使用SSL的服务器绑定。
39 |
40 | #### 3.默认端口不同
41 |
42 | http和https使用的是完全不同的连接方式,同时使用的端口也不同,http使用的是80端口,https使用的是443端口。在网络模型中,HTTP工作于应用层,而HTTPS工作在传输层。
43 |
44 | #### 4.对搜索排名的提升
45 |
46 | 这也是很多站长所关注的地方。百度和谷歌两大搜索引擎都已经明确表示,HTTPS网站将会作为搜索排名的一个重要权重指标。也就是说HTTPS网站比起HTTP网站在搜索排名中更有优势。
47 |
48 | ## 如何配置 HTTPS
49 |
50 | ### 首先你需要一个 http 网站
51 |
52 | 这是最起码的吧
53 |
54 | ### 购买申请一个 SSL 证书
55 |
56 | 之后你就需要一个SSl证书了,我这里用的是阿里云的免费证书,阿里云可以免费申请20个SSL证书,当然你也可以直接购买。
57 |
58 | 
59 |
60 | ### 部署
61 |
62 | 之后你就可以直接一键部署了,不过我的服务器是在京东云上的caddy,caddy是自带HTTPS的。
63 |
64 | 将 example.com 替换为您的域名,如果您使用的是 IPv6,请将 type=A 替换为 type=AAAA :
65 |
66 | curl "https://cloudflare-dns.com/dns-query?name=example.com&type=A" \
67 | -H "accept: application/dns-json"
68 |
69 | 可能有些同学跑不通,到时候就要设置镜像或者下载刚刚在阿里云申请到的证书,把证书上传到服务器,然后在Caddyfile里
70 | 配置tls后面跟上你上传的证书名字。
71 | 之后再在命令行输入systemctl restart caddy重启caddy服务,你就发现你的网站从http协议变成https协议了
72 |
--------------------------------------------------------------------------------
/blog/program/如何给博客增加评论效果.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: blog-comments
3 | title: 如何给博客增加评论效果
4 | date: 2023-01-17
5 | authors: 東雲研究所
6 | tags: [vue, vite, Giscus, github]
7 | keywords: [vue, vite, Giscus, github]
8 | description: 如何用Giscus给博客增加评论效果
9 | ---
10 |
11 | 我们在写博客时有时候想有一个评论的效果,有时候可以利用Giscus和github的功能完成这个。
12 |
13 | ## giscus 介绍
14 |
15 | giscus是利用 GitHub Discussions 实现的评论系统。
16 | giscus 加载时,会使用 GitHub Discussions 搜索 API 根据选定的映射方式(如 URL、pathname、 等)来查找与当前页面关联的 discussion。如果找不到匹配的 discussion,giscus bot 就会在第一次有人留下评论或回应时自动创建一个 discussion。
17 |
18 | 访客如果想要评论,必须按照 GitHub OAuth 流程授权 giscus app 代表他发布,或者可以直接在 GitHub Discussion 里评论。你可以在 GitHub 上管理评论。
19 |
20 | ## 操作
21 |
22 | 1.先安装 vuepress-plugin-comment2
23 | pnpm add -D vuepress-plugin-comment2@next
24 |
25 | 2.设置插件
26 |
27 | ```Vue title='.vuepress/config.js'
28 | const { commentPlugin } = require("vuepress-plugin-comment2");
29 | module.exports =
30 | { plugins: [ commentPlugin({ // 插件选项 }), ], };
31 |
32 | ```
33 |
34 | 3.将仓库公开并开启评论区,以作为评论存放的地点
35 | 开启仓库的Disscussions
36 |
37 | 
38 |
39 | 
40 |
41 | 4. 在完成以上步骤后,请前往 Giscus 页面open in new window 获得你的设置。
42 |
43 | 
44 |
45 | 5.补足插件设置就完成了
46 |
47 | ```Vue title='.vuepress/config.js'
48 | const { commentPlugin } = require("vuepress-plugin-comment2");
49 | module.exports = {
50 | ... // 省略Vuepress的配置
51 | ...
52 | plugins: [
53 | // vuepress-plugin-comment2评论与阅读量插件
54 | commentPlugin({
55 | // 插件选项
56 | provider: "Giscus", //评论服务提供者。
57 | comment: true, //启用评论功能
58 | repo: "Chengyunlai/Xxxx", //远程仓库
59 | repoId: "xxx", //对应自己的仓库Id
60 | category: "Announcements",
61 | categoryId: "xxx" //对应自己的分类Id
62 | }),
63 | ],
64 | ...
65 | ...
66 | };
67 | ```
68 |
69 | 
70 |
--------------------------------------------------------------------------------
/blog/project/使用Vite开发Chrome插件.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: Vite-chrome-extension
3 | title: 使用Vite开发Chrome插件
4 | date: 2021-09-18
5 | authors: 東雲研究所
6 | tags: [chrome, plugin, vue, develop]
7 | keywords: [chrome, plugin, vue, develop]
8 | description: 使用 Vue2 开发一个 Chrome 插件
9 | ---
10 |
11 | 
12 |
13 |
14 |
15 | ## 前言
16 |
17 | 之前学习开发 Chrome 插件的时候,用的是原生的技术,效率低,而这次发现了一个基于 vite 的浏览器插件入门模板,分享学习一下
18 |
19 | 相关代码开源[github 地址](https://github.com/antfu/vitesse-webext)
20 |
21 | ## 环境搭建
22 |
23 | ```sh
24 | npx degit antfu/vitesse-webext my-webext
25 | cd my-webext
26 | pnpm i
27 | ```
28 |
29 | 安装完就可以跑了
30 |
31 | ### 项目结构
32 |
33 | ```
34 | src - main source.
35 | contentScript - scripts and components to be injected as content_script
36 | background - scripts for background.
37 | components - auto-imported Vue components that are shared in popup and options page.
38 | styles - styles shared in popup and options page
39 | assets - assets used in Vue components
40 | manifest.ts - manifest for the extension.
41 | extension - extension package root.
42 | assets - static assets (mainly for manifest.json).
43 | dist - built files, also serve stub entry for Vite on development.
44 | scripts - development and bundling helper scripts.
45 |
46 |
47 | ```
48 |
49 | 开发模式 pnpm dev
50 | 如果是 firefox pnpm start:firefox
51 | 生成扩展pnpm build,然后将文件打包在 extension 以下
52 |
53 | 整体的开发和 vue 开发基本上没太大的区别,不过有很多 api
54 |
55 | 在 src/popup/App.vue 可以修改扩展框弹出的内容
56 |
57 | ```vue title="src/popup/App.vue"
58 |
65 |
66 |
67 |
68 |
69 | Popup
70 |
71 |
72 |
73 |
74 | Storage: {{ storageDemo }}
75 |
76 |
77 |
78 | ```
79 |
80 | ## 相关模板
81 |
82 | [vitesse-webext](https://github.com/antfu/vitesse-webext)
83 |
84 | [plasmo](https://www.plasmo.com/)
85 |
86 | ## 整体体验
87 |
88 | 当时写 Chrome 插件的效率比较低,还不能用框架。现在基于 vite 写一个插件就和编写网页一样简单,也非常感谢 antfu 大佬能带来这么一款模板脚手架,我目前仅仅只是体验了一下,写了几个跳转页面 demo,还需要了解更多 api
89 |
--------------------------------------------------------------------------------
/blog/project/第二个博客搭建之Docusaurus.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: second-blog-is-docusaurus
3 | title: 第二个博客搭建之Docusaurus
4 | date: 2021-08-20
5 | authors: 東雲研究所
6 | tags: [blog, docusaurus, project]
7 | keywords: [blog, docusaurus, project]
8 | description: 使用 docusaurus 搭建个人博客,并对其主题进行魔改
9 | sticky: 5
10 | ---
11 |
12 | 博客地址: [東雲研究所的小站](https://blog.xxsoftware.fun/)
13 |
14 | 时隔近半年没好好整理文章,博客也写的不像个人样。:joy:
15 |
16 | 大半年没更新博客,一直忙着写项目(写到手软的那种),然后无意间在 B 站看到一个 Up 主 [峰华前端工程师](https://zxuqian.cn/) 基于 React 驱动的静态网站生成器搭建的个人博客。第一眼看到该站点的时候惊艳到我了,于是我在其基础上并魔改了一些页面功能,作为个人站点使用。
17 |
18 | > 不过国内 docusaurus 的使用者是真的少,Vuepress 都快烂大街了...
19 |
20 |
21 |
22 | ## 安装
23 |
24 | 如果你想搭建一个类似的博客,可以 [fork 本项目](https://github.com/xxsoftware/blog/fork),修改个人信息,并将文章迁移过来。
25 |
26 | ```bash
27 | git clone https://github.com/xxsoftware/blog
28 | cd blog
29 | yarn
30 | yarn start
31 | ```
32 |
33 | ## 一些页面
34 |
35 | ### [博客页](/)
36 |
37 | 
38 |
39 | - 支持 3 种博文信息展示
40 | - 博客个人信息卡片
41 | - 可根据 `sticky` 字段对文章进行置顶推荐
42 |
43 | ### [归档页](/archive)
44 |
45 | 
46 |
47 | ### [资源导航](/resource)
48 |
49 | 
50 |
51 | - 在此分享所收藏的一些好用、实用网站。
52 |
53 | ### 评论
54 |
55 | 
56 |
57 | - 接入 [giscus](https://giscus.app) 作为评论系统,支持 GitHub 登录。
58 |
59 | ### [项目](/project)
60 |
61 | 
62 |
63 | - 存放你的项目,或是当做一个作品集用于展示。
64 |
65 | ## 部署
66 |
67 | 按传统的方式,你编写好一篇文章后,需要重新打包成静态文件(.html),然后将静态文件上传到服务器(需要自己准备)上,然后通过 nginx 配置域名访问。如今有了自动化部署,你只需要将代码 push 到 Github 上,然后通过 CI/CD 自动化部署到服务器上。可以参考 [ci.yml](https://github.com/xxsoftware/blog/blob/main/.github/workflows/ci.yml) 配置文件。
68 |
69 | 这里推荐使用 [Vercel 部署个人博客](/vercel-deploy-blog),部署十分简单,你甚至不需要服务器,只需要有个 Github 账号,将你的博客项目添加为一个仓库中即可(也许需要科学上网)。
70 |
71 | ## 最后
72 |
73 | 博客的意义在于记录,记录自己的成长,记录自己的所思所想,记录自己的所学所得。希望更多的时间用在创作内容上,而不是在搭建博客上。
74 |
75 | 也就不浪费口舌了,博客搭建完毕,应该好好的去编写有意义的文章,才能够吸引他人的阅读。
76 |
--------------------------------------------------------------------------------
/blog/reference/记一次前端面试过程.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: frontend-interview-experience
3 | title: 记第一次前端面试过程
4 | date: 2021-07-25
5 | authors: 東雲研究所
6 | tags: [随笔, 面试, fontend]
7 | keywords: [随笔, 面试, fontend]
8 | description: 记录一次前端面试过程
9 | draft: true
10 | ---
11 |
12 | 终于毕业了,玩了一段时间之后准备找工作的事情,然后面了第一次试遇到了很多不懂的事情
13 |
14 | 面试之前也没背过八股文,在学校里经常写vue的我以为面试是相对简单的事情,没想到第三个问题直接把我难住了,我记得是0.1+0.2为什么不等于0.3,平时都没怎么注意过这种问题直接把我考住了,后面还有setInterval的最短间隔时间,直接把我整的头晕了,后面有关vue的问题都答得不太好了。
15 |
16 | 面试之后我也不好意思问人家了,得,还是回去背背八股文和做做项目沉淀一下吧。
17 |
--------------------------------------------------------------------------------
/data/friend.ts:
--------------------------------------------------------------------------------
1 | export const Friends: Friend[] = []
2 |
3 | export type Friend = {
4 | title: string
5 | description: string
6 | website: string
7 | avatar?: any
8 | }
9 |
--------------------------------------------------------------------------------
/data/project.ts:
--------------------------------------------------------------------------------
1 | export const projects: Project[] = [
2 | {
3 | title: '東雲研究所的小站',
4 | description: '基于Docusaurus v2 静态网站生成器实现个人博客',
5 | preview: 'https://img.xxsoftware.fun/blog.png',
6 | website: 'https://blog.xxsoftware.fun/',
7 | source: 'https://github.com/xxsoftware/blog',
8 | tags: ['opensource', 'design', 'favorite'],
9 | type: 'web',
10 | },
11 | {
12 | title: '前端示例代码库',
13 | description:
14 | '📦 整理前端样式和功能的实现代码,可以用来寻找灵感或直接使用示例中的代码',
15 | preview: 'https://img.xxsoftware.fun/lib.png',
16 | website: 'https://lib.xxsoftware.fun/',
17 | source: 'https://gitee.com/xxsoftware/vue',
18 | tags: ['opensource', 'design'],
19 | type: 'web',
20 | },
21 | {
22 | title: 'xx记账',
23 | description:
24 | 'trao+vue3+pinia+nutUI+echarts构建的记账微信小程序',
25 | preview: 'https://img.xxsoftware.fun/taro-app.png',
26 | website: 'https://img.xxsoftware.fun/gh_6f9f88ffcb9a_1280.jpg',
27 | source: 'https://gitee.com/xxsoftware/taro-app',
28 | tags: ['opensource', 'product'],
29 | type: 'web',
30 | },
31 | {
32 | title: '小游戏平台',
33 | description:
34 | 'vitesse+elementplus构建的小游戏平台',
35 | preview: 'https://img.xxsoftware.fun/game-minesweeper.png',
36 | website: 'https://www.xxsoftware.fun/',
37 | source: 'https://gitee.com/xxsoftware/games',
38 | tags: ['opensource', 'product'],
39 | type: 'web',
40 | },
41 | ]
42 |
43 | export type Tag = {
44 | label: string
45 | description: string
46 | color: string
47 | }
48 |
49 | export type TagType =
50 | | 'favorite'
51 | | 'opensource'
52 | | 'product'
53 | | 'design'
54 | | 'large'
55 | | 'personal'
56 |
57 | export type ProjectType = 'personal' | 'web' | 'app' | 'toy' | 'other'
58 |
59 | export type Project = {
60 | title: string
61 | description: string
62 | preview?: any
63 | website: string
64 | source?: string | null
65 | tags: TagType[]
66 | type: ProjectType
67 | }
68 |
69 | export const Tags: Record = {
70 | favorite: {
71 | label: '喜爱',
72 | description: '我最喜欢的网站,一定要去看看!',
73 | color: '#e9669e',
74 | },
75 | opensource: {
76 | label: '开源',
77 | description: '开源项目可以提供灵感!',
78 | color: '#39ca30',
79 | },
80 | product: {
81 | label: '产品',
82 | description: '与产品相关的项目!',
83 | color: '#dfd545',
84 | },
85 | design: {
86 | label: '设计',
87 | description: '设计漂亮的网站!',
88 | color: '#a44fb7',
89 | },
90 | large: {
91 | label: '大型',
92 | description: '大型项目,原多于平均数的页面',
93 | color: '#8c2f00',
94 | },
95 | personal: {
96 | label: '个人',
97 | description: '个人项目',
98 | color: '#12affa',
99 | },
100 | }
101 |
102 | export const TagList = Object.keys(Tags) as TagType[]
103 |
104 | export const groupByProjects = projects.reduce((group, project) => {
105 | const { type } = project
106 | group[type] = group[type] ?? []
107 | group[type].push(project)
108 | return group
109 | }, {} as Record)
110 |
--------------------------------------------------------------------------------
/docs/skill/algorithm/数组中的第K个最大元素.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: find-number
3 | slug: /find-number
4 | title: 数组中的第K个最大元素
5 | authors: 東雲研究所
6 | tags: [algorithm]
7 | keywords: [algorithm]
8 | ---
9 |
10 | ## 数组中的第 K 个最大元素
11 |
12 | 在未排序的数组中找到第`k`个最大的元素。请注意,你需要找的是数组排序后的第`k`个最大的元素,而不是第`k`个不同的元素。
13 |
14 | ### 示例
15 |
16 | ```
17 | 输入: [3,2,1,5,6,4] 和 k = 2
18 | 输出: 5
19 | ```
20 |
21 | ```
22 | 输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
23 | 输出: 4
24 | ```
25 |
26 | ### 题解
27 |
28 | ```javascript
29 | /**
30 | * @param {number[]} nums
31 | * @param {number} k
32 | * @return {number}
33 | */
34 | var findKthLargest = function (arr, k) {
35 | var adjustHeap = function (arr, i, n) {
36 | for (let k = 2 * i + 1; k < n; k = 2 * k + 1) {
37 | let parent = arr[i]
38 | if (k + 1 < n && arr[k] < arr[k + 1]) ++k
39 | if (parent < arr[k]) {
40 | ;[arr[i], arr[k]] = [arr[k], arr[i]]
41 | i = k
42 | } else {
43 | break
44 | }
45 | }
46 | }
47 | var n = arr.length
48 | for (let i = Math.floor(n / 2 - 1); i >= 0; --i) adjustHeap(arr, i, n)
49 | var target = 0
50 | for (let i = n - 1; i >= n - k; --i) {
51 | target = arr[0]
52 | if (i - 1 >= n - k) {
53 | ;[arr[0], arr[i]] = [arr[i], arr[0]]
54 | adjustHeap(arr, 0, i)
55 | }
56 | }
57 | return target
58 | }
59 | ```
60 |
61 | ### 思路
62 |
63 | 采用大顶堆的数据结构解决问题,大顶堆要求根节点的关键字既大于或等于左子树的关键字值,又大于或等于右子树的关键字值并且为完全二叉树,首先定义`adjustHeap`函数左调整堆使用,首先以`i`作为双亲元素的下标,以`k`作为左孩子的下标,当右孩子存在时判断右孩子是否大于左孩子,大于左孩子则将`k`作为右孩子的指向下标,然后判断双亲值与`k`指向的孩子的节点值的大小,如果孩子值大于双亲值则交换,并且以`k`作为双亲节点沿着路径继续向下调整,否则就结束本次循环,然后定义`n`作为数组长度,之后将堆中每个作为双亲节点的子树进行调整,使整个树符合大顶堆的特征,之后进行`k`次循环,由于是大顶堆且已调整完成将顶堆的顶值也就是最大值取出赋值给`target`,之后判断是否需要进一步调整,如果需要则交换顶端值与最后一个值,然后调整顶堆符合大顶堆的条件,同样取出顶堆最大值,取出`k`次即可完成。
64 |
--------------------------------------------------------------------------------
/docs/skill/code-specification/editorconfig.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: editorconfig
3 | slug: /editorconfig
4 | title: editorconfig
5 | authors: 東雲研究所
6 | keywords: ['code-specification', 'editorconfig']
7 | ---
8 |
9 | [Editorconfig](https://editorconfig.org/) 有助于跨各种编辑器和 IDE 为处理同一项目的多个开发人员维护一致的编码样式。
10 |
11 | ## 使用 ESLint 做代码 lint,那么为什么还要使用 .editorconfig 呢?
12 |
13 | - ESLint 确实包含 .editorconfig 中的一些属性,如缩进等,但并不全部包含,如 .editorconfig 中的 insert_final_newline 属性 Eslint 就没有。Eslint 更偏向于对语法的提示,如定义了一个变量但是没有使用时应该给予提醒。而 .editorconfig 更偏向于代码风格,如缩进等。
14 | - ESLint 仅仅支持对 js 文件的校验,而 .editorconfig 不光可以检验 js 文件的代码风格,还可以对 .py(python 文件)、.md(markdown 文件)进行代码风格控制。
15 |
16 | > 根据项目需要,Eslint 和 .editorconfig 并不冲突,同时配合使用可以使代码风格更加优雅。
17 |
18 | ## 安装 EditorConfig
19 |
20 | [EditorConfig for VS Code](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig)
21 |
22 | 创建 `.editorconfig`,示例内容如下
23 |
24 | ```editorconfig title='.editorconfig'
25 | # http://editorconfig.org
26 |
27 | root = true
28 |
29 | [*]
30 | charset = utf-8
31 | indent_style = space
32 | indent_size = 2
33 | end_of_line = lf
34 | insert_final_newline = true
35 | trim_trailing_whitespace = true
36 | quote_type = single
37 |
38 | [*.md]
39 | insert_final_newline = false
40 | trim_trailing_whitespace = false
41 | ```
--------------------------------------------------------------------------------
/docs/skill/code-specification/eslint.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: eslint
3 | slug: /eslint
4 | title: eslint
5 | authors: 東雲研究所
6 | keywords: ['code-specification', 'eslint']
7 | ---
8 |
9 | ESLint 是一种用于识别和报告 ECMAScript/JavaScript 代码中发现的模式的工具,目的是使代码更加一致并避免错误。
10 |
11 | [Getting Started with ESLint](https://eslint.org/docs/latest/user-guide/getting-started)
12 |
13 | ## eslint-config
14 |
15 | 这里强烈推荐 [antfu/eslint-config](https://github.com/antfu/eslint-config),以及大佬的文章 [Why I don't use Prettier (antfu.me)](https://antfu.me/posts/why-not-prettier)
16 |
17 | 这份 eslint 配置对于 ts 与 vue 已经足够完整,如果还有其他需求,可自行添加 rule 或使用[overrides](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#how-do-overrides-work)。
18 |
19 | ## 在 Vscode 中集成 ESlint 插件
20 |
21 | - 在 VScode 插件市场安装 [ESLint 插件](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
22 |
23 | - 开启代码保存时自动执行 ESLint 修复功能(全局设置)
24 |
25 | ```
26 | "editor.codeActionsOnSave": {
27 | "source.fixAll": false,
28 | "source.fixAll.eslint": true,
29 | "source.organizeImports": false
30 | },
31 | ```
32 |
33 | - 工作区示例如下
34 |
35 | ```json title='.vscode/settings.json'
36 | {
37 | "prettier.enable": false,
38 | "editor.formatOnSave": false,
39 | "editor.codeActionsOnSave": {
40 | "source.fixAll.eslint": true
41 | }
42 | }
43 | ```
44 |
45 | ## 注意事项
46 |
47 | 由于 eslint 配置相对繁琐,所以很多时候编辑器的 eslint 可能都没有生效,具体看编辑器下方状态栏或者日志输出查看 ESLint 状态。如果为警告(黄色感叹号)或者错误(红色),那么 ESLint 就是没配置好,可能缺少某些依赖文件或是配置文件写错了。
48 |
--------------------------------------------------------------------------------
/docs/skill/code-specification/guides.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: code-specification-guides
3 | slug: /code-specification
4 | title: 代码规范
5 | authors: 東雲研究所
6 | keywords: ['code-specification']
7 | ---
8 |
9 | 杂乱不堪如同屎山般的代码,会让开发者头皮发麻,无从下手,往往更容易诱发bug。而一个良好的代码规范,能够修复团队的各个成员间代码格式不统一,有利于维护与审查。
10 |
11 | 这里主要不是介绍具体的代码规范标准,这些在对应的官方文档中的风格指南可查看。本文主要利用插件工具,在保存代码与上传代码时,根据配置规则来规范代码。
12 |
13 | > 本栏主要针对前端项目的代码规范配置,使用 VSCode 文本编辑器及其插件配置。
14 |
15 | import DocCardList from '@theme/DocCardList'; import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
16 |
17 |
--------------------------------------------------------------------------------
/docs/skill/code-specification/husky.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: husky
3 | slug: /husky
4 | title: husky
5 | authors: 東雲研究所
6 | keywords: ['code-style', 'husky']
7 | ---
8 |
9 | 为了确保只有合格的代码才能够提交到仓库。需要配置自动化脚本,确保代码在提交前通过了代码验证工具的检验。
10 |
11 | 实际上 git 本身就设计了生命周期钩子来完成这个任务。但是设置过程比较复杂。所以通常情况下会使用 husky 来简化配置。
12 |
13 | [Husky](https://typicode.github.io/husky/#/)
14 |
15 | [Git - githooks](https://git-scm.com/docs/githooks)
16 |
17 | ```bash
18 | pnpm i husky -D
19 | ```
20 |
21 | 会创建一个 npm script
22 |
23 | ```
24 | npm set-script prepare "husky install"
25 | ```
26 |
27 | ## githooks
28 |
29 | ### 在 commit 提交前执行 lint 代码校验
30 |
31 | 执行下方命令,以添加生命周期钩子:
32 |
33 | ```sql
34 | npx husky add .husky/pre-commit "pnpm lint"
35 | ```
36 |
37 | 会创建 `.husky/pre-commit` 文件,其内容如下
38 |
39 | ```bash title='.husky/pre-commit'
40 | #!/usr/bin/env sh
41 | . "$(dirname -- "$0")/_/husky.sh"
42 |
43 | pnpm lint
44 | ```
45 |
46 | 在每次提交时,都将会执行 lint 脚本来检查代码。
47 |
48 | ### 在 push 之前通过单元测试
49 |
50 | 不过更多的做法都是用 **github action** 配置 CI 在虚拟机上跑测试,而不是本地测试。(故这步可省略)
51 |
52 | 执行下方命令,以添加生命周期钩子:
53 |
54 | ```bash
55 | npx husky add .husky/pre-push "pnpm test"
56 | ```
57 |
58 | ### 提交时自动检查 commit 信息是否符合要求
59 |
60 | [commitlint - Lint commit messages](https://commitlint.js.org/#/?id=getting-started)
61 |
62 | 安装
63 |
64 | ```bash
65 | pnpm i -g @commitlint/cli @commitlint/config-conventional
66 | ```
67 |
68 | ```bash
69 | echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
70 | ```
71 |
72 | :::caution 注意
73 |
74 | windows 系统请勿使用上行命令,否则会导致编码不是 UTF-8。建议直接复制文本内容到 `commitlint.config.js`
75 |
76 | ```javascript title='commitlint.config.js'
77 | module.exports = {extends: ['@commitlint/config-conventional']};
78 | ```
79 |
80 | :::
81 |
82 | 将 commitlint 脚本添加到 githooks 中, 让每次提交前都验证信息是否正常。
83 |
84 | ```bash
85 | npx husky add .husky/commit-msg "npx --no-install commitlint --edit "$1""
86 | ```
87 |
88 | 其内容如下
89 |
90 | ```bash title='.husky/commit-msg'
91 | #!/bin/sh
92 | . "$(dirname "$0")/_/husky.sh"
93 |
94 | npx --no-install commitlint --edit "$1"
95 | ```
96 |
97 | 测试 commit 提交 `echo 'foo: bar' | commitlint` 将会报错,不符合 commit msg 规范。
98 |
99 | ```
100 | echo 'foo: bar' | commitlint
101 | ⧗ input: foo: bar✖ type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test] [type-enum]
102 |
103 | ✖ found 1 problems, 0 warnings
104 | ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
105 | ```
106 |
--------------------------------------------------------------------------------
/docs/skill/code-specification/npmrc.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: npmrc
3 | slug: /npmrc
4 | title: npmrc
5 | authors: 東雲研究所
6 | keywords: ['code-specification', 'npmrc']
7 | ---
8 |
9 | 对于 pnpm 项目,通常会有一个 `.npmrc` 文件,用于配置npm的一些参数,比如使用pnpm的严格模式等,其内容如下。
10 |
11 | ```properties title='.npmrc'
12 | shamefully-hoist=true
13 | strict-peer-dependencies=false
14 | shell-emulator=true
15 | ```
16 |
17 | 此外,配置仓库镜像源,node版本等等。更多配置可看 [.npmrc](https://pnpm.io/npmrc)。
18 |
--------------------------------------------------------------------------------
/docs/skill/code-specification/prettier.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: prettier
3 | slug: /prettier
4 | title: prettier
5 | authors: 東雲研究所
6 | keywords: ['code-specification', 'prettier']
7 | ---
8 |
9 | Prettier 是一个固执己见的代码格式化程序。
10 |
11 | [Install · Prettier](https://prettier.io/docs/en/install.html)
12 |
13 | ## 集成在 ESlint 中
14 |
15 | ESlint 与 Prettier 可能会冲突,故需做如下设置:
16 |
17 | ```js
18 | //1. 安装 eslint-config-prettier 插件
19 | npm i -D eslint-config-prettier
20 | //2. 在 eslint 的配置文件中写入以下内容
21 | extends: ['plugin:prettier/recommended'], // 避免与 prettier 冲突
22 | ```
23 |
24 | ## prettier 与 eslint 如何选择
25 |
26 | prettier 只需要按照一个 vscode 插件,几乎没有任何门槛,按下 Ctrl + Alt + F 就可以美化你的代码。而 eslint 需要配合代码编辑器与相关规则,通过保存文件或者执行 eslint 命令才能格式化代码。但往往也是因为过少的配置,使 prettier 对代码的约束不如 eslint。
27 |
28 | 可以看看 Antfu 大佬的博客 [Why I don't use Prettier (antfu.me)](https://antfu.me/posts/why-not-prettier),阐述了他为何不使用 Prettier。
29 |
30 | 这两个我都有在使用,在临时编写 demo 代码的时候,肯定优先使用 prettier。
31 |
32 | 但是在实际项目中,如果不使用 eslint 的话,每次保存代码都需要手动格式化,还是比较繁琐的。
33 |
--------------------------------------------------------------------------------
/docs/skill/code-specification/stylelint.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: stylelint
3 | slug: /stylelint
4 | title: stylelint
5 | authors: 東雲研究所
6 | keywords: ['code-specification', 'stylelint']
7 | ---
8 |
9 | stylelint 主要针对 css 样式进行格式化(包括css预处理器),同时对一些属性拼写进行检查。
10 |
11 | [Getting started | Stylelint](https://stylelint.io/user-guide/get-started)
12 |
13 | 配置文件示例:
14 |
15 | ```json title='.stylelintrc.json'
16 | {
17 | "extends": ["stylelint-config-recommended","stylelint-config-standard"],
18 | "rules": {
19 | "indentation": 4
20 | }
21 | }
22 | ```
23 |
24 | ## 配合 prettier
25 |
26 | [prettier/stylelint-config-prettier](https://github.com/prettier/stylelint-config-prettier)
--------------------------------------------------------------------------------
/docs/skill/css/如何实现toast垂直居中.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: toast-center
3 | slug: /toast-center
4 | title: 如何实现toast垂直居中
5 | date: 2023-2-11
6 | authors: 東雲研究所
7 | tags: [css]
8 | keywords: [css]
9 | ---
10 |
11 | toast效果是前端非常常见的一种效果,就是中间有个文本弹框的效果
12 |
13 | ## 第 1 种方法
14 |
15 | .toast{
16 | position: fixed;
17 | left:50%;
18 | top:50%;
19 | transform:translate(-50%,-50%);
20 | }
21 | 这是一个非常经典的居中,但是有一个缺点那就是他把transform属性占用掉了,如果我要写个hover让他scale一下大小就很烦了
22 | 还有一个问题就是文字多的情况下会换行
23 |
24 | ## 第 2 种方法
25 |
26 | .toast{
27 | position:fixed;
28 | width:fit-content;
29 | inset-inline:1rem
30 | margin-inline:auto;
31 | }
32 | 这个就比较好了,不过兼容性可能有点问题
33 |
34 | ## 第 3 种方法
35 |
36 | .toast{
37 | position:fixed;
38 | width:fit-content;
39 | left:1rem;
40 | right:1rem;
41 | margin-left:auto;
42 | margin-right:auto;
43 | }
44 | 第三个就完美了
45 |
46 | 这篇文章是看(张鑫旭老师的视频)[https://www.bilibili.com/video/BV19Y411q7kH/?spm_id_from=333.999.0.0&vd_source=ca3c9326184c271fd49027dac62987a6]学的,如果要深入 css,可以购买《css 新世界》
47 |
--------------------------------------------------------------------------------
/docs/skill/css/纯css实现固定宽高比.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: css-Fixed-wh
3 | slug: /css-Fixed-wh
4 | title: 纯css实现固定宽高比
5 | date: 2022-3-11
6 | authors: 東雲研究所
7 | tags: [css]
8 | keywords: [css]
9 | ---
10 |
11 | 遇到实际开发时经常有这么一个需求,需要实现盒子的宽高比始终喂 2:1 在这种情况下我们可以用纯 css 实现这个需求
12 |
13 | ## 直接通过 css 宽高属性 aspect-ratio 控制
14 |
15 | ```html
16 |
23 | ```
24 |
25 | ## 上面的方法虽然简单 但是在兼容性上还是稍微欠缺点,在一些古早的项目了我们可以这么写
26 |
27 | ```html
28 |
36 | ```
37 |
38 | 这是因为一个机制,当一个盒子高度为 0 时且 padding-top 的值为百分比时,padding-top 的依赖是我们的宽度
39 | 我们依靠这个机制也能完成2/1
40 | 注意:在用第二种方法时因为box的高度为0 所以box里的文本会偏出box。
41 |
--------------------------------------------------------------------------------
/docs/skill/git/git commit规范.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: git-conmit-specification
3 | slug: git-conmit-specification
4 | title: git commit规范
5 | date: 2021-08-31
6 | authors: 東雲研究所
7 | tags: [git, commit]
8 | keywords: [git, commit]
9 | ---
10 |
11 |
12 |
13 | 提交规范主要是为了让开发者提交完整的更新信息,方便查阅。
14 |
15 | 目前最为流行的提交信息规范来自于 Angular 团队。
16 |
17 | 规范中,主要就是要求提交内容要进行分类并填写内容,更为严格的规定是要求标注开发模块,整个语法如下
18 |
19 | ```bash
20 | type(scope?): subject #scope is optional; multiple scopes are supported (current delimiter options: "/", "\" and ",")
21 | ```
22 |
23 | | type | commit 的类型 |
24 | | ----------- | ---------------------------------------------------------- |
25 | | feat | 新功能、新特性 (feature) |
26 | | fix | 修改 bug |
27 | | perf | 更改代码,以提高性能 |
28 | | refactor | 代码重构(重构,在不影响代码内部行为、功能下的代码修改) |
29 | | docs | 文档修改 (documentation) |
30 | | style | 代码格式修改, 注意不是 css 修改(例如分号修改) |
31 | | test | 测试用例新增、修改 |
32 | | build | 影响项目构建或依赖项修改 |
33 | | revert | 恢复上一次提交 (撤销,版本回退) |
34 | | ci | 持续集成相关文件修改 |
35 | | chore | 其他修改(不在上述类型中的修改),构建过程或辅助工具的变动 |
36 | | release | 发布新版本 |
37 | | workflow | 工作流相关文件修改 |
38 | | improvement | 改进 |
39 |
40 | 以下是一些示例:
41 |
42 | | commit message | 描述 |
43 | | ---------------------------------- | ------------------------- |
44 | | chore: init | 初始化项目 |
45 | | chore: update deps | 更新依赖 |
46 | | chore: wording | 调整文字(措词) |
47 | | chore: fix typos | 修复拼写错误 |
48 | | chore: release v1.0.0 | 发布 1.0.0 版本 |
49 | | fix: icon size | 修复图标大小 |
50 | | fix: value.length -> values.length | value 变量调整为 values |
51 | | feat(blog): add comment section | blog 新增评论部分 |
52 | | feat: support typescript | 新增 typescript 支持 |
53 | | feat: improve xxx types | 改善 xxx 类型 |
54 | | style(component): code | 调整 component 代码样式 |
55 | | refactor: xxx | 重构 xxx |
56 | | perf(utils): random function | 优化 utils 的 random 函数 |
57 | | docs: xxx.md | 添加 xxx.md 文章 |
58 |
59 | 更多示例可以参考主流开源项目的 commit。
60 |
61 | ## 检查 commit 规范
62 |
63 | 要检查 commit message 是否符合要求,可以使用 [commitlint](https://github.com/conventional-changelog/commitlint) 工具,并配合 [husky](https://github.com/typicode/husky) 对每次提交的 commit 进行检查。
64 |
65 | 当然规范不是强求,但 commit message 一定要能简要说明本次代码的改动主要部分,有利于他人与自己后期查看代码记录。
66 |
--------------------------------------------------------------------------------
/docs/skill/git/github apps示例.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: github-apps-example
3 | slug: github-apps-example
4 | title: github apps示例
5 | date: 2021-10-01
6 | authors: 東雲研究所
7 | tags: [github, app]
8 | keywords: [github, app]
9 | ---
10 |
11 |
12 |
13 | ### Github Dependabot
14 |
15 | 介绍:[About Dependabot security updates - GitHub Docs](https://docs.github.com/cn/code-security/dependabot/dependabot-security-updates/about-dependabot-security-updates)
16 |
17 | 简单说就是一个能够自动更新项目依赖,确保仓库代码依赖的包和应用程序一直处于最新版本的机器人。
18 |
19 | 将 `dependabot.yml` 配置文件放入仓库的 `.github` 目录中即可开启。当然也可以到 `Insights` => `Dependency graph` => `Dependabot` 中开启。如下图
20 |
21 | 
22 |
23 | 然后创建你的配置文件,默认内容如下
24 |
25 | 
26 |
27 | 其中要修改 package-ecosystem 配置,也就是包管理器,比如 node 就用 npm,python 就用 pip。可以在 [About Dependabot version updates - GitHub Docs](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/about-dependabot-version-updates#supported-repositories-and-ecosystems) 中查看。
28 |
29 | 然后配置完毕后,根据时间周期,就会自动检测依赖更新,并创建一个 pull request 请求,仓库拥有者根据实际需要合并即可。
30 |
31 | ### [Stale](https://github.com/marketplace/stale)
32 |
33 | 可在一段时间不活动后关闭废弃的问题。即**关闭长时间未回复的 issues**。
34 |
35 | ### [Imgbot](https://github.com/marketplace/imgbot)
36 |
37 | Imgbot 是一个友好的机器人,可以优化您的图像并节省您的时间。优化的图像意味着在不牺牲质量的情况下减小文件大小。
38 |
39 | ### [giscus](https://github.com/marketplace/giscus)
40 |
41 | 由 GitHub 讨论提供支持的评论系统。让访问者通过 GitHub 在您的网站上发表评论和反应!
42 |
43 | ### [WakaTime](https://github.com/marketplace/wakatime)
44 |
45 | 从编程活动中自动生成的生产力指标、见解和时间跟踪。
46 |
47 | ### [wxwork](https://github.com/marketplace/wxwork-github-webhook)
48 |
49 | Github 企业微信群机器人,无需配置轻松集成 Github 与 企业微信。
50 |
--------------------------------------------------------------------------------
/docs/skill/git/github 其他文件.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: github-other
3 | slug: github-other
4 | title: .github 其他文件
5 | date: 2021-10-01
6 | authors: 東雲研究所
7 | tags: [github]
8 | keywords: [github]
9 | ---
10 |
11 |
12 |
13 | ### ISSUE_TEMPLATE
14 |
15 | **issues 默认模版**
16 |
17 | [Configuring issue templates for your repository - GitHub Docs](https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository)
18 |
19 | ### CODEOWNERS
20 |
21 | **仓库代码拥有者。用于合并请求批准**
22 |
23 | ```
24 | # https://help.github.com/articles/about-codeowners/
25 |
26 | * @xxsoftware
27 | ```
28 |
29 | ### FUNDING.yml
30 |
31 | **配置赞助者按钮**
32 |
33 | [在仓库中显示赞助者按钮 - GitHub Docs](https://docs.github.com/cn/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository#about-funding-files)
34 |
35 | ### CONTRIBUTING.md
36 |
37 | **贡献指南**
38 |
--------------------------------------------------------------------------------
/docs/skill/git/git修改默认分支main.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: git-change-default-branch
3 | slug: git-change-default-branch
4 | title: git修改默认分支main
5 | date: 2021-08-04
6 | authors: 東雲研究所
7 | tags: [git]
8 | keywords: [git]
9 | ---
10 |
11 |
12 |
13 | ## 前言
14 |
15 | GitHub 官方表示,从 2020 年 10 月 1 日起,在该平台上创建的所有新的源代码仓库将默认被命名为 "main",而不是原先的"master"。值得注意的是,现有的存储库不会受到此更改影响。
16 |
17 | 也就是现在从 github 初始化的项目都是 main 分支,然而在此之前安装的 git 默认分支为 master,本地使用 git 创建项目都是 master,通过如下命令可更改默认分支的名字
18 |
19 | ## 命令
20 |
21 | - 修改默认分支为 `main` 分支
22 |
23 | ```
24 | git config --global init.defaultBranch main
25 | ```
26 |
27 | - 修改当前项目的分支为 `main`
28 |
29 | ```
30 | git branch -M main
31 | ```
32 |
33 | 要更改为其他名字 只需把 main 替换即可
34 |
35 | ## 要求
36 |
37 | Git 版本为 **v2.28** 或更高 查看版本 `git --version`
38 |
39 | ## 其他
40 |
41 | #### 禁止忽略大小写
42 |
43 | ```
44 | git config core.ignorecase false
45 | ```
46 |
--------------------------------------------------------------------------------
/docs/skill/introduction.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: introduction
3 | slug: /skill
4 | title: 技术笔记简介
5 | keywords:
6 | - 前端
7 | - Vue
8 | - React
9 | - JavaScript
10 | - HTTP
11 | ---
12 |
13 | 本页面为个人学习中所涉及相关技术栈的笔记汇总,包含但不限于
14 |
15 | - Web
16 | - 前端
17 | - Vue
18 | - JavaScript(TypeScript)
19 | - HTTP
20 |
21 | **做到即查即用,能复制粘贴解决的,就绝不百度。**
22 |
--------------------------------------------------------------------------------
/docs/skill/js/JS变量提升.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: js-variable-promotion
3 | slug: /js-variable-promotion
4 | title: js变量提升
5 | date: 2021-7-21
6 | authors: 東雲研究所
7 | tags: [js, es6]
8 | keywords: [js, es6]
9 | ---
10 |
11 | # JavaScript 变量提升
12 |
13 | 在`JavaScript`中变量声明与函数声明都会被提升到作用域顶部,优先级依次为: 函数声明 变量声明 变量赋值。
14 |
15 | ## 变量提升
16 |
17 | ### var 的变量提升
18 |
19 | ```javascript
20 | console.log(a) // undefined
21 | var a = 1
22 | console.log(a) // 1
23 | // console.log(b); // ReferenceError: b is not defined
24 | ```
25 |
26 | 为了显示`a`与`b`的区别,打印了一下未声明的变量`b`,其抛出了一个`ReferenceError`异常,而`a`并未抛出异常,其实对`a`的定义并赋值类似于以下的操作,将`a`的声明提升到作用域最顶端,然后再执行赋值操作。
27 |
28 | ```javascript
29 | var a
30 | console.log(a) // undefined
31 | a = 1
32 | console.log(a) // 1
33 | ```
34 |
35 | ### let/const 的变量提升
36 |
37 | `let`与`const`都具有块级作用域,对于变量提升的相关问题的表现是相同的。实际上关于这个问题目前有所分歧,但在`ES6`的文档中出现了`var/let hoisting`字样,也就是说官方文档说明`let`与`var`一样,都存在变量提升,我觉得能够接受的说法是:
38 |
39 | - `let`的「创建」过程被提升了,但是初始化没有提升。
40 | - `var`的「创建」和「初始化」都被提升了。
41 | - `function`的「创建」「初始化」和「赋值」都被提升了。
42 |
43 | `JS`中无论哪种形式声明`var`、`let`、`const`、`function`、`function*`、`class`都会存在提升现象,不同的是`var`,`function`,`function*`的声明会在提升时进行初始化赋值为`undefined`,因此访问这些变量的时候,不会报`ReferenceError`异常,而使用`let`,`const`,`class`声明的变量,被提升后不会被初始化,这些变量所处的状态被称为`temporal dead zone`,此时如果访问这些变量会抛出`ReferenceError`异常,看上去就像没被提升一样。关于这个问题的讨论,可以参考下边的链接,尤其是在`stackoverflow`中的回答。
44 |
45 | ```
46 | https://www.jianshu.com/p/0f49c88cf169
47 | https://blog.bitsrc.io/hoisting-in-modern-javascript-let-const-and-var-b290405adfda
48 | https://stackoverflow.com/questions/31219420/are-variables-declared-with-let-or-const-not-hoisted-in-es6
49 | ```
50 |
51 | ## 函数声明提升
52 |
53 | 函数声明会将声明与赋值都提前,也就是整个函数体都会被提升到作用域顶部。
54 |
55 | ```javascript
56 | s() // 1
57 | function s() {
58 | console.log(1)
59 | }
60 | ```
61 |
62 | 函数表达式只会提升变量的声明,本质上是变量提升并将一个匿名函数对象赋值给变量。
63 |
64 | ```javascript
65 | console.log(s) // undefined
66 | var s = function s() {
67 | console.log(1)
68 | }
69 | console.log(s) // f s(){console.log(1);}
70 | ```
71 |
72 | 由此来看,直接进行函数声明与函数表达式声明的函数之间就存在一个优先级关系。
73 |
74 | ```javascript
75 | var s = function () {
76 | console.log(0)
77 | }
78 | function s() {
79 | console.log(1)
80 | }
81 | s() // 0
82 | ```
83 |
84 | ## 优先级
85 |
86 | 在`JS`中函数是第一等公民,在《你不知道的`JavaScript`》(上卷)一书的第`40`页中写到,函数会首先被提升,然后才是变量。也就是说,同一作用域下提升,函数会在更前面。即在`JS`引擎的执行的优先级是函数声明、变量声明、变量赋值。
87 |
88 | ```javascript
89 | function s() {
90 | //函数声明
91 | console.log(1)
92 | }
93 |
94 | var s // 变量声明
95 |
96 | // 函数已声明`a` 相同的变量名`var`声明会被直接忽略
97 | console.log(s) // f s(){...}
98 |
99 | s = function () {
100 | // 变量赋值
101 | console.log(0)
102 | }
103 |
104 | s() // 0
105 | ```
106 |
--------------------------------------------------------------------------------
/docs/skill/js/JS如何获取当天零点时间戳.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: how-does-js-get-today-zero-timestamp
3 | slug: /how-does-js-get-today-zero-timestamp
4 | title: JS如何获取当天零点时间戳
5 | date: 2021-08-18
6 | authors: 東雲研究所
7 | tags: [javascript]
8 | keywords: [javascript]
9 | ---
10 |
11 |
12 |
13 | ## 需求
14 |
15 | 准备做一个签到系统,所以当天的 0 点就成为了判断是否签到过的关键点,那 Js 又如何获取对应的时间戳呢?
16 |
17 | ## 实现
18 |
19 | 我一开始是这么实现的,利用到的 js 时间库,moment 或者 dayjs 都行,这里选择 dayjs(因为轻量)。
20 |
21 | 代码如下
22 |
23 | ```js
24 | dayjs(dayjs().format('YYYY-MM-DD')).valueOf()
25 | ```
26 |
27 | moment 的话,只需要将 dayjs 替换成 moment 即可。
28 |
29 | 中间部分取出来的时间为 `“2021-08-18”`,然后再通过 dayjs 转为 Dayjs 对象,并通过 valueOf(),就可获取到当天的零点的时间戳。
30 |
31 | 思路很明确,就是要先获取到当前日期,然后通过日期在转为时间戳即可
32 |
33 | 对应的原生 Js 代码也就很明显了
34 |
35 | ```js
36 | new Date(new Date().toLocaleDateString()).getTime()
37 | ```
38 |
39 | 但要我选择我依旧毫不犹豫选择使用 js 时间库,一些复杂的时间计算,如时间格式化,计算两者时间秒/天数差,给指定时间增加/减少天数,这些如果使用原生 Js 代码,不如直接使用已有的库,何必造个轮子呢。
40 |
41 | 有关 dayjs 的具体使用就不做介绍了,贴个官方文档,要用的时候查阅一下便可。
42 |
43 | [Day.js · 中文文档 - 2kB 大小的 JavaScript 时间日期库](https://day.js.org/zh-CN/)
44 |
--------------------------------------------------------------------------------
/docs/skill/js/JS实现函数缓存.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: js-implement-function-cache
3 | slug: /js-implement-function-cache
4 | title: JS实现函数缓存
5 | date: 2021-11-22
6 | authors: 東雲研究所
7 | tags: [javascript]
8 | keywords: [javascript]
9 | ---
10 |
11 |
12 |
13 | ## 原理
14 |
15 | - 闭包
16 | - 柯里化
17 |
18 | - 高阶函数
19 |
20 | ## 例子:求和
21 |
22 | 正常的循环累加代码
23 |
24 | ```javascript
25 | function add() {
26 | let sum = 0
27 | for (let i = 0; i < arguments.length; i++) {
28 | sum += arguments[i]
29 | }
30 | return sum
31 | }
32 | ```
33 |
34 | 使用数组的 reduce 方法
35 |
36 | ```javascript
37 | function add() {
38 | var arr = Array.prototype.slice.call(arguments)
39 | return arr.reduce(function (prev, cur) {
40 | return prev + cur
41 | }, 0)
42 | }
43 | ```
44 |
45 | 但多次传入同样的参数 如 `add(1, 2, 3)` 都将执行运算对应的次数,将会耗费一定的性能。
46 |
47 | ### 使用函数缓存
48 |
49 | 使用闭包,将每次运算的参数与结果存入置 cache 对象中,如果 cache 中有,便直接获取,来达到缓存的目的
50 |
51 | ```javascript
52 | let add = (function () {
53 | let cache = {}
54 |
55 | return function () {
56 | let args = Array.prototype.join.call(arguments, ',')
57 | if (cache[args]) {
58 | return cache[args]
59 | }
60 | let sum = 0
61 | for (let i = 0; i < arguments.length; i++) {
62 | sum += arguments[i]
63 | }
64 | return (cache[args] = sum)
65 | }
66 | })()
67 |
68 | add(1, 2, 3) // 输出6
69 | add(1, 2, 3) // 直接从cache中获取
70 | ```
71 |
72 | 已经达到缓存的目的了,但这时我想将乘法也想实现缓存的目的,那么又得写一大行这样的代码,同时原本求和的代码又想单独分离出来,就可以使用代理模式,具体演示如下
73 |
74 | ### 代理模式
75 |
76 | #### 创建缓存代理的工厂
77 |
78 | ```javascript
79 | let memoize = function (fn) {
80 | let cache = {}
81 | return function () {
82 | let args = Array.prototype.join.call(arguments, ',')
83 | if (args in cache) {
84 | return cache[args]
85 | }
86 | return (cache[args] = fn.apply(this.arguments))
87 | }
88 | }
89 | ```
90 |
91 | 那么通过`memoize` 就能将函数运行后的结果给缓存起来,如
92 |
93 | ```javascript
94 | let add1 = memoize(add)
95 |
96 | add1(1, 2, 3) // 输出6
97 | add1(1, 2, 3) // 直接从cache中获取
98 | ```
99 |
100 | 我们只需要编写我们正常的业务逻辑(加法,乘法等),然后通过 memoize 调用 便可达到缓存的目的
101 |
102 | 同理乘法
103 |
104 | ```javascript
105 | function mult() {
106 | let a = 0
107 | for (let i = 0; i < arguments.length; i++) {
108 | a *= arguments[i]
109 | }
110 | return a
111 | }
112 |
113 | let mult1 = memoize(mult)
114 |
115 | mult1(1, 2, 3) // 输出6
116 | mult1(1, 2, 3) // 直接从cache中获取
117 | ```
118 |
--------------------------------------------------------------------------------
/docs/skill/js/JS数组对象去重.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: js-array-object-unique
3 | slug: /js-array-object-unique
4 | title: JS数组对象去重
5 | date: 2021-07-05
6 | authors: 東雲研究所
7 | tags: [javascript]
8 | keywords: [javascript]
9 | ---
10 |
11 |
12 |
13 | 参考 [数组对象去重](https://www.nodejs.red/#/javascript/base?id=数组去重的三种实现方式)
14 |
15 | 数据如下:
16 |
17 | ```js
18 | [{ name: 'zs', age: 15 }, { name: 'lisi' }, { name: 'zs' }]
19 | ```
20 |
21 | 想要将 name 为 zs 的数据去重,优先保留第一条相同数据
22 |
23 | ## 解决方法
24 |
25 | ### reduce 去重
26 |
27 | ```js
28 | let hash = {}
29 |
30 | function unique(arr, initialValue) {
31 | return arr.reduce(function (previousValue, currentValue, index, array) {
32 | hash[currentValue.name] ? '' : (hash[currentValue.name] = true && previousValue.push(currentValue))
33 |
34 | return previousValue
35 | }, initialValue)
36 | }
37 |
38 | const uniqueArr = unique([{ name: 'zs', age: 15 }, { name: 'lisi' }, { name: 'zs' }], [])
39 |
40 | console.log(uniqueArr) // uniqueArr.length == 2
41 | ```
42 |
43 | ### lodash 工具库去重
44 |
45 | [Lodash Documentation](https://lodash.com/docs/4.17.15#uniqBy)
46 |
47 | ```js
48 | _.uniqBy([{ x: 1 }, { x: 2 }, { x: 1 }], 'x')
49 |
50 | // => [{ 'x': 1 }, { 'x': 2 }]
51 |
52 | // 指定条件
53 | _.uniqBy([2.1, 1.2, 2.3], Math.floor)
54 | // => [2.1, 1.2]
55 | ```
56 |
57 | 想要所有对象属性都一样才去重也简单
58 |
59 | ```js
60 | var objects = [
61 | { x: 1, y: 2 },
62 | { x: 2, y: 1 },
63 | { x: 1, y: 2 },
64 | ]
65 |
66 | _.uniqWith(objects, _.isEqual)
67 | // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
68 | ```
69 |
--------------------------------------------------------------------------------
/docs/skill/node/axios请求gbk页面乱码解决.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: axios-request-gbk-page-encoding-solution
3 | slug: /axios-request-gbk-page-encoding-solution
4 | title: axios请求gbk页面乱码解决
5 | date: 2021-09-19
6 | authors: 東雲研究所
7 | tags: [node, axios, encode]
8 | keywords: [node, axios, encode]
9 | ---
10 |
11 |
12 |
13 | 使用 axios 请求 gbk 编码的网站,将会出现乱码,原因很简单,node 默认字符编码为 utf8,如果要正常显示 gbk 数据的话就需要将 gbk 转 utf8 格式。
14 |
15 | ## 解决办法
16 |
17 | 借助`iconv-lite`,不让 axios 自动处理响应数据,添加`responseType`和`transformResponse`参数,演示代码如下
18 |
19 | ```js
20 | import axios from 'axios'
21 | import * as iconv from 'iconv-lite'
22 |
23 | axios
24 | .get(`https://www.ip138.com/`, {
25 | responseType: 'arraybuffer',
26 | transformResponse: [
27 | function (data) {
28 | return iconv.decode(data, 'gbk')
29 | },
30 | ],
31 | })
32 | .then((res) => {
33 | console.log(res.data)
34 | })
35 | ```
36 |
37 | 或者不使用`transformResponse`,在响应结束后使用`iconv.decode(res.data, 'gbk')`,使用`transformResponse`相对优雅一点。
38 |
39 | 如果返回的是 json 格式的话,可以直接`JSON.parse`转为 json 对象(前提得确保是 json 格式,不然解析报错)
40 |
41 | ```js
42 | return JSON.parse(iconv.decode(data, 'gbk'))
43 | ```
44 |
--------------------------------------------------------------------------------
/docs/skill/node/npm镜像配置.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: npm-mirror-config
3 | slug: /npm-mirror-config
4 | title: npm镜像配置
5 | date: 2022-03-17
6 | authors: 東雲研究所
7 | tags: [node, npm, electron]
8 | keywords: [node, npm, electron]
9 | ---
10 |
11 |
12 |
13 | 由于原淘宝 npm 域名(**http://npm.taobao.org 和 http://registry.npm.taobao.org**)将于 **2022.06.30 号正式下线和停止 DNS 解析**,不妨提前修改镜像的地址,以免受到影响。
14 |
15 | 域名切换规则:
16 |
17 | - http://npm.taobao.org => http://npmmirror.com
18 | - http://registry.npm.taobao.org => http://registry.npmmirror.com
19 |
20 | 同时不推荐使用镜像下载依赖,因为有可能会导致与官方包不同步(亲测,就因为下载依赖折腾了一晚上,还以为是电脑问题),但有时候开启科学上网(或者没有),下载也不见得特别快,所以这时候才会使用国内镜像。
21 |
22 | ## 镜像站点
23 |
24 | [npmmirror 中国镜像站](https://www.npmmirror.com/)
25 |
26 | http://registry.npmjs.org
27 |
28 | ## 单次使用镜像
29 |
30 | ```sh
31 | npm install [name] --registry=https://registry.npmmirror.com
32 | ```
33 |
34 | ## 永久配置镜像
35 |
36 | ```sh
37 | npm config set registry https://registry.npmmirror.com
38 | ```
39 |
40 | ## 查看镜像
41 |
42 | ```
43 | npm get registry
44 | ```
45 |
46 | ## nrm镜像管理工具
47 |
48 | ```
49 | npm install nrm -g
50 | ```
51 |
52 | ### nrm ls 查看所有镜像
53 |
54 | ```
55 | npm ---------- https://registry.npmjs.org/
56 | yarn --------- https://registry.yarnpkg.com/
57 | tencent ------ https://mirrors.cloud.tencent.com/npm/
58 | cnpm --------- https://r.cnpmjs.org/
59 | taobao ------- https://registry.npmmirror.com/
60 | npmMirror ---- https://skimdb.npmjs.com/registry/
61 | ```
62 |
63 | ### nrm use 镜像 切换镜像
64 |
65 | ```
66 | nrm use taobao
67 | ```
68 |
69 | ## 清除 npm 缓存
70 |
71 | ```sh
72 | npm cache clean --force
73 | ```
74 |
75 | ## 配置 electron 镜像
76 |
77 | ```sh
78 | npm config set ELECTRON_MIRROR https://npmmirror.com/mirrors/electron/
79 |
80 | npm config set ELECTRON_BUILDER_BINARIES_MIRROR https://npmmirror.com/mirrors/electron-builder-binaries/
81 | ```
82 |
--------------------------------------------------------------------------------
/docs/skill/node/使用 require.context 实现模块自动导入.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: use-require.context-to-auto-import-modules
3 | slug: /use-require.context-to-auto-import-modules
4 | title: 使用 require.context 实现模块自动导入
5 | date: 2021-09-12
6 | authors: 東雲研究所
7 | tags: [node, webpack]
8 | keywords: [node, webpack]
9 | ---
10 |
11 |
12 |
13 | ## 前言
14 |
15 | 在写资源导航的时候,我在将资源分类为一个文件的时候,发现如果我每定义一个分类,那我就需要创建一个文件,然后又要通过`import form`导入,就很烦躁。
16 |
17 | 突然想到貌似 vue-element-admin 中的路由好像也是这样的,而 store 貌似定义完就无需再次导入,于是就开始研究代码,果不其然,发现了`require.context`
18 |
19 | [依赖管理 | webpack 中文文档 (docschina.org)](https://webpack.docschina.org/guides/dependency-management/)
20 |
21 | ## 实现
22 |
23 | require.context:是一个 webpack 提供的 api,通过执行 require.context 函数遍历获取到指定文件夹(及其下子文件夹)内的指定文件,然后自动导入。
24 |
25 | 语法:`require.context(directory, useSubdirectories = false, regExp = /^.//)`
26 |
27 | - directory 指定文件
28 | - useSubdirectories 是否遍历目录的子目录
29 | - regExp 匹配文件的正则表达式,即文件类型
30 |
31 | 而上图代码中对应的代码也明确表达要指定`./modules`目录下的,所有 js 文件
32 |
33 | ```js
34 | const modulesFiles = require.context('./modules', true, /\.js$/)
35 | ```
36 |
37 | 输出一下看看 modulesFiles 到底是什么(console.dir 输出)
38 |
39 | 返回一个函数,但该函数包含三个属性 resolve()、keys()、id
40 |
41 | 其中`modulesFiles.keys()`则是指定目录下文件名数组
42 |
43 | ```
44 | ['./app.js', './permission.js','./settings.js', './tagsView.js', './user.js']
45 | ```
46 |
47 | 接着看下 vue-element-admin 中的下一行代码
48 |
49 | ```js
50 | const modules = modulesFiles.keys().reduce((modules, modulePath) => {
51 | // set './app.js' => 'app'
52 | const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
53 | const value = modulesFiles(modulePath)
54 | modules[moduleName] = value.default
55 | return modules
56 | }, {})
57 | ```
58 |
59 | 这边先输出一下 modules,看下结果是什么
60 |
61 | 没错,正对应着 modules 下的所有文件,以及所导出的对象
62 |
63 | 其中在循环体中还调用了`const value = modulesFiles(modulePath)`,其中 value 是 Module 对象,有个属性`default`,通过`value.default`便可获取到对应模块所导出的内容。
64 |
65 | 就此便可以实现自动导入模块。不过由于导出的是 store 对象,所封装的代码也有点过于复杂,这边我贴下我是如何自动导入数组对象的
66 |
67 | ```typescript
68 | const modulesFiles = require.context('./modules', true, /\.ts$/)
69 |
70 | let allData: any[] = []
71 |
72 | modulesFiles.keys().forEach(modulePath => {
73 | const value = modulesFiles(modulePath)
74 | let data = value.default
75 |
76 | if (!data) return
77 | allData.push(...value.default)
78 | })
79 | ```
80 |
81 | ## 参考链接
82 |
83 | > [前端优化之 -- 使用 require.context 让项目实现路由自动导入 - 沐浴点阳光 - 博客园 (cnblogs.com)](https://www.cnblogs.com/garfieldzhong/p/12585280.html)
84 |
--------------------------------------------------------------------------------
/docs/skill/vue/$router和$route的区别.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: $router-and-$route
3 | slug: /$router-and-$route
4 | title: $router和$route 的区别
5 | date: 2021-8-13
6 | authors: 東雲研究所
7 | tags: [vue, router]
8 | keywords: [vue, router]
9 | ---
10 |
11 | # $router和$route 的区别
12 |
13 | `Vue Router`是`Vue.js`的路由管理器,路由就是`SPA`单页应用的访问路径,在`Vue`实例内部,可以通过`$router`访问路由实例,即在路由定义文件中`export default`的`new Router(/*...*/)`路由实例,通过`$route`可以访问当前激活的路由的状态信息,包含了当前`URL`解析得到的信息,还有`URL`匹配到的路由记录,可以将`$router`理解为一个容器去管理了一组`$route`,而`$route`是进行了当前`URL`和组件的映射。
14 |
15 | ## $router 对象属性
16 |
17 | - `$router.app`: 配置了`router`的`Vue`根实例。
18 | - `$router.mode`: 路由使用的模式。
19 | - `$router.currentRoute`: 当前路由对应的路由信息对象。
20 |
21 | ## $router 对象方法
22 |
23 | - `$router.beforeEach(to, from, next)`: 全局前置守卫,守卫是异步解析执行,此时导航在所有守卫`resolve`完之前一直处于等待中状态,守卫方法接收三个参数: `to: Route`即将要进入的目标路由对象、`from: Route`: 当前导航正要离开的路由、`next: Function`: 调用该方法来`resolve`这个钩子,执行效果依赖`next`方法的调用参数,例如`next()`、`next(false)`、`next('/')`、`next({path:'/',name:'home',replace:true,query:{q:1}})`、`next(error)`等,通常在`main.js`中`import`的`Vue Router`实例中直接定义导航守卫,当然也可在`Vue`实例中访问`$router`来定义。
24 | - `$router.beforeResolve(to, from, next)`: 全局解析守卫,在`beforeRouteEnter`调用之后调用,同样接收`to`、`from`、`next`三个参数。
25 | - `$router.afterEach(to, from)`: 全局后置钩子,进入路由之后调用,接收`to`、`from`两个参数。
26 | - `$router.push(location[, onComplete[, onAbort]])`: 编程式导航,使用`$router.push`方法导航到不同的`URL`,此方法会向`history`栈添加一个新的记录,当点击浏览器后退按钮时,则回到之前的`URL`。
27 | - `$router.replace(location[, onComplete[, onAbort]])`: 编程式导航,跟`$router.push`很像,唯一的不同就是,其不会向`history`添加新记录,而是跟它的方法名一样替换掉当前的`history`记录。
28 | - `$router.go(n)`: 编程式导航,这个方法的参数是一个整数,意思是在`history`记录中向前或者后退多少步,类似`window.history.go(n)`。
29 | - `$router.back()`: 编程式导航,后退一步记录,等同于`$router.go(-1)`。
30 | - `$history.forward()`: 编程式导航,前进一步记录,等同于`$router.go(1)`。
31 | - `$router.getMatchedComponents([location])`: 返回目标位置或是当前路由匹配的组件数组 ,是数组的定义或构造类,不是实例,通常在服务端渲染的数据预加载时使用。
32 | - `$router.resolve(location[, current[, append]])`: 解析目标位置,格式和``的`to prop`相同,`current`是当前默认的路由,`append`允许在`current`路由上附加路径,如同 `router-link`。
33 | - `$router.addRoutes(route)`: 动态添加更多的路由规则,参数必须是一个符合`routes`选项要求的数组。
34 | - `$router.onReady(callback[, errorCallback])`: 该方法把一个回调排队,在路由完成初始导航时调用,这意味着它可以解析所有的异步进入钩子和路由初始化相关联的异步组件,这可以有效确保服务端渲染时服务端和客户端输出的一致,第二个参数`errorCallback`会在初始化路由解析运行出错时被调用。
35 | - `$router.onError(callback)`: 注册一个回调,该回调会在路由导航过程中出错时被调用,被调用的错误必须是下列情形中的一种,错误在一个路由守卫函数中被同步抛出、错误在一个路由守卫函数中通过调用`next(err)`的方式异步捕获并处理、渲染一个路由的过程中需要尝试解析一个异步组件时发生错误。
36 |
37 | ## $route 对象属性
38 |
39 | - `$route.path`: 返回字符串,对应当前路由的路径,总是解析为绝对路径。
40 | - `$route.params`: 返回一个`key-value`对象,包含了动态片段和全匹配片段,如果没有路由参数,就是一个空对象。
41 | - `$route.query`: 返回一个`key-value`对象,表示`URL`查询参数。
42 | - `$route.hash`: 返回当前路由的带`#`的`hash`值,如果没有`hash`值,则为空字符串。
43 | - `$route.fullPath`: 返回完成解析后的`URL`,包含查询参数和`hash`的完整路径。
44 | - `$route.matched`: 返回一个数组,包含当前路由的所有嵌套路径片段的路由记录,路由记录就是`routes`配置数组中的对象副本。
45 | - `$route.name`: 如果存在当前路由名称则返回当前路由的名称。
46 | - `$route.redirectedFrom`: 如果存在重定向,即为重定向来源的路由的名字。
47 |
--------------------------------------------------------------------------------
/docs/skill/vue/customRef实现敏感词替换.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: customRef
3 | slug: /customRef
4 | title: customRef实现敏感词替换
5 | date: 2022-5-13
6 | authors: 東雲研究所
7 | tags: [vue, customRef]
8 | keywords: [vue, customRef]
9 | ---
10 |
11 | customRef可以帮助在输入内容时遇到敏感词汇,做一个*的替换。
12 |
13 | ## 思路
14 |
15 | 1.字符串的替换 str.replace(, 'X')
16 | 2.维护白名单列表 const list = ["sb","SB"]
17 | 3.在输入的过程中需要不断监听输入值是否触发机制,如果触发则替换 customRef
18 |
19 | ## 实现
20 |
21 | ```vue title="demo.vue"
22 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | ```
58 |
59 | 当然这是很简单的功能用 computed 之类的都行,只是因为 customRef 是个低频的 api,这里写个笔记,而且customRef更容易抽离出来做复用
60 |
--------------------------------------------------------------------------------
/docs/skill/vue/v-if与v-show的区别.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: vif-and-vshow
3 | slug: /vif-and-vshow
4 | title: v-if 与 v-show 的区别
5 | date: 2021-8-23
6 | authors: 東雲研究所
7 | tags: [vue, v-if, v-show]
8 | keywords: [vue, v-if, v-show]
9 | ---
10 |
11 | # v-if 与 v-show 的区别
12 |
13 | `v-if`指令与`v-show`指令都可以根据值动态控制`DOM`元素显示隐藏,`v-if`和`v-show`属于`Vue`的内部常用的指令,指令的职责是当表达式的值改变时把某些特殊的行为应用到`DOM`上。
14 |
15 | ## 描述
16 |
17 | ### v-if
18 |
19 | `v-if`指令用于条件性地渲染一块内容,这块内容只会在指令的表达式返回`truthy`值的时候被渲染。
20 |
21 | ```html
22 | show
23 | hide
24 | ```
25 |
26 | ### v-show
27 |
28 | `v-show`指令用法大致一样,不同的是带有`v-show`指令的元素始终会被渲染并保留在`DOM`中,`v-show`只是简单地切换元素的`CSS property display`。
29 |
30 | ```html
31 | show
32 | ```
33 |
34 | ## 区别
35 |
36 | - 实现方式: `v-if`是动态的向`DOM`树内添加或者删除`DOM`元素,`v-show`是通过设置`DOM`元素的`display`样式属性控制显隐。
37 | - 编译过程: `v-if`切换有一个局部编译卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件,`v-show`只是简单的基于`CSS`切换。
38 | - 编译条件: `v-if`是惰性的,如果初始条件为假,则什么也不做,只有在条件第一次变为真时才开始局部编译, `v-show`是在任何条件下都被编译,然后被缓存,而且`DOM`元素保留。
39 | - 性能消耗: `v-if`有更高的切换消耗,`v-show`有更高的初始渲染消耗。
40 | - 使用场景: `v-if`适合条件不太可能改变的情况,`v-show`适合条件频繁切换的情况。
41 |
--------------------------------------------------------------------------------
/docs/skill/vue/使用ShallowRef对接Redux到Vue.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: shallowRef
3 | slug: /shallowRef
4 | title: 使用ShallowRef对接Redux到Vue
5 | date: 2022-11-23
6 | authors: 東雲研究所
7 | tags: [vue, shallowRef, Redux]
8 | keywords: [vue, shallowRef, Redux]
9 | ---
10 |
11 | > 官方文档:(https://cn.vuejs.org/api/reactivity-advanced.html#shallowref)
12 |
13 | shallowRef()是ref() 的浅层作用形式。
14 | ref会不断递归把深层转成响应式,而shallowRef只会对第一层也就是value进行响应式转换。
15 | shallowRef() 常常用于对大型数据结构的性能优化或是与外部的状态管理系统集成。
16 | 将 Vue 的响应性系统与外部状态管理方案集成的大致思路是:将外部状态放在一个 shallowRef 中。一个浅层的 ref 中只有它的 .value 属性本身被访问时才是有响应性的,而不关心它内部的值。当外部状态改变时,替换此 ref 的 .value 才会触发更新。
17 |
18 | ```javascript title="src/store/index.js"
19 | import { shallowRef } from 'vue'
20 |
21 | import { createSlice, configureStore } from '@reduxjs/toolkit'
22 | // 创建counterSlice
23 | export const counterSlice = createSlice({
24 | name: 'counter',
25 | initialState: {
26 | count: 0,
27 | },
28 | reducers: {
29 | increment: state => {
30 | state.count++
31 | },
32 | },
33 | })
34 | // 导出ActionCreater
35 | const { increment } = counterSlice.actions
36 | // 生成store实例
37 | const store = configureStore({
38 | reducer: {
39 | counter: counterSlice.reducer,
40 | },
41 | })
42 |
43 | const refState = shallowRef(store.getState())
44 | // 订阅状态变化
45 | store.subscribe(() => {
46 | console.log('状态改变', store.getState())
47 | // 给refState赋值,进行整体覆盖
48 | refState.value = store.getState()
49 | })
50 | // 导出store实例和actionCreater
51 | export { store, increment, refState }
52 | ```
53 |
54 | redux 这边的工作就完成了,因为我平时也不用 redux,如果有问题可以查询官网
55 |
56 | ```Vue title="app.vue"
57 |
60 |
61 |
62 |
63 |
64 |
65 | ```
66 |
--------------------------------------------------------------------------------
/docs/skill/vue/图片懒加载指令.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: v-lazyload
3 | slug: /v-lazyload
4 | title: 图片懒加载指令
5 | date: 2022-6-27
6 | authors: 東雲研究所
7 | tags: [vue, lazyload]
8 | keywords: [vue, lazyload]
9 | ---
10 |
11 | 图片懒加载可以一定程度上减少网络请求的发送,算是一种网络请求的优化,我们今天来实现图片懒加载的vue指令
12 |
13 | ## 思路
14 |
15 | 1.判断图片是否进入视口区域 只有进入视口区域才发送请求
16 | 查询是否进入视口区域有两种办法 一种是传统的判断距离 还有一种是使用VueUse提供的方法简便又准确
17 | 2.img.src = url
18 |
19 | ## 实现
20 |
21 | ```javascript title="imgLazyload.js"
22 | import { useIntersectionObserver } from '@vueuse/core'
23 |
24 | export default {
25 | mounted(el, binding) {
26 | // el:指令挂载到的元素
27 | // binding可以拿到指令表达式的值 在这里就是图片URL V-imgLazyload = "url"
28 | const { stop } = useIntersectionObserver(
29 | el,
30 | ([{ isIntersecting }], observerElement) => {
31 | if (isIntersecting) {
32 | el.src = binding.value
33 | // 加载图片之后停止监听
34 | stop()
35 | }
36 | },
37 | )
38 | },
39 | }
40 | ```
41 |
42 | 但是实际使用中存在 root margin 无法生效的问题。看 ElementPlus 的源码发现了一种更通用的懒加载实现方案:判断图片是否进入最近的滚动容器的视口,在判断是否进入的时候可以设置 margin。
43 | 所以第二种不使用useIntersectionObserver
44 |
45 | ```js title="imgLazyload.js"
46 | import { useThrottleFn, useEventListener } from '@vueuse/core'
47 |
48 | function isScroll(el) {
49 | let overflow = el.style['overflow']
50 |
51 | if (!overflow) {
52 | overflow = document.defaultView.getComputedStyle(el)['overflow']
53 | }
54 |
55 | return ['auto', 'scroll', 'overlay'].some(v => v === overflow)
56 | }
57 |
58 | function getScrollContainer(el) {
59 | let parent = el
60 |
61 | while (parent) {
62 | if ([window, document, document.documentElement].includes(parent)) {
63 | return window
64 | }
65 |
66 | if (isScroll(parent)) {
67 | return parent
68 | }
69 |
70 | parent = parent.parentElement
71 | }
72 |
73 | return parent
74 | }
75 |
76 | function isInContainer(el, container, marginTop = 0) {
77 | let rect
78 | const { top, left, right, bottom } = el.getBoundingClientRect()
79 | if (container instanceof Element) {
80 | rect = container.getBoundingClientRect()
81 | } else {
82 | rect = {
83 | top: 0,
84 | left: 0,
85 | bottom: window.innerHeight,
86 | right: window.innerWidth,
87 | }
88 | }
89 |
90 | return (
91 | top - marginTop < rect.bottom &&
92 | bottom > rect.top &&
93 | left < rect.right &&
94 | right > rect.left
95 | )
96 | }
97 |
98 | export default {
99 | mounted(el, bindings) {
100 | const { top, src } = bindings.value
101 |
102 | const container = getScrollContainer(el)
103 |
104 | const loadImageHandler = () => {
105 | if (isInContainer(el, container, parseInt(top) || 0)) {
106 | el.setAttribute('src', src)
107 | stopListener()
108 | }
109 | }
110 |
111 | const stopListener = useEventListener(
112 | container,
113 | 'scroll',
114 | useThrottleFn(loadImageHandler),
115 | )
116 |
117 | setTimeout(loadImageHandler, 50)
118 | },
119 | }
120 | ```
121 |
--------------------------------------------------------------------------------
/docs/skill/vue/异步组件实现按需加载.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: defineAsyncComponent
3 | slug: /defineAsyncComponent
4 | title: 异步组件实现按需加载
5 | date: 2022-7-2
6 | authors: 東雲研究所
7 | tags: [vue, defineAsyncComponent]
8 | keywords: [vue, defineAsyncComponent]
9 | ---
10 |
11 | 有这么一个需求:下面的组件在页面初始化时不渲染,而是当用户正式浏览到的时候才进行渲染,我们可以使用defineAsyncComponent这个api。
12 |
13 | ## 实现
14 |
15 | ```vue title="App.vue"
16 |
40 |
41 |
42 |
43 |

44 |
45 |
48 |
49 | ```
50 |
--------------------------------------------------------------------------------
/docs/skill/web/跨域问题.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: cross-domain
3 | slug: /cross-domain
4 | title: 跨域问题
5 | date: 2021-10-07
6 | authors: 東雲研究所
7 | tags: [http, browser]
8 | keywords: [http, browser]
9 | ---
10 |
11 | # 跨域
12 |
13 | 制定`HTML`规则时,出于安全的考虑,一个源的网站不允许与另一个源的资源进行交互,浏览器制定此规则为**同源策略**。
14 |
15 | 同源即指的网站具有相同的域,即 **协议(protocol)、主机(host)、端口号(port)** 相同。
16 |
17 | 跨域资源嵌入是允许的,但是浏览器限制了`Javascript`不能与加载的内容进行交互,如嵌入的`')
38 | obj.attr('src', url)
39 | $('body').append(obj) // 动态地添加一个script
40 | ```
41 |
42 | ```php
43 | // 后端配合实现
44 | $data = ["a" => 1, "b" => 2];
45 | $callback = $_GET['callback'];
46 | return $callback."(".json_encode($data).")";
47 | ```
48 |
49 | ### CORS 跨域
50 |
51 | 对于简单请求,浏览器会直接发送`CORS`请求,具体说来就是在`header`中加入`origin`请求头字段。同样,在响应头中,返回服务器设置的相关`CORS`头部字段,`Access-Control-Allow-Origin`字段为允许跨域请求的源。请求时浏览器在请求头的`Origin`中说明请求的源,服务器收到后发现允许该源跨域请求,则会成功返回。
52 | 对于非简单请求,浏览器会自动先发送一个`options`请求,如果发现服务器支持该请求,则会将真正的请求发送到后端,反之,如果浏览器发现服务端并不支持该请求,则会在控制台抛出错误。
53 |
54 | ```php
55 | //响应头 Response Headers
56 | header('Content-Type: text/html;charset=utf-8');
57 | header('Access-Control-Allow-Origin:http://localhost:8080'); // *代表允许任何网址请求
58 | header('Access-Control-Allow-Methods:POST,GET'); // 允许请求的类型
59 | header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
60 | header('Access-Control-Allow-Headers: Content-Type,Origin,Refer'); // 允许自定义请求头的字段
61 | ```
62 |
63 | ### Nginx 代理
64 |
65 | 通过代理的手段,监听同一端口添加不同路径实现不同服务的跨域访问。
66 |
67 | ```
68 | location /test
69 | {
70 | proxy_pass http://127.0.0.1:81;
71 | }
72 | ```
73 |
74 | ### 图片 ping
75 |
76 | 直接新建一个`
`,然后在地址中存放一些简单数据,这种方法只支持`get`请求,且只能单向地向服务器发送请求,在统计广告曝光次数中比较常见,`XSS`攻击也常用其获取`cookie`。
77 |
78 | ```
79 |
80 | ```
81 |
82 | ### 相同主域 document.domain
83 |
84 | 例如对于`www.example.com`与`abc.example.com`,其主域名是一样的。
85 |
86 | ```javascript
87 | document.domain = 'example.com' //相同主域
88 | var ifrWin = document.getElementById('ifr').contentWindow //可以操作iframe
89 | ```
90 |
91 | ### window.name 共享数据
92 |
93 | 不同域的`iframe`把共享的信息放在`window.name`里面,此方法只适用于两个`iframe`之间的跨域。
94 |
95 | ```javascript
96 | window.name = JSON.stringify({ a: 1, b: 2 })
97 | ```
98 |
99 | ### window.postMessage
100 |
101 | 使用`window.postMessage`来向其它的`window`对象发送消息,无论这个`window`对象是属于同源或不同源,这种方法不能和服务端交换数据。
102 |
103 | ```javascript
104 | //主window
105 | window.frames[0].postMessage({ a: 1 }, 'http://127.0.0.1:81')
106 | //iframe //出于安全考虑验证来源
107 | window.addEventListener('message', event => {
108 | if (event.origin === 'http://127.0.0.1') console.log(event.data)
109 | })
110 | ```
111 |
--------------------------------------------------------------------------------
/docs/tools/Everything快速搜索本地文件.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: everything-quick-search-local-files
3 | slug: /everything-quick-search-local-files
4 | title: Everything快速搜索本地文件
5 | date: 2020-09-08
6 | authors: 東雲研究所
7 | tags: [工具]
8 | keywords: [工具]
9 | ---
10 |
11 | 
12 |
13 | 你有没有经历过为了找一个**本地**文件,反复点开一个个文件夹,甚至有可能还找不到,于是开始 Windows 自带的搜索引擎去搜索想要的文件,然后等你吃完饭回来,搜索的进度条竟然还没有走完。而现在有一个软件,它可以让你不必等待,一个眨眼的功夫,你想要的文件就呈现在你的面前,它就是`Everything`
14 |
15 | 软件下载地址 [点我下载](https://www.voidtools.com/zh-cn/) 一路下一步,默认选项即可。
16 |
17 | ## 软件说明
18 |
19 | 可以说`Everything`是速度最快的文件搜索软件,可以瞬间搜索到你所需要的文件,并且页面简洁,体积仅有几兆,可以说你想要的优点都有了。内存占用也不过 200m(我本地文件很多),单丝毫不影响它搜索的速度。
20 |
21 | ## 使用方法
22 |
23 | ### 设置打开快捷键
24 |
25 | 请设置一个快捷键用于快捷的打开`Everything`,有时候你需要搜索一个文件时,不必在找到`Everything`的图标打开,只需要按下所设置的快捷键,这里我设置为`Ctrl+Shift+Alt+Q`,一是避免其他键冲突,二是按键方便。
26 |
27 | 设置方式如下图
28 |
29 | 
30 |
31 | 其余的相关快捷键感兴趣可以自行设置,这里就举我最常用的一个。
32 |
33 | ## 搜索文件
34 |
35 | 在搜索前建议你设置一下排除列表,避免文件过多,根据自己需求来设置排除的文件夹,这里我把我基本用不上的文件夹放在这。
36 |
37 | 
38 |
39 | 这里我先放一个 gif 图,先感受一下
40 |
41 | 
42 |
43 | 我也只是简单的使用一下,实际使用过就知道这个工具有多牛逼,你就会知道我为什么会推荐了。想上面的简单使用已经够用了,还有一些针对性的操作,可以在 帮助->搜索语法 即可看到相关搜索语法帮助。
44 |
45 | 这里我就简单说几个常用的 比如我要打开`pycharm` 这个软件,这时候我会肯定会先输入`pycharm`,但是发现出现了特别多的结果且没有我想要的,于是我尝试补上`.exe`后缀,然后发现竟然没有!怎么可能我明明安装了,其实这个文件名是`pycharm64.exe`你输入`pycharm.exe`肯定搜不到。而遇到这种问题一般都不是这样搜索而是输入 `pycharm exe:` 意思就是我要找` 包含``pycharm `文件名同时又要是`exe`为后缀的。操作情况如下
46 |
47 | 
48 |
49 | 不过一般这种常用软件的话我还是建议你把这个文件设置一个快捷方式放在桌面,开始菜单或者任务栏。
50 |
51 | ### 右键搜索 Everything
52 |
53 | 有时候我只想在指定文件夹下搜索,而不是全局搜索,那么我可以右键点击文件夹空白区域,其中你会看到`搜索 Everything…`的选项,接着会在搜索框为你补上当前的文件夹的路径,然后你在输入你想搜索的文件名即可,这里我就不在赘述和放图了。
54 |
55 | ## 一些注意的坑
56 |
57 | ### 全字匹配
58 |
59 | 有时候我要搜索一个文件,文件名是`demo123.txt`,但是我输入`demo`的时候下面却没有`demo123`,而当我补上`123`的时候`demo123.txt`又出现了的我的面前了。别急,你多半是不小心按下了 Ctrl+B 触发了全字匹配,从而搜索不到文件,你只要在按一下 Ctrl+B 关闭即可。
60 |
61 | 
62 |
63 | ## 总结
64 |
65 | 想必你如果已经安装完,并且试用一番,你可能会忍不住给该作者捐赠。没错,Everything 谁用谁知道,windows 必装之一。
66 |
67 | 但如果你并没有反复打开和搜索文件的需求,而电脑只是用来刷刷剧,玩玩游戏的话,请赶快把这篇文章关掉,不适合你。但如果有,赶快把这篇文章分享给别人吧,也许你的分享能让别人少掉几根头发。
68 |
--------------------------------------------------------------------------------
/docs/tools/Wappalyzer识别网站上的技术.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: wappalyzer-recognize-technology
3 | slug: /wappalyzer-recognize-technology
4 | title: Wappalyzer识别网站上的技术
5 | date: 2021-07-20
6 | authors: 東雲研究所
7 | tags: [chrome, 插件]
8 | keywords: [chrome, 插件]
9 | ---
10 |
11 | 你是否还在为不知道心仪的网站所采用的技术栈而烦恼吗,如果是,那么这款 Chrome 插件值得你拥有
12 |
13 |
14 |
15 | ## 前言
16 |
17 | 作为前端开发者而言,浏览器可以说是我们的栖息地,随着大前端发展,催生出许许多多的技术栈,各式各样的网站迎刃而出。在面对一个悉知而又陌生的网站,想了解所采用的技术栈无不得从利用开发者工具或者分析 js 入手,而有一款 Chrome 插件,能轻松分析出对应网站所采用技术。
18 |
19 | ## 开始
20 |
21 | 正如标题所言,利用它可以大致分析网站所用编程语言,框架,库,Web 服务器等等。
22 |
23 | 这里先贴下官方链接和使用截图
24 |
25 | [Find out what websites are built with - Wappalyzer](https://www.wappalyzer.com/)
26 |
27 | Wappalyzer 官网
28 |
29 | 
30 |
31 | 东云研究所的个人博客
32 |
33 | 
34 |
35 | 我的组件库
36 |
37 | 
38 |
39 | 本人测试过多个网站,分析结果与实际也如图所示,按理来说不会分析错,最多也就是没分析出来,不过效果可以说非常明显了,有时候看到一个心仪的网站,帮助我们去分析了解对应的技术栈,这再好不过了。
40 |
--------------------------------------------------------------------------------
/docs/tools/introduction.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: introduction
3 | slug: /tools
4 | title: 开发工具推荐
5 | ---
6 |
7 | 本页为个人开发中使用到的一些开发工具。
8 |
9 | 同时你也可以到上方的导航栏中的工具中,查看一些本人常用的工具,我都将其部署在自有的服务器上,加快访问速度。
--------------------------------------------------------------------------------
/docs/tools/实用工具PowerToys.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: power-toys
3 | slug: /power-toys
4 | title: PowerToys多功能软件
5 | date: 2022-09-08
6 | authors: 東雲研究所
7 | tags: [工具]
8 | keywords: [工具]
9 | ---
10 |
11 | ## 微软最强工具集
12 |
13 | 你听说过 PowerToys 吗?你用过 PowerToys 吗?你可能从来也没听说过,PowerToys 是微软最初发布于 Windows 95 平台的系统增强工具,2019 年,微软宣布开源 PowerToys 实用工具。
14 |
15 | 在`PowerToys`里集成了多项功能,我最喜欢的功能有颜色选择器、置顶窗口、图像大小调整器等等 ,但只支持 win10、win11
16 |
17 | 今天,就带你深入了解 PowerToys
18 |
19 | ## 下载安装
20 |
21 | 软件网址 [点我进入](https://learn.microsoft.com/zh-cn/windows/powertoys/install)
22 |
23 | 之后可以通过 github,Microsoft Store 或者 Windows 包管理器安装
24 |
25 | 我用的是 github 下载完安装软件一路下一步,默认选项即可就行了
26 |
27 | ## 不同功能与使用方法
28 |
29 | ### alt + space 快速查找软件
30 |
31 | 
32 |
33 | ### win+shift+c 颜色选择器
34 |
35 | 
36 |
37 | ### win+shift+m 屏幕标尺
38 |
39 | 
40 |
41 | ### win+ctrl+t 让软件窗口固定在顶部
42 |
43 |  软件外面套了层灰色的边界就代表被置顶了
44 |
45 | ### hosts 文件编辑器
46 |
47 | 
48 |
49 | ### win+shift+t 文本提取器
50 |
51 | ### powerrename 可以快速的用正则更换文件名
52 |
53 | 除此之外还有很多功能,在下载了 powerToys 之后就可以使用啦
54 |
--------------------------------------------------------------------------------
/docsearch.json:
--------------------------------------------------------------------------------
1 | {
2 | "index_name": "東雲研究所",
3 | "start_urls": ["https://blog.xxsoftware.fun/"],
4 | "sitemap_urls": ["https://blog.xxsoftware.fun/sitemap.xml"],
5 | "selectors": {
6 | "lvl0": {
7 | "selector": "(//ul[contains(@class,'menu__list')]//a[contains(@class, 'menu__link menu__link--sublist menu__link--active')]/text() | //nav[contains(@class, 'navbar')]//a[contains(@class, 'navbar__link--active')]/text())[last()]",
8 | "type": "xpath",
9 | "global": true,
10 | "default_value": "Documentation"
11 | },
12 | "lvl1": "header h1, article h1",
13 | "lvl2": "article h2",
14 | "lvl3": "article h3",
15 | "lvl4": "article h4",
16 | "lvl5": "article h5, article td:first-child",
17 | "lvl6": "article h6",
18 | "text": "article p, article li, article td:last-child"
19 | },
20 | "custom_settings": {
21 | "attributesForFaceting": [
22 | "type",
23 | "lang",
24 | "language",
25 | "version",
26 | "docusaurus_tag"
27 | ],
28 | "attributesToRetrieve": [
29 | "hierarchy",
30 | "content",
31 | "anchor",
32 | "url",
33 | "url_without_anchor",
34 | "type"
35 | ],
36 | "attributesToHighlight": ["hierarchy", "content"],
37 | "attributesToSnippet": ["content:10"],
38 | "camelCaseAttributes": ["hierarchy", "content"],
39 | "searchableAttributes": [
40 | "unordered(hierarchy.lvl0)",
41 | "unordered(hierarchy.lvl1)",
42 | "unordered(hierarchy.lvl2)",
43 | "unordered(hierarchy.lvl3)",
44 | "unordered(hierarchy.lvl4)",
45 | "unordered(hierarchy.lvl5)",
46 | "unordered(hierarchy.lvl6)",
47 | "content"
48 | ],
49 | "distinct": true,
50 | "attributeForDistinct": "url",
51 | "customRanking": [
52 | "desc(weight.pageRank)",
53 | "desc(weight.level)",
54 | "asc(weight.position)"
55 | ],
56 | "ranking": [
57 | "words",
58 | "filters",
59 | "typo",
60 | "attribute",
61 | "proximity",
62 | "exact",
63 | "custom"
64 | ],
65 | "highlightPreTag": "",
66 | "highlightPostTag": "",
67 | "minWordSizefor1Typo": 3,
68 | "minWordSizefor2Typos": 7,
69 | "allowTyposOnNumericTokens": false,
70 | "minProximity": 1,
71 | "ignorePlurals": true,
72 | "advancedSyntax": true,
73 | "attributeCriteriaComputedByMinProximity": true,
74 | "removeWordsIfNoResults": "allOptional",
75 | "separatorsToIndex": "_",
76 | "synonyms": [
77 | ["js", "javascript"],
78 | ["ts", "typescript"]
79 | ]
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/i18n/zh/docusaurus-plugin-content-blog/options.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": {
3 | "message": "Blog",
4 | "description": "The title for the blog used in SEO"
5 | },
6 | "description": {
7 | "message": "Blog",
8 | "description": "The description for the blog used in SEO"
9 | },
10 | "sidebar.title": {
11 | "message": "近期文章",
12 | "description": "The label for the left sidebar"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/i18n/zh/docusaurus-plugin-content-docs/current.json:
--------------------------------------------------------------------------------
1 | {
2 | "version.label": {
3 | "message": "Next",
4 | "description": "The label for version current"
5 | },
6 | "sidebar.skill.category.Vue": {
7 | "message": "Vue",
8 | "description": "The label for category Vue in sidebar skill"
9 | },
10 | "sidebar.skill.category.逆向": {
11 | "message": "逆向",
12 | "description": "The label for category 逆向 in sidebar skill"
13 | },
14 | "sidebar.skill.category.逆向.link.generated-index.title": {
15 | "message": "逆向笔记",
16 | "description": "The generated-index page title for category 逆向 in sidebar skill"
17 | },
18 | "sidebar.skill.category.逆向.link.generated-index.description": {
19 | "message": "Web逆向与安卓逆向笔记",
20 | "description": "The generated-index page description for category 逆向 in sidebar skill"
21 | },
22 | "sidebar.skill.category.安卓": {
23 | "message": "安卓",
24 | "description": "The label for category 安卓 in sidebar skill"
25 | },
26 | "sidebar.skill.category.frida": {
27 | "message": "frida",
28 | "description": "The label for category frida in sidebar skill"
29 | },
30 | "sidebar.skill.category.刷机": {
31 | "message": "刷机",
32 | "description": "The label for category 刷机 in sidebar skill"
33 | },
34 | "sidebar.skill.category.Web": {
35 | "message": "Web",
36 | "description": "The label for category Web in sidebar skill"
37 | },
38 | "sidebar.skill.category.密码学": {
39 | "message": "密码学",
40 | "description": "The label for category 密码学 in sidebar skill"
41 | },
42 | "sidebar.skill.category.后端": {
43 | "message": "后端",
44 | "description": "The label for category 后端 in sidebar skill"
45 | },
46 | "sidebar.skill.category.数据库": {
47 | "message": "数据库",
48 | "description": "The label for category 数据库 in sidebar skill"
49 | },
50 | "sidebar.skill.category.Mysql": {
51 | "message": "Mysql",
52 | "description": "The label for category Mysql in sidebar skill"
53 | },
54 | "sidebar.skill.category.MongoDB": {
55 | "message": "MongoDB",
56 | "description": "The label for category MongoDB in sidebar skill"
57 | },
58 | "sidebar.skill.category.Redis": {
59 | "message": "Redis",
60 | "description": "The label for category Redis in sidebar skill"
61 | },
62 | "sidebar.skill.category.Elasticsearch": {
63 | "message": "Elasticsearch",
64 | "description": "The label for category Elasticsearch in sidebar skill"
65 | },
66 | "sidebar.skill.link.React": {
67 | "message": "React",
68 | "description": "The label for link React in sidebar skill, linking to /docs/category/react"
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/i18n/zh/docusaurus-theme-classic/footer.json:
--------------------------------------------------------------------------------
1 | {
2 | "link.title.学习": {
3 | "message": "学习",
4 | "description": "The title of the footer links column with title=学习 in the footer"
5 | },
6 | "link.title.社交媒体": {
7 | "message": "社交媒体",
8 | "description": "The title of the footer links column with title=社交媒体 in the footer"
9 | },
10 | "link.title.友情链接": {
11 | "message": "友情链接",
12 | "description": "The title of the footer links column with title=友情链接 in the footer"
13 | },
14 | "link.item.label.技术博客": {
15 | "message": "技术博客",
16 | "description": "The label of footer link with label=技术博客 linking to /#homepage_blogs"
17 | },
18 | "link.item.label.技术笔记": {
19 | "message": "技术笔记",
20 | "description": "The label of footer link with label=技术笔记 linking to docs/skill"
21 | },
22 | "link.item.label.实战项目": {
23 | "message": "实战项目",
24 | "description": "The label of footer link with label=实战项目 linking to project"
25 | },
26 | "link.item.label.首页": {
27 | "message": "首页",
28 | "description": "The label of footer link with label=首页 linking to /"
29 | },
30 | "link.item.label.关于我": {
31 | "message": "关于我",
32 | "description": "The label of footer link with label=关于我 linking to /about"
33 | },
34 | "link.item.label.GitHub": {
35 | "message": "GitHub",
36 | "description": "The label of footer link with label=GitHub linking to https://github.com/xxsoftware"
37 | },
38 | "link.item.label.掘金": {
39 | "message": "掘金",
40 | "description": "The label of footer link with label=掘金 linking to https://juejin.cn/user/1565318510545901"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/i18n/zh/docusaurus-theme-classic/navbar.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": {
3 | "message": "東雲研究所",
4 | "description": "The title in the navbar"
5 | },
6 | "item.label.标签": {
7 | "message": "标签",
8 | "description": "Navbar item with label 标签"
9 | },
10 | "item.label.归档": {
11 | "message": "归档",
12 | "description": "Navbar item with label 归档"
13 | },
14 | "item.label.学习": {
15 | "message": "学习",
16 | "description": "Navbar item with label 学习"
17 | },
18 | "item.label.小工具": {
19 | "message": "小工具",
20 | "description": "Navbar item with label 小工具"
21 | },
22 | "item.label.实战项目": {
23 | "message": "实战项目",
24 | "description": "Navbar item with label 实战项目"
25 | },
26 | "item.label.技术笔记": {
27 | "message": "技术笔记",
28 | "description": "Navbar item with label 技术笔记"
29 | },
30 | "item.label.网址导航": {
31 | "message": "网址导航",
32 | "description": "Navbar item with label 网址导航"
33 | },
34 | "item.label.JS代码还原": {
35 | "message": "JS代码还原",
36 | "description": "Navbar item with label JS代码还原"
37 | },
38 | "item.label.CyberChef加密": {
39 | "message": "CyberChef加密",
40 | "description": "Navbar item with label CyberChef加密"
41 | },
42 | "item.label.网盘": {
43 | "message": "网盘",
44 | "description": "Navbar item with label 网盘"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.module.scss' {
2 | const classes: { readonly [key: string]: string }
3 | export default classes
4 | }
5 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:base"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/sidebars.js:
--------------------------------------------------------------------------------
1 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
2 | const sidebars = {
3 | skill: [
4 | 'skill/introduction',
5 |
6 | {
7 | label: '代码规范',
8 | type: 'category',
9 | link: {
10 | type: 'doc',
11 | id: 'skill/code-specification/code-specification-guides',
12 | },
13 | items: [
14 | 'skill/code-specification/eslint',
15 | 'skill/code-specification/prettier',
16 | 'skill/code-specification/stylelint',
17 | 'skill/code-specification/editorconfig',
18 | 'skill/code-specification/husky',
19 | 'skill/code-specification/npmrc',
20 | ],
21 | },
22 | {
23 | label: 'Vue',
24 | type: 'category',
25 | link: {
26 | type: 'generated-index',
27 | },
28 | items: [
29 | {
30 | type: 'autogenerated',
31 | dirName: 'skill/vue',
32 | },
33 | ],
34 | },
35 |
36 | {
37 | label: 'Web',
38 | type: 'category',
39 | link: {
40 | type: 'generated-index',
41 | },
42 | items: [
43 | {
44 | type: 'autogenerated',
45 | dirName: 'skill/web',
46 | },
47 | ],
48 | },
49 | {
50 | label: 'JavaScript',
51 | type: 'category',
52 | link: {
53 | type: 'generated-index',
54 | },
55 | items: [
56 | {
57 | type: 'autogenerated',
58 | dirName: 'skill/js',
59 | },
60 | ],
61 | },
62 | {
63 | label: 'Node',
64 | type: 'category',
65 | link: {
66 | type: 'generated-index',
67 | },
68 | items: [
69 | {
70 | type: 'autogenerated',
71 | dirName: 'skill/node',
72 | },
73 | ],
74 | },
75 | {
76 | label: 'Css',
77 | type: 'category',
78 | link: {
79 | type: 'generated-index',
80 | },
81 | items: [
82 | {
83 | type: 'autogenerated',
84 | dirName: 'skill/css',
85 | },
86 | ],
87 | },
88 |
89 | {
90 | label: 'Git',
91 | type: 'category',
92 | link: {
93 | type: 'generated-index',
94 | },
95 | items: [
96 | {
97 | type: 'autogenerated',
98 | dirName: 'skill/git',
99 | },
100 | ],
101 | },
102 | {
103 | label: '算法',
104 | type: 'category',
105 | link: {
106 | type: 'generated-index',
107 | },
108 | items: [
109 | {
110 | type: 'autogenerated',
111 | dirName: 'skill/algorithm',
112 | },
113 | ],
114 | },
115 | ],
116 | tools: [
117 | 'tools/introduction',
118 | 'tools/everything-quick-search-local-files',
119 | 'tools/power-toys',
120 | 'tools/wappalyzer-recognize-technology',
121 | 'tools/vite-plugin',
122 | ],
123 | }
124 |
125 | module.exports = sidebars
126 |
--------------------------------------------------------------------------------
/src/components/BrowserWindow/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import styles from './styles.module.css'
4 |
5 | function BrowserWindow({ children, minHeight, url }) {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
{url}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
{children}
25 |
26 | )
27 | }
28 |
29 | export default BrowserWindow
30 |
--------------------------------------------------------------------------------
/src/components/BrowserWindow/styles.module.css:
--------------------------------------------------------------------------------
1 | .browserWindow {
2 | border: 3px solid var(--ifm-color-emphasis-200);
3 | border-top-left-radius: var(--ifm-global-radius);
4 | border-top-right-radius: var(--ifm-global-radius);
5 | margin-bottom: 10px;
6 | }
7 |
8 | .browserWindowHeader {
9 | align-items: center;
10 | background: var(--ifm-color-emphasis-200);
11 | display: flex;
12 | padding: 0.5rem 1rem;
13 | }
14 |
15 | .row:after {
16 | content: '';
17 | display: table;
18 | clear: both;
19 | }
20 |
21 | .buttons {
22 | white-space: nowrap;
23 | }
24 |
25 | .right {
26 | align-self: center;
27 | width: 10%;
28 | }
29 |
30 | .browserWindowAddressBar {
31 | flex: 1 0;
32 | margin: 0 1rem 0 0.5rem;
33 | border-radius: 12.5px;
34 | background-color: #fff;
35 | color: #666;
36 | padding: 5px 15px;
37 | font: 400 13px Arial;
38 | user-select: none;
39 | }
40 |
41 | .dot {
42 | margin-right: 6px;
43 | margin-top: 4px;
44 | height: 12px;
45 | width: 12px;
46 | background-color: #bbb;
47 | border-radius: 50%;
48 | display: inline-block;
49 | }
50 |
51 | .browserWindowMenuIcon {
52 | margin-left: auto;
53 | }
54 |
55 | .bar {
56 | width: 17px;
57 | height: 3px;
58 | background-color: #aaa;
59 | margin: 3px 0;
60 | display: block;
61 | }
62 |
63 | .browserWindowBody {
64 | padding: 1rem;
65 | }
66 |
--------------------------------------------------------------------------------
/src/components/Button/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from '@emotion/styled'
3 |
4 | function Button({ isLink = false, children, ...rest }) {
5 | return (
6 |
7 | {children}
8 |
9 | )
10 | }
11 |
12 | const StyledButton = styled.button`
13 | display: inline-block;
14 | color: white;
15 | padding: 0.75em 20px;
16 | margin-left: -2px;
17 | font-weight: 600;
18 | background: linear-gradient(
19 | 90deg,
20 | var(--ifm-color-primary) 11.3%,
21 | var(--ifm-color-primary-light) 161.54%
22 | );
23 | box-shadow: 0px 0px 32px rgba(0, 105, 165, 0.35);
24 | border-radius: 7px;
25 | font-family: 'Yuanti SC', 'Youyuan', 'You Yuan', '幼圆', 'PingFang SC',
26 | 'Microsoft Yahei', Helvetica, sans-serif;
27 |
28 | :hover {
29 | color: white;
30 | text-decoration: none;
31 | }
32 | `
33 |
34 | export default Button
35 |
--------------------------------------------------------------------------------
/src/components/CodeSandBox/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import useThemeContext from '@theme/hooks/useThemeContext'
4 |
5 | function index({ slug, title, height = '600px' }) {
6 | const { isDarkTheme } = useThemeContext()
7 | const themedSrc = `https://codesandbox.io/embed/${slug}?fontsize=14&hidenavigation=1&view=preview&theme=${
8 | isDarkTheme ? 'dark' : 'light'
9 | }`
10 | return (
11 |
12 |
25 |
26 | )
27 | }
28 |
29 | index.propTypes = {}
30 |
31 | export default index
32 |
--------------------------------------------------------------------------------
/src/components/Codepen/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function Codepen({ height = 300, title, hash, theme = 'dark', ...rest }) {
4 | return (
5 |
20 | )
21 | }
22 |
23 | export default Codepen
24 |
--------------------------------------------------------------------------------
/src/components/Comment/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useThemeConfig, useColorMode } from '@docusaurus/theme-common'
3 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
4 | import { ThemeConfig } from '@docusaurus/preset-classic'
5 | import BrowserOnly from '@docusaurus/BrowserOnly'
6 | import Giscus, { GiscusProps } from '@giscus/react'
7 |
8 | interface CustomThemeConfig extends ThemeConfig {
9 | giscus: GiscusProps & { darkTheme: string }
10 | }
11 |
12 | const defaultConfig: Partial & { darkTheme: string } = {
13 | id: 'comments',
14 | mapping: 'title',
15 | reactionsEnabled: '1',
16 | emitMetadata: '0',
17 | inputPosition: 'top',
18 | lang: 'zh-CN',
19 | theme: 'light',
20 | darkTheme: 'dark',
21 | }
22 |
23 | export default function Comment(): JSX.Element {
24 | const themeConfig = useThemeConfig() as CustomThemeConfig
25 | const { i18n } = useDocusaurusContext()
26 |
27 | // merge default config
28 | const giscus = { ...defaultConfig, ...themeConfig.giscus }
29 |
30 | if (!giscus.repo || !giscus.repoId || !giscus.categoryId) {
31 | throw new Error(
32 | 'You must provide `repo`, `repoId`, and `categoryId` to `themeConfig.giscus`.',
33 | )
34 | }
35 |
36 | giscus.theme =
37 | useColorMode().colorMode === 'dark' ? giscus.darkTheme : giscus.theme
38 | giscus.lang = i18n.currentLocale
39 |
40 | return (
41 | Loading Comments...}>
42 | {() => }
43 |
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/Playground/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Translate from '@docusaurus/Translate'
3 | import Link from '@docusaurus/Link'
4 | import Image from '@theme/IdealImage'
5 | import clsx from 'clsx'
6 |
7 | const Playgrounds = [
8 | {
9 | name: '📦 CodeSandbox',
10 | image: require('@site/static/img/playgrounds/codesandbox.png'),
11 | url: 'https://new.docusaurus.io/codesandbox',
12 | description: (
13 |
14 | CodeSandbox is a popular playground solution. Runs Docusaurus in a
15 | remote Docker container.
16 |
17 | ),
18 | },
19 | {
20 | name: '⚡ StackBlitz 🆕',
21 | image: require('@site/static/img/playgrounds/stackblitz.png'),
22 | url: 'https://new.docusaurus.io/stackblitz',
23 | description: (
24 |
29 | WebContainers
30 |
31 | ),
32 | }}
33 | >
34 | {
35 | 'StackBlitz uses a novel {webContainersLink} technology to run Docusaurus directly in your browser.'
36 | }
37 |
38 | ),
39 | },
40 | ]
41 |
42 | function PlaygroundCard({ name, image, url, description }) {
43 | return (
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
{name}
53 |
{description}
54 |
55 |
56 |
57 |
58 | Try it now!
59 |
60 |
61 |
62 |
63 |
64 | )
65 | }
66 |
67 | export function PlaygroundCardsRow() {
68 | return (
69 |
70 | {Playgrounds.map(playground => (
71 |
72 | ))}
73 |
74 | )
75 | }
76 |
--------------------------------------------------------------------------------
/src/components/Svg/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { type ReactNode, type ComponentProps } from 'react'
2 | import clsx from 'clsx'
3 | import styles from './styles.module.css'
4 |
5 | export interface SvgIconProps extends ComponentProps<'svg'> {
6 | viewBox?: string
7 | size?: 'inherit' | 'small' | 'medium' | 'large'
8 | color?: 'inherit' | 'primary' | 'secondary' | 'success' | 'error' | 'warning'
9 | svgClass?: string // Class attribute on the child
10 | colorAttr?: string // Applies a color attribute to the SVG element.
11 | children: ReactNode // Node passed into the SVG element.
12 | }
13 |
14 | export default function Svg(props: SvgIconProps): JSX.Element {
15 | const {
16 | svgClass,
17 | colorAttr,
18 | children,
19 | color = 'inherit',
20 | size = 'medium',
21 | viewBox = '0 0 24 24',
22 | ...rest
23 | } = props
24 |
25 | return (
26 |
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/Svg/styles.module.css:
--------------------------------------------------------------------------------
1 | .svgIcon {
2 | user-select: none;
3 | width: 1em;
4 | height: 1em;
5 | display: inline-block;
6 | fill: currentColor;
7 | flex-shrink: 0;
8 | color: inherit;
9 | }
10 |
11 | /* font-size */
12 | .small {
13 | font-size: 1.25rem;
14 | }
15 |
16 | .medium {
17 | font-size: 1.5rem;
18 | }
19 |
20 | .large {
21 | font-size: 2.185rem;
22 | }
23 |
24 | /* colors */
25 | .primary {
26 | color: var(--ifm-color-primary);
27 | }
28 |
29 | .secondary {
30 | color: var(--ifm-color-secondary);
31 | }
32 |
33 | .success {
34 | color: var(--ifm-color-success);
35 | }
36 |
37 | .error {
38 | color: var(--ifm-color-error);
39 | }
40 |
41 | .warning {
42 | color: var(--ifm-color-warning);
43 | }
44 |
45 | .inherit {
46 | color: inherit;
47 | }
48 |
--------------------------------------------------------------------------------
/src/components/svgIcons/FavoriteIcon/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Svg, { type SvgIconProps } from '@site/src/components/Svg'
3 |
4 | export default function FavoriteIcon(
5 | props: Omit,
6 | ): JSX.Element {
7 | return (
8 |
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/src/pages/index.tsx.bak:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import classnames from 'classnames';
3 | import Layout from '@theme/Layout';
4 | import Link from '@docusaurus/Link';
5 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
6 | import useBaseUrl from '@docusaurus/useBaseUrl';
7 | import styles from './styles.module.css';
8 |
9 | const features = [
10 | {
11 | title: <>Easy to Use>,
12 | imageUrl: 'img/undraw_docusaurus_mountain.svg',
13 | description: (
14 | <>
15 | Docusaurus was designed from the ground up to be easily installed and
16 | used to get your website up and running quickly.
17 | >
18 | ),
19 | },
20 | {
21 | title: <>Focus on What Matters>,
22 | imageUrl: 'img/undraw_docusaurus_tree.svg',
23 | description: (
24 | <>
25 | Docusaurus lets you focus on your docs, and we'll do the chores. Go
26 | ahead and move your docs into the docs
directory.
27 | >
28 | ),
29 | },
30 | {
31 | title: <>Powered by React>,
32 | imageUrl: 'img/undraw_docusaurus_react.svg',
33 | description: (
34 | <>
35 | Extend or customize your website layout by reusing React. Docusaurus can
36 | be extended while reusing the same header and footer.
37 | >
38 | ),
39 | },
40 | ];
41 |
42 | function Feature({imageUrl, title, description}) {
43 | const imgUrl = useBaseUrl(imageUrl);
44 | return (
45 |
46 | {imgUrl && (
47 |
48 |

49 |
50 | )}
51 |
{title}
52 |
{description}
53 |
54 | );
55 | }
56 |
57 | function Home() {
58 | const context = useDocusaurusContext();
59 | const {siteConfig = {}} = context;
60 | return (
61 |
64 |
80 |
81 | {features && features.length && (
82 |
83 |
84 |
85 | {features.map((props, idx) => (
86 |
87 | ))}
88 |
89 |
90 |
91 | )}
92 |
93 |
94 | );
95 | }
96 |
97 | export default Home;
98 |
--------------------------------------------------------------------------------
/src/pages/project/_components/ShowcaseCard/styles.module.css:
--------------------------------------------------------------------------------
1 | .showcaseCard {
2 | position: relative;
3 | box-shadow: 0 10px 30px -5px rgb(0 0 0 / 30%);
4 | transition: box-shadow 0.5s, opacity 0.5s;
5 | will-change: transform;
6 | overflow: hidden;
7 | touch-action: none;
8 | }
9 |
10 | .showcaseCard::before {
11 | display: block;
12 | font-weight: bold;
13 | height: 0;
14 | overflow: hidden;
15 | visibility: hidden;
16 | }
17 |
18 | .showcaseCardImage {
19 | overflow: hidden;
20 | height: 150px;
21 | border-bottom: 2px solid var(--ifm-color-emphasis-200);
22 | }
23 |
24 | .showcaseCardHeader {
25 | display: flex;
26 | align-items: center;
27 | margin-bottom: 12px;
28 | }
29 |
30 | .showcaseCardTitle {
31 | margin-bottom: 0;
32 | flex: 1 1 auto;
33 | }
34 |
35 | .showcaseCardTitle a {
36 | text-decoration: none;
37 | background: linear-gradient(
38 | var(--ifm-color-primary),
39 | var(--ifm-color-primary)
40 | )
41 | 0% 100% / 0% 1px no-repeat;
42 | transition: background-size ease-out 200ms;
43 | }
44 |
45 | .showcaseCardTitle a:not(:focus):hover {
46 | background-size: 100% 1px;
47 | }
48 |
49 | .showcaseCardTitle,
50 | .showcaseCardHeader .svgIconFavorite {
51 | margin-right: 0.25rem;
52 | }
53 |
54 | .showcaseCardHeader .svgIconFavorite {
55 | color: var(--site-color-svg-icon-favorite);
56 | }
57 |
58 | .showcaseCardSrcBtn {
59 | margin-left: 6px;
60 | padding-left: 12px;
61 | padding-right: 12px;
62 | border: none;
63 | }
64 |
65 | .showcaseCardSrcBtn:focus-visible {
66 | background-color: var(--ifm-color-secondary-dark);
67 | }
68 |
69 | html[data-theme='dark'] .showcaseCardSrcBtn {
70 | background-color: var(--ifm-color-emphasis-200) !important;
71 | color: inherit;
72 | }
73 |
74 | html[data-theme='dark'] .showcaseCardSrcBtn:hover {
75 | background-color: var(--ifm-color-emphasis-300) !important;
76 | }
77 |
78 | .showcaseCardBody {
79 | font-size: smaller;
80 | line-height: 1.66;
81 | }
82 |
83 | .cardFooter {
84 | display: flex;
85 | flex-wrap: wrap;
86 | }
87 |
88 | .tag {
89 | font-size: 0.675rem;
90 | border: 1px solid var(--ifm-color-secondary-darkest);
91 | cursor: default;
92 | margin-right: 6px;
93 | margin-bottom: 6px !important;
94 | border-radius: 12px;
95 | display: inline-flex;
96 | align-items: center;
97 | }
98 |
99 | .tag .textLabel {
100 | margin-left: 8px;
101 | }
102 |
103 | .tag .colorLabel {
104 | width: 7px;
105 | height: 7px;
106 | border-radius: 50%;
107 | margin-left: 6px;
108 | margin-right: 6px;
109 | }
110 |
--------------------------------------------------------------------------------
/src/pages/project/_components/ShowcaseFilterToggle/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useCallback } from 'react'
2 | import { useHistory, useLocation } from '@docusaurus/router'
3 |
4 | import { prepareUserState } from '../../index.tsx'
5 |
6 | import styles from './styles.module.css'
7 | import clsx from 'clsx'
8 |
9 | export type Operator = 'OR' | 'AND'
10 |
11 | export const OperatorQueryKey = 'operator'
12 |
13 | export function readOperator(search: string): Operator {
14 | return (new URLSearchParams(search).get(OperatorQueryKey) ?? 'OR') as Operator
15 | }
16 |
17 | export default function ShowcaseFilterToggle(): JSX.Element {
18 | const id = 'showcase_filter_toggle'
19 | const location = useLocation()
20 | const history = useHistory()
21 | const [operator, setOperator] = useState(false)
22 | useEffect(() => {
23 | setOperator(readOperator(location.search) === 'AND')
24 | }, [location])
25 | const toggleOperator = useCallback(() => {
26 | setOperator(o => !o)
27 | const searchParams = new URLSearchParams(location.search)
28 | searchParams.delete(OperatorQueryKey)
29 | if (!operator) {
30 | searchParams.append(OperatorQueryKey, operator ? 'OR' : 'AND')
31 | }
32 | history.push({
33 | ...location,
34 | search: searchParams.toString(),
35 | state: prepareUserState(),
36 | })
37 | }, [operator, location, history])
38 |
39 | return (
40 |
41 | {
48 | if (e.key === 'Enter') {
49 | toggleOperator()
50 | }
51 | }}
52 | checked={operator}
53 | />
54 | {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
55 |
59 |
60 | )
61 | }
62 |
--------------------------------------------------------------------------------
/src/pages/project/_components/ShowcaseFilterToggle/styles.module.css:
--------------------------------------------------------------------------------
1 | .checkboxLabel {
2 | --height: 25px;
3 | --width: 80px;
4 | --border: 2px;
5 | display: flex;
6 | width: var(--width);
7 | height: var(--height);
8 | position: relative;
9 | border-radius: var(--height);
10 | border: var(--border) solid var(--ifm-color-primary-darkest);
11 | cursor: pointer;
12 | justify-content: space-around;
13 | opacity: 0.75;
14 | transition: opacity var(--ifm-transition-fast)
15 | var(--ifm-transition-timing-default);
16 | box-shadow: var(--ifm-global-shadow-md);
17 | }
18 |
19 | .checkboxLabel:hover {
20 | opacity: 1;
21 | box-shadow: var(--ifm-global-shadow-md),
22 | 0 0 2px 1px var(--ifm-color-primary-dark);
23 | }
24 |
25 | .checkboxLabel::after {
26 | position: absolute;
27 | content: '';
28 | inset: 0;
29 | width: calc(var(--width) / 2);
30 | height: 100%;
31 | border-radius: var(--height);
32 | background-color: var(--ifm-color-primary-darkest);
33 | transition: transform var(--ifm-transition-fast)
34 | var(--ifm-transition-timing-default);
35 | transform: translateX(calc(var(--width) / 2 - var(--border)));
36 | }
37 |
38 | input:focus-visible ~ .checkboxLabel::after {
39 | outline: 2px solid currentColor;
40 | }
41 |
42 | .checkboxLabel > * {
43 | font-size: 0.8rem;
44 | color: inherit;
45 | transition: opacity 150ms ease-in 50ms;
46 | }
47 |
48 | input:checked ~ .checkboxLabel::after {
49 | transform: translateX(calc(-1 * var(--border)));
50 | }
51 |
--------------------------------------------------------------------------------
/src/pages/project/_components/ShowcaseTagSelect/index.tsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | type ComponentProps,
3 | type ReactNode,
4 | type ReactElement,
5 | useCallback,
6 | useState,
7 | useEffect,
8 | } from 'react'
9 | import { useHistory, useLocation } from '@docusaurus/router'
10 | import { toggleListItem } from '@site/src/utils/jsUtils'
11 | import { prepareUserState } from '../../index.tsx'
12 | import type { TagType } from '@site/data/users'
13 |
14 | import styles from './styles.module.css'
15 |
16 | interface Props extends ComponentProps<'input'> {
17 | icon: ReactElement>
18 | label: ReactNode
19 | tag: TagType
20 | }
21 |
22 | const TagQueryStringKey = 'tags'
23 |
24 | export function readSearchTags(search: string): TagType[] {
25 | return new URLSearchParams(search).getAll(TagQueryStringKey) as TagType[]
26 | }
27 |
28 | function replaceSearchTags(search: string, newTags: TagType[]) {
29 | const searchParams = new URLSearchParams(search)
30 | searchParams.delete(TagQueryStringKey)
31 | newTags.forEach(tag => searchParams.append(TagQueryStringKey, tag))
32 | return searchParams.toString()
33 | }
34 |
35 | const ShowcaseTagSelect = React.forwardRef(
36 | ({ id, icon, label, tag, ...rest }, ref) => {
37 | const location = useLocation()
38 | const history = useHistory()
39 | const [selected, setSelected] = useState(false)
40 | useEffect(() => {
41 | const tags = readSearchTags(location.search)
42 | setSelected(tags.includes(tag))
43 | }, [tag, location])
44 | const toggleTag = useCallback(() => {
45 | const tags = readSearchTags(location.search)
46 | const newTags = toggleListItem(tags, tag)
47 | const newSearch = replaceSearchTags(location.search, newTags)
48 | history.push({
49 | ...location,
50 | search: newSearch,
51 | state: prepareUserState(),
52 | })
53 | }, [tag, location, history])
54 | return (
55 | <>
56 | {
61 | if (e.key === 'Enter') {
62 | toggleTag()
63 | }
64 | }}
65 | onFocus={e => {
66 | if (e.relatedTarget) {
67 | e.target.nextElementSibling?.dispatchEvent(
68 | new KeyboardEvent('focus'),
69 | )
70 | }
71 | }}
72 | onBlur={e => {
73 | e.target.nextElementSibling?.dispatchEvent(
74 | new KeyboardEvent('blur'),
75 | )
76 | }}
77 | onChange={toggleTag}
78 | checked={selected}
79 | {...rest}
80 | />
81 |
85 | >
86 | )
87 | },
88 | )
89 |
90 | export default ShowcaseTagSelect
91 |
--------------------------------------------------------------------------------
/src/pages/project/_components/ShowcaseTagSelect/styles.module.css:
--------------------------------------------------------------------------------
1 | .checkboxLabel:hover {
2 | opacity: 1;
3 | box-shadow: 0 0 2px 1px var(--ifm-color-secondary-darkest);
4 | }
5 |
6 | input[type='checkbox'] + .checkboxLabel {
7 | display: flex;
8 | align-items: center;
9 | cursor: pointer;
10 | line-height: 1.5;
11 | border-radius: 4px;
12 | padding: 0.275rem 0.8rem;
13 | opacity: 0.85;
14 | transition: opacity 200ms ease-out;
15 | border: 2px solid var(--ifm-color-secondary-darkest);
16 | }
17 |
18 | input:focus-visible + .checkboxLabel {
19 | outline: 2px solid currentColor;
20 | }
21 |
22 | input:checked + .checkboxLabel {
23 | opacity: 0.9;
24 | background-color: var(--site-color-checkbox-checked-bg);
25 | border: 2px solid var(--ifm-color-primary-darkest);
26 | }
27 |
28 | input:checked + .checkboxLabel:hover {
29 | opacity: 0.75;
30 | box-shadow: 0 0 2px 1px var(--ifm-color-primary-dark);
31 | }
32 |
--------------------------------------------------------------------------------
/src/pages/project/_components/ShowcaseTooltip/styles.module.css:
--------------------------------------------------------------------------------
1 | .tooltip {
2 | border-radius: 4px;
3 | padding: 4px 8px;
4 | color: var(--site-color-tooltip);
5 | background: var(--site-color-tooltip-background);
6 | font-size: 0.8rem;
7 | z-index: 500;
8 | line-height: 1.4;
9 | font-weight: 500;
10 | max-width: 300px;
11 | opacity: 0.92;
12 | }
13 |
14 | .tooltipArrow {
15 | visibility: hidden;
16 | }
17 |
18 | .tooltipArrow,
19 | .tooltipArrow::before {
20 | position: absolute;
21 | width: 8px;
22 | height: 8px;
23 | background: inherit;
24 | }
25 |
26 | .tooltipArrow::before {
27 | visibility: visible;
28 | content: '';
29 | transform: rotate(45deg);
30 | }
31 |
32 | .tooltip[data-popper-placement^='top'] > .tooltipArrow {
33 | bottom: -4px;
34 | }
35 |
36 | .tooltip[data-popper-placement^='bottom'] > .tooltipArrow {
37 | top: -4px;
38 | }
39 |
--------------------------------------------------------------------------------
/src/pages/project/styles.module.css:
--------------------------------------------------------------------------------
1 | .filterCheckbox {
2 | justify-content: space-between;
3 | }
4 |
5 | .filterCheckbox,
6 | .checkboxList {
7 | display: flex;
8 | align-items: center;
9 | }
10 |
11 | .filterCheckbox > div:first-child {
12 | display: flex;
13 | flex: 1 1 auto;
14 | align-items: center;
15 | }
16 |
17 | .filterCheckbox > div > * {
18 | margin-bottom: 0;
19 | margin-right: 8px;
20 | }
21 |
22 | .checkboxList {
23 | flex-wrap: wrap;
24 | }
25 |
26 | .checkboxList,
27 | .showcaseList {
28 | padding: 0;
29 | list-style: none;
30 | }
31 |
32 | .checkboxListItem {
33 | user-select: none;
34 | white-space: nowrap;
35 | height: 32px;
36 | font-size: 0.8rem;
37 | margin-top: 0.5rem;
38 | margin-right: 0.5rem;
39 | }
40 |
41 | .checkboxListItem:last-child {
42 | margin-right: 0;
43 | }
44 |
45 | .searchContainer {
46 | margin-left: auto;
47 | }
48 |
49 | .searchContainer input {
50 | height: 30px;
51 | border-radius: 15px;
52 | padding: 10px;
53 | border: 1px solid gray;
54 | }
55 |
56 | .showcaseList {
57 | display: grid;
58 | grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
59 | gap: 24px;
60 | }
61 |
62 | .showcaseFavorite {
63 | padding-top: 2rem;
64 | padding-bottom: 2rem;
65 | background-color: var(--site-color-favorite-background);
66 | }
67 |
68 | .showcaseFavoriteHeader {
69 | display: flex;
70 | align-items: center;
71 | }
72 |
73 | .showcaseFavoriteHeader > h2 {
74 | margin-bottom: 0;
75 | }
76 |
77 | .showcaseFavoriteHeader > svg {
78 | width: 30px;
79 | height: 30px;
80 | }
81 |
82 | .svgIconFavoriteXs,
83 | .svgIconFavorite {
84 | color: var(--site-color-svg-icon-favorite);
85 | }
86 |
87 | .svgIconFavoriteXs {
88 | margin-left: 0.625rem;
89 | font-size: 1rem;
90 | }
91 |
92 | .svgIconFavorite {
93 | margin-left: 1rem;
94 | }
95 |
--------------------------------------------------------------------------------
/src/pages/resource/_components/ResourceCard/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { memo } from 'react'
2 | import clsx from 'clsx'
3 | import Link from '@docusaurus/Link'
4 |
5 | import styles from './styles.module.css'
6 | import { type Resource } from '@site/data/resource'
7 | import Tooltip from '../../../project/_components/ShowcaseTooltip'
8 |
9 | const ResourceCard = memo(({ resource }: { resource: Resource }) => (
10 |
14 |
23 |
24 |
25 |
26 |
27 | {resource.name}
28 |
29 |
30 |
31 |
37 | {resource.desc}
38 |
39 |
40 |
41 | ))
42 |
43 | export default ResourceCard
44 |
--------------------------------------------------------------------------------
/src/pages/resource/_components/ResourceCard/styles.module.css:
--------------------------------------------------------------------------------
1 | .resourceCard {
2 | background-color: var(--ifm-card-background-color);
3 | border-radius: 8px;
4 | border: 1px solid rgb(204 204 204 / 50%);
5 | height: 100%;
6 | display: flex;
7 | flex-direction: row;
8 | overflow: hidden;
9 | transition: all 0.3s ease-in-out;
10 | }
11 |
12 | .resourceCard:hover {
13 | box-shadow: 0 10px 20px -10px rgb(0 0 0 / 20%);
14 | transform: translateY(-5px);
15 | }
16 |
17 | html[data-theme='dark'] .resourceCard:hover {
18 | box-shadow: 0 10px 20px -10px rgb(255 255 255 / 20%);
19 | transform: translateY(-5px);
20 | }
21 |
22 | .resourceCardImage {
23 | align-self: center;
24 | width: 64px;
25 | height: 64px;
26 | min-width: 64px;
27 | object-fit: contain;
28 | }
29 |
30 | .resourceCardHeader {
31 | display: flex;
32 | align-items: center;
33 | margin-bottom: 4px;
34 | }
35 |
36 | .resourceCardTitle {
37 | margin-bottom: 0;
38 | flex: 1 1 auto;
39 | }
40 |
41 | .resourceCardTitle a {
42 | text-decoration: none;
43 | background: linear-gradient(
44 | var(--ifm-color-primary),
45 | var(--ifm-color-primary)
46 | )
47 | 0% 100% / 0% 1px no-repeat;
48 | transition: background-size ease-out 200ms;
49 | }
50 |
51 | .resourceCardTitle a:not(:focus):hover {
52 | background-size: 100% 1px;
53 | }
54 |
55 | .resourceCardBody {
56 | padding: 0.2rem 0 0 var(--ifm-card-horizontal-spacing);
57 | }
58 |
59 | .resourceCardDesc {
60 | cursor: pointer;
61 | font-size: smaller;
62 | line-height: 1.66;
63 | width: 100%;
64 | margin: 0;
65 | /* stylelint-disable-next-line value-no-vendor-prefix */
66 | display: -webkit-box;
67 | overflow: hidden;
68 | -webkit-line-clamp: 2;
69 | -webkit-box-orient: vertical;
70 | }
71 |
--------------------------------------------------------------------------------
/src/pages/resource/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import clsx from 'clsx'
3 | import Link from '@docusaurus/Link'
4 | import { PageMetadata } from '@docusaurus/theme-common'
5 | import Layout from '@theme/Layout'
6 | import ResourceCard from './_components/ResourceCard'
7 | import BackToTopButton from '@theme/BackToTopButton'
8 | import { resourceData } from '@site/data/resource'
9 | import styles from './resource.module.css'
10 |
11 | function CategoryNav() {
12 | const sidebar = {
13 | title: '',
14 | items: resourceData.map(w => ({ title: w.name, permalink: `#${w.name}` })),
15 | }
16 |
17 | return (
18 |
37 | )
38 | }
39 |
40 | function CategoryList() {
41 | return (
42 |
43 | {resourceData.map(cate => (
44 |
45 |
46 |
47 | {cate.name}
48 |
53 |
54 |
55 |
56 |
57 | {cate.resources.map(resource => (
58 |
59 | ))}
60 |
61 |
62 |
63 | ))}
64 |
65 | )
66 | }
67 |
68 | export default function Resources() {
69 | const title = '网址导航'
70 | const description = '整合日常开发常用,推荐的网站导航页'
71 |
72 | return (
73 | <>
74 |
75 |
76 |
77 |
78 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | >
89 | )
90 | }
91 |
--------------------------------------------------------------------------------
/src/pages/resource/resource.module.css:
--------------------------------------------------------------------------------
1 | .sidebar {
2 | max-height: calc(100vh - (var(--ifm-navbar-height) + 2rem));
3 | overflow-y: auto;
4 | position: sticky;
5 | top: calc(var(--ifm-navbar-height) + 2rem);
6 | }
7 |
8 | .sidebarItemTitle {
9 | font-size: var(--ifm-h3-font-size);
10 | font-weight: var(--ifm-font-weight-bold);
11 | font-family: var(--ifm-heading-font-family);
12 | }
13 |
14 | .sidebarItemList {
15 | font-size: 0.9rem;
16 | }
17 |
18 | .sidebarItem {
19 | margin-top: 0.7rem;
20 | }
21 |
22 | .sidebarItemLink {
23 | color: var(--ifm-font-color-base);
24 | display: block;
25 | }
26 |
27 | .sidebarItemLink:hover {
28 | text-decoration: none;
29 | }
30 |
31 | .sidebarItemLinkActive {
32 | color: var(--ifm-color-primary) !important;
33 | }
34 |
35 | @media (max-width: 996px) {
36 | .sidebar {
37 | display: none;
38 | }
39 | }
40 |
41 | .hero {
42 | align-items: center;
43 | background-color: var(--ifm-color-primary);
44 | color: var(--ifm-font-color-base-inverse);
45 | display: flex;
46 | padding: 2rem 2rem;
47 | }
48 |
49 | .heroTitle {
50 | font-size: 2rem !important;
51 | font-weight: 700;
52 | line-height: 2rem;
53 | color: #fff;
54 | }
55 |
56 | .heroDesc {
57 | margin-bottom: 0;
58 | }
59 |
60 | .cateHeader {
61 | font-size: 1rem;
62 | font-weight: 600;
63 | }
64 |
65 | .cateHeader > h2 {
66 | font-family: sans-serif;
67 | }
68 |
69 | .cateBody {
70 | display: flex;
71 | padding: 0 1rem;
72 | }
73 |
74 | .resourceList {
75 | display: grid;
76 | grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
77 | gap: 16px;
78 | padding: 0;
79 | }
80 |
--------------------------------------------------------------------------------
/src/plugin/plugin-baidu-push/index.js:
--------------------------------------------------------------------------------
1 | module.exports = function (context, options) {
2 | return {
3 | name: 'docusaurus-plugin-baidu-push',
4 | injectHtmlTags() {
5 | return {
6 | headTags: [
7 | {
8 | tagName: 'script',
9 | innerHTML: `
10 | (function(){
11 | var bp = document.createElement('script');
12 | var curProtocol = window.location.protocol.split(':')[0];
13 | if (curProtocol === 'https') {
14 | bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
15 | }
16 | else {
17 | bp.src = 'http://push.zhanzhang.baidu.com/push.js';
18 | }
19 | bp.defer = true;
20 | var s = document.getElementsByTagName("script")[0];
21 | s.parentNode.insertBefore(bp, s);
22 | })();
23 | `,
24 | },
25 | ],
26 | }
27 | },
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/plugin/plugin-baidu-tongji/index.js:
--------------------------------------------------------------------------------
1 | module.exports = function (context, options) {
2 | return {
3 | name: 'docusaurus-plugin-baidu-tongji',
4 | injectHtmlTags() {
5 | return {
6 | headTags: [
7 | {
8 | tagName: 'script',
9 | innerHTML: `
10 | var _hmt = _hmt || [];
11 | (function() {
12 | var hm = document.createElement("script");
13 | hm.src = "https://hm.baidu.com/hm.js?c9a3849aa75f9c4a4e65f846cd1a5155";
14 | hm.defer = true;
15 | var s = document.getElementsByTagName("script")[0];
16 | s.parentNode.insertBefore(hm, s);
17 | })();
18 | `,
19 | },
20 | {
21 | tagName: 'meta',
22 | attributes: {
23 | name: 'baidu-site-verification',
24 | content: 'code-rqLUw5reVS',
25 | },
26 | },
27 | ],
28 | }
29 | },
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/plugin/plugin-content-blog/lib/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | var __awaiter =
3 | (this && this.__awaiter) ||
4 | function (thisArg, _arguments, P, generator) {
5 | function adopt(value) {
6 | return value instanceof P
7 | ? value
8 | : new P(function (resolve) {
9 | resolve(value)
10 | })
11 | }
12 | return new (P || (P = Promise))(function (resolve, reject) {
13 | function fulfilled(value) {
14 | try {
15 | step(generator.next(value))
16 | } catch (e) {
17 | reject(e)
18 | }
19 | }
20 | function rejected(value) {
21 | try {
22 | step(generator['throw'](value))
23 | } catch (e) {
24 | reject(e)
25 | }
26 | }
27 | function step(result) {
28 | result.done
29 | ? resolve(result.value)
30 | : adopt(result.value).then(fulfilled, rejected)
31 | }
32 | step((generator = generator.apply(thisArg, _arguments || [])).next())
33 | })
34 | }
35 | Object.defineProperty(exports, '__esModule', { value: true })
36 | const blogPluginExports = require('@docusaurus/plugin-content-blog')
37 | const blogPlugin = blogPluginExports.default
38 | function blogPluginEnhanced(context, options) {
39 | return __awaiter(this, void 0, void 0, function* () {
40 | const blogPluginInstance = yield blogPlugin(context, options)
41 | return Object.assign(Object.assign({}, blogPluginInstance), {
42 | contentLoaded({ content, actions }) {
43 | return __awaiter(this, void 0, void 0, function* () {
44 | // Create default plugin pages
45 | yield blogPluginInstance.contentLoaded({ content, actions })
46 | // Create your additional pages
47 | const { blogPosts, blogTags } = content
48 | const { setGlobalData } = actions
49 | setGlobalData({
50 | blogs: blogPosts,
51 | tags: blogTags,
52 | })
53 | })
54 | },
55 | })
56 | })
57 | }
58 | module.exports = Object.assign(Object.assign({}, blogPluginExports), {
59 | default: blogPluginEnhanced,
60 | })
61 |
--------------------------------------------------------------------------------
/src/plugin/plugin-content-blog/lib/types.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | Object.defineProperty(exports, '__esModule', { value: true })
3 |
--------------------------------------------------------------------------------
/src/plugin/plugin-content-blog/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "plugin-content-blog",
3 | "version": "^2.2.0",
4 | "description": "Blog plugin for Docusaurus.",
5 | "main": "lib/index.js",
6 | "types": "src/plugin-content-blog.d.ts",
7 | "scripts": {
8 | "build": "tsc",
9 | "watch": "tsc --watch"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/facebook/docusaurus.git",
14 | "directory": "packages/docusaurus-plugin-content-blog"
15 | },
16 | "publishConfig": {
17 | "access": "public"
18 | },
19 | "license": "MIT",
20 | "dependencies": {
21 | "@docusaurus/core": "^2.2.0",
22 | "@docusaurus/logger": "^2.2.0",
23 | "@docusaurus/mdx-loader": "^2.2.0",
24 | "@docusaurus/utils": "^2.2.0",
25 | "@docusaurus/utils-common": "^2.2.0",
26 | "@docusaurus/utils-validation": "^2.2.0",
27 | "cheerio": "^1.0.0-rc.11",
28 | "feed": "^4.2.2",
29 | "fs-extra": "^10.1.0",
30 | "lodash": "^4.17.21",
31 | "reading-time": "^1.5.0",
32 | "remark-admonitions": "^1.2.1",
33 | "tslib": "^2.4.0",
34 | "unist-util-visit": "^2.0.3",
35 | "utility-types": "^3.10.0",
36 | "webpack": "^5.72.1"
37 | },
38 | "devDependencies": {
39 | "@docusaurus/types": "^2.2.0",
40 | "escape-string-regexp": "^4.0.0"
41 | },
42 | "peerDependencies": {
43 | "react": "^16.8.4 || ^17.0.0",
44 | "react-dom": "^16.8.4 || ^17.0.0"
45 | },
46 | "engines": {
47 | "node": ">=16.14"
48 | },
49 | "gitHead": "69ac49fc6909517f13615ee40290c4bd00c39df4"
50 | }
51 |
--------------------------------------------------------------------------------
/src/plugin/plugin-content-blog/src/index.ts:
--------------------------------------------------------------------------------
1 | import { LoadContext, Plugin } from '@docusaurus/types'
2 | import * as blogPluginExports from '@docusaurus/plugin-content-blog'
3 | import type { PluginOptions } from '@docusaurus/plugin-content-blog'
4 | import { BlogContent } from './types'
5 |
6 | const blogPlugin = blogPluginExports.default
7 |
8 | async function blogPluginEnhanced(
9 | context: LoadContext,
10 | options: PluginOptions,
11 | ): Promise> {
12 | const blogPluginInstance: any = await blogPlugin(context, options)
13 |
14 | return {
15 | ...blogPluginInstance,
16 | async contentLoaded({ content, actions }) {
17 | // Create default plugin pages
18 | await blogPluginInstance.contentLoaded({ content, actions })
19 |
20 | // Create your additional pages
21 | const { blogPosts, blogTags } = content
22 | const { setGlobalData } = actions
23 |
24 | setGlobalData({
25 | blogs: blogPosts,
26 | tags: blogTags,
27 | })
28 | },
29 | }
30 | }
31 |
32 | module.exports = {
33 | ...blogPluginExports,
34 | default: blogPluginEnhanced,
35 | }
36 |
--------------------------------------------------------------------------------
/src/plugin/plugin-content-blog/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { BrokenMarkdownLink, ContentPaths } from '@docusaurus/utils'
2 | import type { BlogPostMetadata } from '@docusaurus/plugin-content-blog'
3 | // @ts-ignore
4 | import type { Tag } from '@docusaurus/types'
5 | // @ts-ignore
6 | import type { Metadata as BlogPaginatedMetadata } from '@theme/BlogListPage'
7 |
8 | export type BlogContentPaths = ContentPaths
9 |
10 | export type BlogContent = {
11 | blogSidebarTitle: string
12 | blogPosts: BlogPost[]
13 | blogListPaginated: BlogPaginated[]
14 | blogTags: BlogTags
15 | blogTagsListPath: string
16 | }
17 |
18 | export type BlogTags = {
19 | [permalink: string]: BlogTag
20 | }
21 |
22 | export type BlogTag = Tag & {
23 | /** Blog post permalinks. */
24 | items: string[]
25 | pages: BlogPaginated[]
26 | }
27 |
28 | export type BlogPost = {
29 | id: string
30 | metadata: BlogPostMetadata
31 | content: string
32 | }
33 |
34 | export type BlogPaginated = {
35 | metadata: BlogPaginatedMetadata
36 | /** Blog post permalinks. */
37 | items: string[]
38 | }
39 |
40 | export type BlogBrokenMarkdownLink = BrokenMarkdownLink
41 | export type BlogMarkdownLoaderOptions = {
42 | siteDir: string
43 | contentPaths: BlogContentPaths
44 | truncateMarker: RegExp
45 | sourceToPermalink: { [aliasedPath: string]: string }
46 | onBrokenMarkdownLink: (brokenMarkdownLink: BlogBrokenMarkdownLink) => void
47 | }
48 |
--------------------------------------------------------------------------------
/src/plugin/plugin-content-blog/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2016",
4 | "module": "commonjs",
5 | "skipLibCheck": true,
6 | "noEmit": false,
7 | "incremental": true,
8 | "rootDir": "src",
9 | "outDir": "lib",
10 | "declaration": false
11 | },
12 | "include": ["src"],
13 | "exclude": [
14 | "src/theme",
15 | "**/__tests__/**",
16 | "**/node_modules/**",
17 | "**/coverage/**",
18 | "**/lib/**/*",
19 | "**/__mocks__/**/*",
20 | "**/__fixtures__/**/*"
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/src/sw.js:
--------------------------------------------------------------------------------
1 | import { registerRoute } from 'workbox-routing'
2 | import { StaleWhileRevalidate } from 'workbox-strategies'
3 |
4 | export default function swCustom(params) {
5 | if (params.debug) {
6 | console.log('[Docusaurus-PWA][SW]: running swCustom code', params)
7 | }
8 |
9 | // Cache responses from external resources
10 | registerRoute(
11 | context =>
12 | [
13 | /graph\.facebook\.com\/.*\/picture/,
14 | /netlify\.com\/img/,
15 | /avatars1\.githubusercontent/,
16 | ].some(regex => context.url.href.match(regex)),
17 | new StaleWhileRevalidate(),
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/src/theme/BlogArchivePage/styles.module.css:
--------------------------------------------------------------------------------
1 | .archiveCount {
2 | text-align: right;
3 | margin-top: -2.5rem;
4 | font-size: 0.85rem;
5 | opacity: 0.8;
6 | }
7 |
8 | .archive {
9 | max-width: 860px;
10 | margin: 0 auto;
11 | padding: 1rem 2.5rem;
12 |
13 | box-shadow: 0 1px 2px 0 rgb(0 0 0 / 10%);
14 | margin-bottom: 1rem;
15 | border-radius: 2px;
16 | background: var(--card-background);
17 | }
18 |
19 | .archiveTitle {
20 | display: inline-flex;
21 | align-items: center;
22 | }
23 |
24 | .archiveYear {
25 | position: relative;
26 | display: inline-flex;
27 | justify-content: space-between;
28 | align-items: flex-end;
29 | margin: 1rem 0 1rem 0.8rem;
30 | width: 100%;
31 | font-weight: 400;
32 | font-size: 1.4rem;
33 | }
34 |
35 | .archiveYear::after {
36 | content: '';
37 | position: absolute;
38 | width: 100%;
39 | height: 2px;
40 | bottom: -1rem;
41 | left: -0.8rem;
42 | background: var(--ifm-color-primary);
43 | }
44 |
45 | .archiveYear span {
46 | margin-right: 1rem;
47 | font-size: 0.85rem;
48 | font-weight: 300;
49 | color: var(--ifm-secondary-text-color);
50 | }
51 |
52 | .archiveList {
53 | padding: 0;
54 | margin: 0;
55 | }
56 |
57 | .archiveItem {
58 | position: relative;
59 | display: flex;
60 | padding: 0.4rem 0;
61 | border-bottom: 1px solid var(--ifm-color-primary-light);
62 | list-style: none;
63 | }
64 |
65 | .archiveItem::before {
66 | content: '';
67 | position: absolute;
68 | width: 2px;
69 | top: 0;
70 | height: 100%;
71 | background: rgb(0 120 231 / 30%);
72 | }
73 |
74 | .archiveItem a {
75 | display: flex;
76 | align-items: center;
77 | color: var(--ifm-text-color);
78 | padding: 0.4rem 1rem;
79 | font-size: 1.2rem;
80 | font-weight: 500;
81 | font-family: var(--ifm-font-family-base);
82 |
83 | text-decoration: none;
84 | transition: padding 0.3s;
85 | }
86 |
87 | .archiveItem a::before {
88 | content: '';
89 | position: absolute;
90 | left: 0;
91 | top: 1.5rem;
92 | width: 0.5rem;
93 | height: 0.5rem;
94 | margin-left: -4px;
95 | border-radius: 50%;
96 | border: 1px solid rgb(0 120 231);
97 | background-color: rgb(255 255 255);
98 | z-index: 1;
99 | transition-duration: 0.3s;
100 | transition-delay: 0s;
101 | transition-property: background;
102 | }
103 |
104 | .archiveItem a:hover::before {
105 | background: rgb(0 120 231);
106 | }
107 |
108 | .archiveTime {
109 | opacity: 0.6;
110 | font-size: 0.85rem;
111 | font-weight: 400;
112 | margin-right: 0.5rem;
113 | width: 2.5rem;
114 | position: relative;
115 | color: var(--hty-secondary-text-color);
116 |
117 | font-family: 'Source Code Pro', Consolas, Monaco, SFMono-Regular,
118 | 'Ubuntu Mono', Menlo, monospace;
119 | }
120 |
121 | .archiveItem a > span:hover {
122 | color: var(--ifm-color-primary);
123 | }
124 |
--------------------------------------------------------------------------------
/src/theme/BlogListPage/useViewType.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect, useState } from 'react'
2 |
3 | type ViewType = 'list' | 'grid' | 'card'
4 |
5 | export function useViewType() {
6 | const [viewType, setViewType] = useState('card')
7 |
8 | useEffect(() => {
9 | setViewType((localStorage.getItem('viewType') as ViewType) || 'card')
10 | }, [])
11 |
12 | const toggleViewType = useCallback((newViewType: ViewType) => {
13 | setViewType(newViewType)
14 | localStorage.setItem('viewType', newViewType)
15 | }, [])
16 |
17 | return {
18 | viewType,
19 | toggleViewType,
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/theme/BlogPostItem/Container/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useBaseUrlUtils } from '@docusaurus/useBaseUrl'
3 | import { useBlogPost } from '@docusaurus/theme-common/internal'
4 | import type { Props } from '@theme/BlogPostItem/Container'
5 |
6 | export default function BlogPostItemContainer({
7 | children,
8 | className,
9 | }: Props): JSX.Element {
10 | const { frontMatter, assets } = useBlogPost()
11 | const { withBaseUrl } = useBaseUrlUtils()
12 | const image = assets.image ?? frontMatter.image
13 | return (
14 |
20 | {image && (
21 |
25 | )}
26 | {children}
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/src/theme/BlogPostItem/Container/styles.module.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/src/theme/BlogPostItem/Container/styles.module.css
--------------------------------------------------------------------------------
/src/theme/BlogPostItem/Content/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import clsx from 'clsx'
3 | import { blogPostContainerID } from '@docusaurus/utils-common'
4 | import { useBlogPost } from '@docusaurus/theme-common/internal'
5 | import MDXContent from '@theme/MDXContent'
6 | import type { Props } from '@theme/BlogPostItem/Content'
7 |
8 | export default function BlogPostItemContent({
9 | children,
10 | className,
11 | }: Props): JSX.Element {
12 | const { isBlogPostPage } = useBlogPost()
13 | return (
14 |
20 | {children}
21 |
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/src/theme/BlogPostItem/Footer/ReadMoreLink/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Translate, { translate } from '@docusaurus/Translate'
3 | import Link from '@docusaurus/Link'
4 | import type { Props } from '@theme/BlogPostItem/Footer/ReadMoreLink'
5 |
6 | function ReadMoreLabel() {
7 | return (
8 |
9 |
13 | Read More
14 |
15 |
16 | )
17 | }
18 |
19 | export default function BlogPostItemFooterReadMoreLink(
20 | props: Props,
21 | ): JSX.Element {
22 | const { blogPostTitle, ...linkProps } = props
23 | return (
24 |
36 |
37 |
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/src/theme/BlogPostItem/Footer/styles.module.css:
--------------------------------------------------------------------------------
1 | .blogPostFooterDetailsFull {
2 | flex-direction: column;
3 | }
4 |
5 | .blogPostInfo {
6 | margin-top: 0.5em;
7 | display: flex;
8 | flex-wrap: wrap;
9 | align-items: center;
10 | margin-bottom: var(--ifm-spacing-m);
11 | gap: 4px;
12 | color: rgb(156 163 175);
13 | }
14 |
15 | .blogPostInfoTags {
16 | display: flex;
17 | gap: 4px;
18 | }
19 |
20 | .blogPostInfoTags > a {
21 | font-size: 0.8em;
22 | padding: 1px 5px;
23 | }
24 |
25 | .blogPostAuthor {
26 | display: inline-block;
27 | margin: 0 2px;
28 | font-size: 0.95rem;
29 | font-weight: 400;
30 | color: inherit;
31 | }
32 |
33 | .blogPostAuthor:hover {
34 | text-decoration: none;
35 | }
36 |
37 | .blogPostDate,
38 | .blogPostReadTime {
39 | font-size: 0.9rem;
40 | }
41 |
42 | .blogPostDetailsFull {
43 | flex-direction: column;
44 | }
45 |
46 | .divider {
47 | background-color: #eaecef;
48 | border: 0;
49 | height: var(--ifm-hr-height);
50 | margin: 0.25rem 0;
51 | }
52 |
53 | html[data-theme='dark'] .divider {
54 | background-color: #2f3336;
55 | }
56 |
--------------------------------------------------------------------------------
/src/theme/BlogPostItem/Header/Author/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import clsx from 'clsx'
3 | import Link, { type Props as LinkProps } from '@docusaurus/Link'
4 |
5 | import type { Props } from '@theme/BlogPostItem/Header/Author'
6 |
7 | function MaybeLink(props: LinkProps): JSX.Element {
8 | if (props.href) {
9 | return
10 | }
11 | return <>{props.children}>
12 | }
13 |
14 | export default function BlogPostItemHeaderAuthor({
15 | author,
16 | className,
17 | }: Props): JSX.Element {
18 | const { name, title, url, imageURL, email } = author
19 | const link = url || (email && `mailto:${email}`) || undefined
20 | return (
21 |
22 | {imageURL && (
23 |
24 |
25 |
26 | )}
27 |
28 | {name && (
29 |
35 |
36 |
37 | {name}
38 |
39 |
40 | {title && (
41 |
42 | {title}
43 |
44 | )}
45 |
46 | )}
47 |
48 | )
49 | }
50 |
--------------------------------------------------------------------------------
/src/theme/BlogPostItem/Header/Authors/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import clsx from 'clsx'
3 | import { useBlogPost } from '@docusaurus/theme-common/internal'
4 | import BlogPostItemHeaderAuthor from '@theme/BlogPostItem/Header/Author'
5 | import type { Props } from '@theme/BlogPostItem/Header/Authors'
6 | import styles from './styles.module.css'
7 |
8 | // Component responsible for the authors layout
9 | export default function BlogPostItemHeaderAuthors({
10 | className,
11 | }: Props): JSX.Element | null {
12 | const {
13 | metadata: { authors },
14 | assets,
15 | } = useBlogPost()
16 | const authorsCount = authors.length
17 | if (authorsCount === 0) {
18 | return null
19 | }
20 | const imageOnly = authors.every(({ name }) => !name)
21 | return (
22 |
29 | {authors.map((author, idx) => (
30 |
37 |
44 |
45 | ))}
46 |
47 | )
48 | }
49 |
--------------------------------------------------------------------------------
/src/theme/BlogPostItem/Header/Authors/styles.module.css:
--------------------------------------------------------------------------------
1 | .authorCol {
2 | max-width: inherit !important;
3 | flex-grow: 1 !important;
4 | }
5 |
6 | .imageOnlyAuthorRow {
7 | display: flex;
8 | flex-flow: row wrap;
9 | }
10 |
11 | .imageOnlyAuthorCol {
12 | margin-left: 0.3rem;
13 | margin-right: 0.3rem;
14 | }
15 |
--------------------------------------------------------------------------------
/src/theme/BlogPostItem/Header/Info/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import clsx from 'clsx'
3 | import { translate } from '@docusaurus/Translate'
4 | import { usePluralForm } from '@docusaurus/theme-common'
5 | import { useBlogPost } from '@docusaurus/theme-common/internal'
6 | import type { Props } from '@theme/BlogPostItem/Header/Info'
7 |
8 | import styles from './styles.module.css'
9 |
10 | // Very simple pluralization: probably good enough for now
11 | function useReadingTimePlural() {
12 | const { selectMessage } = usePluralForm()
13 | return (readingTimeFloat: number) => {
14 | const readingTime = Math.ceil(readingTimeFloat)
15 | return selectMessage(
16 | readingTime,
17 | translate(
18 | {
19 | id: 'theme.blog.post.readingTime.plurals',
20 | description:
21 | 'Pluralized label for "{readingTime} min read". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)',
22 | message: 'One min read|{readingTime} min read',
23 | },
24 | { readingTime },
25 | ),
26 | )
27 | }
28 | }
29 |
30 | export function ReadingTime({ readingTime }: { readingTime: number }) {
31 | const readingTimePlural = useReadingTimePlural()
32 | return <>{readingTimePlural(readingTime)}>
33 | }
34 |
35 | function Date({
36 | date,
37 | formattedDate,
38 | }: {
39 | date: string
40 | formattedDate: string
41 | }) {
42 | return (
43 |
46 | )
47 | }
48 |
49 | function Spacer() {
50 | return <>{' · '}>
51 | }
52 |
53 | export default function BlogPostItemHeaderInfo({
54 | className,
55 | }: Props): JSX.Element {
56 | const { metadata } = useBlogPost()
57 | const { date, formattedDate, readingTime } = metadata
58 |
59 | return (
60 |
61 |
62 | {typeof readingTime !== 'undefined' && (
63 | <>
64 |
65 |
66 | >
67 | )}
68 |
69 | )
70 | }
71 |
--------------------------------------------------------------------------------
/src/theme/BlogPostItem/Header/Info/styles.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | font-size: 0.9rem;
3 | }
4 |
--------------------------------------------------------------------------------
/src/theme/BlogPostItem/Header/Title/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import clsx from 'clsx'
3 | import Link from '@docusaurus/Link'
4 | import { useBlogPost } from '@docusaurus/theme-common/internal'
5 | import type { Props } from '@theme/BlogPostItem/Header/Title'
6 |
7 | import styles from './styles.module.css'
8 |
9 | export default function BlogPostItemHeaderTitle({
10 | className,
11 | }: Props): JSX.Element {
12 | const { metadata, isBlogPostPage } = useBlogPost()
13 | const { permalink, title } = metadata
14 | const TitleHeading = isBlogPostPage ? 'h1' : 'h2'
15 | return (
16 |
17 | {isBlogPostPage ? (
18 | title
19 | ) : (
20 |
21 | {title}
22 |
23 | )}
24 |
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/src/theme/BlogPostItem/Header/Title/styles.module.css:
--------------------------------------------------------------------------------
1 | .titleLink {
2 | position: relative;
3 | font-weight: 500;
4 | color: var(--ifm-heading-color);
5 | }
6 |
7 | .titleLink:hover {
8 | color: var(--ifm-link-hover-color);
9 | text-decoration: none;
10 | }
11 |
12 | .titleLink:hover::after {
13 | visibility: visible;
14 | transform: scaleX(1);
15 | }
16 |
17 | .titleLink::after {
18 | content: '';
19 | position: absolute;
20 | bottom: 0;
21 | left: 0;
22 | width: 100%;
23 | height: 2px;
24 | background: var(--ifm-color-primary);
25 | visibility: hidden;
26 | transition: all 0.3s linear;
27 | transform: scaleX(0);
28 | }
29 |
--------------------------------------------------------------------------------
/src/theme/BlogPostItem/Header/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import BlogPostItemHeaderTitle from '@theme/BlogPostItem/Header/Title'
3 | import BlogPostItemHeaderInfo from '@theme/BlogPostItem/Header/Info'
4 | import BlogPostItemHeaderAuthors from '@theme/BlogPostItem/Header/Authors'
5 | import { useBlogPost } from '@docusaurus/theme-common/internal'
6 |
7 | export default function BlogPostItemHeader(): JSX.Element {
8 | const { isBlogPostPage } = useBlogPost()
9 | return (
10 |
11 |
12 | {isBlogPostPage && (
13 | <>
14 |
15 |
16 | >
17 | )}
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/src/theme/BlogPostItem/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import clsx from 'clsx'
3 | import { useBlogPost } from '@docusaurus/theme-common/internal'
4 | import BlogPostItemContainer from '@theme/BlogPostItem/Container'
5 | import BlogPostItemHeader from '@theme/BlogPostItem/Header'
6 | import BlogPostItemContent from '@theme/BlogPostItem/Content'
7 | import BlogPostItemFooter from '@theme/BlogPostItem/Footer'
8 | import type { Props } from '@theme/BlogPostItem'
9 |
10 | // apply a bottom margin in list view
11 | function useContainerClassName() {
12 | const { isBlogPostPage } = useBlogPost()
13 | return !isBlogPostPage ? 'blogPost-container margin-bottom--lg' : undefined
14 | }
15 |
16 | export default function BlogPostItem({
17 | children,
18 | className,
19 | }: Props): JSX.Element {
20 | const containerClassName = useContainerClassName()
21 | return (
22 |
23 |
24 | {children}
25 |
26 |
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/src/theme/BlogPostItems/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { BlogPostProvider } from '@docusaurus/theme-common/internal'
3 | import BlogPostItem from '@theme/BlogPostItem'
4 | import type { Props } from '@theme/BlogPostItems'
5 | import { Fade } from 'react-awesome-reveal'
6 |
7 | export default function BlogPostItems({
8 | items,
9 | component: BlogPostItemComponent = BlogPostItem,
10 | }: Props): JSX.Element {
11 | return (
12 | <>
13 | {items.map(({ content: BlogPostContent }) => (
14 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | ))}
25 | >
26 | )
27 | }
28 |
--------------------------------------------------------------------------------
/src/theme/BlogPostPage/Metadata/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { PageMetadata } from '@docusaurus/theme-common'
3 | import { useBlogPost } from '@docusaurus/theme-common/internal'
4 |
5 | export default function BlogPostPageMetadata(): JSX.Element {
6 | const { assets, metadata } = useBlogPost()
7 | const { title, description, date, tags, authors, frontMatter } = metadata
8 |
9 | const { keywords } = frontMatter
10 | const image = assets.image ?? frontMatter.image
11 | return (
12 |
18 |
19 |
20 | {/* TODO double check those article meta array syntaxes, see https://ogp.me/#array */}
21 | {authors.some(author => author.url) && (
22 | author.url)
26 | .filter(Boolean)
27 | .join(',')}
28 | />
29 | )}
30 | {tags.length > 0 && (
31 | tag.label).join(',')}
34 | />
35 | )}
36 |
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/src/theme/BlogPostPage/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { type ReactNode } from 'react'
2 | import clsx from 'clsx'
3 | import {
4 | HtmlClassNameProvider,
5 | ThemeClassNames,
6 | } from '@docusaurus/theme-common'
7 | import {
8 | BlogPostProvider,
9 | useBlogPost,
10 | } from '@docusaurus/theme-common/internal'
11 | import BlogLayout from '@theme/BlogLayout'
12 | import BlogPostItem from '@theme/BlogPostItem'
13 | import BlogPostPaginator from '@theme/BlogPostPaginator'
14 | import BlogPostPageMetadata from '@theme/BlogPostPage/Metadata'
15 | import BackToTopButton from '@theme/BackToTopButton'
16 | import TOC from '@theme/TOC'
17 | import type { Props } from '@theme/BlogPostPage'
18 | import type { BlogSidebar } from '@docusaurus/plugin-content-blog'
19 | import Comment from '@site/src/components/Comment'
20 |
21 | function BlogPostPageContent({
22 | sidebar,
23 | children,
24 | }: {
25 | sidebar: BlogSidebar
26 | children: ReactNode
27 | }): JSX.Element {
28 | const { metadata, toc } = useBlogPost()
29 | const { nextItem, prevItem, frontMatter } = metadata
30 | const {
31 | hide_table_of_contents: hideTableOfContents,
32 | toc_min_heading_level: tocMinHeadingLevel,
33 | toc_max_heading_level: tocMaxHeadingLevel,
34 | hide_comment: hideComment,
35 | } = frontMatter
36 |
37 | return (
38 | 0 ? (
42 |
47 | ) : undefined
48 | }
49 | >
50 | {children}
51 |
52 | {(nextItem || prevItem) && (
53 |
54 | )}
55 | {!hideComment && }
56 |
57 |
58 | )
59 | }
60 |
61 | export default function BlogPostPage(props: Props): JSX.Element {
62 | const BlogPostContent = props.content
63 | return (
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | )
78 | }
79 |
--------------------------------------------------------------------------------
/src/theme/BlogSidebar/Desktop/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import clsx from 'clsx'
3 | import Link from '@docusaurus/Link'
4 | import { translate } from '@docusaurus/Translate'
5 | import type { Props } from '@theme/BlogSidebar/Desktop'
6 |
7 | import styles from './styles.module.css'
8 |
9 | export default function BlogSidebarDesktop({ sidebar }: Props): JSX.Element {
10 | return (
11 |
39 | )
40 | }
41 |
--------------------------------------------------------------------------------
/src/theme/BlogSidebar/Desktop/styles.module.css:
--------------------------------------------------------------------------------
1 | .sidebar {
2 | max-height: calc(100vh - (var(--ifm-navbar-height) + 2rem));
3 | overflow-y: auto;
4 | position: sticky;
5 | top: calc(var(--ifm-navbar-height) + 2rem);
6 | }
7 |
8 | .sidebarItemTitle {
9 | font-size: var(--ifm-h3-font-size);
10 | font-weight: var(--ifm-font-weight-bold);
11 | font-family: var(--ifm-heading-font-family);
12 | }
13 |
14 | .sidebarItemList {
15 | font-size: 0.9rem;
16 | }
17 |
18 | .sidebarItem {
19 | margin-top: 0.7rem;
20 | }
21 |
22 | .sidebarItemLink {
23 | color: var(--ifm-font-color-base);
24 | display: block;
25 | }
26 |
27 | .sidebarItemLink:hover {
28 | text-decoration: none;
29 | }
30 |
31 | .sidebarItemLinkActive {
32 | color: var(--ifm-color-primary) !important;
33 | }
34 |
35 | @media (max-width: 996px) {
36 | .sidebar {
37 | display: none;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/theme/BlogSidebar/Mobile/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Link from '@docusaurus/Link'
3 | import { NavbarSecondaryMenuFiller } from '@docusaurus/theme-common'
4 | import type { Props } from '@theme/BlogSidebar/Mobile'
5 |
6 | function BlogSidebarMobileSecondaryMenu({ sidebar }: Props): JSX.Element {
7 | return (
8 |
9 | {sidebar.items.map(item => (
10 | -
11 |
17 | {item.title}
18 |
19 |
20 | ))}
21 |
22 | )
23 | }
24 |
25 | export default function BlogSidebarMobile(props: Props): JSX.Element {
26 | return (
27 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/src/theme/BlogSidebar/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useWindowSize } from '@docusaurus/theme-common'
3 | import BlogSidebarDesktop from '@theme/BlogSidebar/Desktop'
4 | import BlogSidebarMobile from '@theme/BlogSidebar/Mobile'
5 | import type { Props } from '@theme/BlogSidebar'
6 |
7 | export default function BlogSidebar({ sidebar }: Props): JSX.Element | null {
8 | const windowSize = useWindowSize()
9 | if (!sidebar?.items.length) {
10 | return null
11 | }
12 | // Mobile sidebar doesn't need to be server-rendered
13 | if (windowSize === 'mobile') {
14 | return
15 | }
16 | return
17 | }
18 |
--------------------------------------------------------------------------------
/src/theme/BlogTagsListPage/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import clsx from 'clsx'
3 | import {
4 | PageMetadata,
5 | HtmlClassNameProvider,
6 | ThemeClassNames,
7 | translateTagsPageTitle,
8 | } from '@docusaurus/theme-common'
9 | import BlogLayout from '@theme/BlogLayout'
10 | import TagsListByLetter from '@theme/TagsListByLetter'
11 | import { TagsListByFlat } from '../TagsListByLetter'
12 | import type { Props } from '@theme/BlogTagsListPage'
13 | import SearchMetadata from '@theme/SearchMetadata'
14 | import { Icon } from '@iconify/react'
15 |
16 | export default function BlogTagsListPage({
17 | tags,
18 | sidebar,
19 | }: Props): JSX.Element {
20 | const title = translateTagsPageTitle()
21 |
22 | const [type, setType] = useState<'list' | 'grid'>('list')
23 |
24 | return (
25 |
31 |
32 |
33 |
34 |
35 |
{title}
36 |
37 | setType('list')}
42 | color={type === 'list' ? 'var(--ifm-color-primary)' : '#ccc'}
43 | />
44 | setType('grid')}
49 | color={type === 'grid' ? 'var(--ifm-color-primary)' : '#ccc'}
50 | />
51 |
52 |
53 | {type === 'list' && }
54 | {type === 'grid' && }
55 |
56 |
57 | )
58 | }
59 |
--------------------------------------------------------------------------------
/src/theme/DocTagsListPage/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import clsx from 'clsx'
3 | import {
4 | PageMetadata,
5 | HtmlClassNameProvider,
6 | ThemeClassNames,
7 | translateTagsPageTitle,
8 | } from '@docusaurus/theme-common'
9 | import Layout from '@theme/Layout'
10 | import TagsListByLetter from '@theme/TagsListByLetter'
11 | import SearchMetadata from '@theme/SearchMetadata'
12 | import type { Props } from '@theme/DocTagsListPage'
13 | import { Icon } from '@iconify/react'
14 |
15 | import { TagsListByFlat } from '../TagsListByLetter'
16 |
17 | export default function DocTagsListPage({ tags }: Props): JSX.Element {
18 | const title = translateTagsPageTitle()
19 |
20 | const [type, setType] = useState('letter')
21 |
22 | return (
23 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
{title}
37 |
38 |
39 | setType('list')}
44 | color={
45 | type === 'list' ? 'var(--ifm-color-primary)' : '#ccc'
46 | }
47 | />
48 | setType('grid')}
53 | color={
54 | type === 'grid' ? 'var(--ifm-color-primary)' : '#ccc'
55 | }
56 | />
57 |
58 |
59 |
60 | {type === 'letter' && }
61 | {type === 'flat' && }
62 |
63 |
64 |
65 |
66 |
67 | )
68 | }
69 |
--------------------------------------------------------------------------------
/src/theme/MDXPage/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import clsx from 'clsx'
3 | import {
4 | PageMetadata,
5 | HtmlClassNameProvider,
6 | ThemeClassNames,
7 | } from '@docusaurus/theme-common'
8 | import Layout from '@theme/Layout'
9 | import MDXContent from '@theme/MDXContent'
10 | import TOC from '@theme/TOC'
11 | import type { Props } from '@theme/MDXPage'
12 |
13 | import styles from './styles.module.css'
14 |
15 | export default function MDXPage(props: Props): JSX.Element {
16 | const { content: MDXPageContent } = props
17 | const {
18 | metadata: { title, description, frontMatter },
19 | } = MDXPageContent
20 | const { wrapperClassName, hide_table_of_contents: hideTableOfContents } =
21 | frontMatter
22 |
23 | return (
24 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | {!hideTableOfContents && MDXPageContent.toc && (
40 |
41 |
46 |
47 | )}
48 |
49 |
50 |
51 |
52 | )
53 | }
54 |
--------------------------------------------------------------------------------
/src/theme/MDXPage/styles.module.css:
--------------------------------------------------------------------------------
1 | .mdxPageWrapper {
2 | justify-content: center;
3 | }
4 |
--------------------------------------------------------------------------------
/src/theme/Navbar/MobileSidebar/Layout/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import clsx from 'clsx'
3 | import { useNavbarSecondaryMenu } from '@docusaurus/theme-common/internal'
4 | import type { Props } from '@theme/Navbar/MobileSidebar/Layout'
5 | import { BlogUser } from '@site/src/components/BlogInfo/index'
6 |
7 | export default function NavbarMobileSidebarLayout({
8 | header,
9 | primaryMenu,
10 | secondaryMenu,
11 | }: Props): JSX.Element {
12 | const { shown: secondaryMenuShown } = useNavbarSecondaryMenu()
13 | return (
14 |
15 | {header}
16 |
17 |
22 |
{primaryMenu}
23 |
{secondaryMenu}
24 |
25 |
26 | )
27 | }
28 |
--------------------------------------------------------------------------------
/src/theme/TagsListByLetter/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | listTagsByLetters,
4 | type TagLetterEntry,
5 | } from '@docusaurus/theme-common'
6 | import Tag from '@theme/Tag'
7 | import type { Props } from '@theme/TagsListByLetter'
8 |
9 | import styles from './styles.module.css'
10 |
11 | function TagLetterEntryItem({ letterEntry }: { letterEntry: TagLetterEntry }) {
12 | return (
13 |
14 | {letterEntry.letter}
15 |
16 | {letterEntry.tags.map(tag => (
17 | -
18 |
19 |
20 | ))}
21 |
22 |
23 |
24 | )
25 | }
26 |
27 | export default function TagsListByLetter({ tags }: Props): JSX.Element {
28 | const letterList = listTagsByLetters(tags)
29 | return (
30 |
31 | {letterList.map(letterEntry => (
32 |
36 | ))}
37 |
38 | )
39 | }
40 |
41 | export function TagsListByFlat({ tags }: Props): JSX.Element {
42 | console.log(tags)
43 | return (
44 |
45 |
46 | {tags.map(tag => (
47 | -
48 |
49 |
50 | ))}
51 |
52 |
53 | )
54 | }
55 |
--------------------------------------------------------------------------------
/src/theme/TagsListByLetter/styles.module.css:
--------------------------------------------------------------------------------
1 | .tag {
2 | display: inline-block;
3 | margin: 0.5rem 0.5rem 0 1rem;
4 | }
5 |
--------------------------------------------------------------------------------
/src/types.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/utils/jsUtils.ts:
--------------------------------------------------------------------------------
1 | // Inspired by https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_difference
2 | export function difference(...arrays: T[][]): T[] {
3 | return arrays.reduce((a, b) => a.filter(c => !b.includes(c)))
4 | }
5 |
6 | // Inspired by https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_sortby-and-_orderby
7 | export function sortBy(
8 | array: T[],
9 | getter: (item: T) => string | number | boolean,
10 | ): T[] {
11 | const sortedArray = [...array]
12 | sortedArray.sort((a, b) =>
13 | getter(a) > getter(b) ? 1 : getter(b) > getter(a) ? -1 : 0,
14 | )
15 | return sortedArray
16 | }
17 |
18 | export function toggleListItem(list: T[], item: T): T[] {
19 | const itemIndex = list.indexOf(item)
20 | if (itemIndex === -1) {
21 | return list.concat(item)
22 | } else {
23 | const newList = [...list]
24 | newList.splice(itemIndex, 1)
25 | return newList
26 | }
27 | }
28 |
29 | export function shuffle(arr) {
30 | let i = arr.length
31 |
32 | while (i) {
33 | const j = Math.floor(Math.random() * i--)
34 |
35 | ;[arr[j], arr[i]] = [arr[i], arr[j]]
36 | }
37 |
38 | return arr
39 | }
40 |
--------------------------------------------------------------------------------
/static/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | RewriteEngine On
3 | RewriteCond %{HTTPS} off [OR]
4 | RewriteCond %{HTTP_HOST} ^www\. [NC]
5 | RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
6 | RewriteRule ^ https://%1%{REQUEST_URI} [L,NE,R=301]
7 |
8 |
9 | # Compress HTML, CSS, JavaScript, Text, XML and fonts
10 | AddOutputFilterByType DEFLATE application/javascript
11 | AddOutputFilterByType DEFLATE application/rss+xml
12 | AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
13 | AddOutputFilterByType DEFLATE application/x-font
14 | AddOutputFilterByType DEFLATE application/x-font-opentype
15 | AddOutputFilterByType DEFLATE application/x-font-otf
16 | AddOutputFilterByType DEFLATE application/x-font-truetype
17 | AddOutputFilterByType DEFLATE application/x-font-ttf
18 | AddOutputFilterByType DEFLATE application/x-javascript
19 | AddOutputFilterByType DEFLATE application/xhtml+xml
20 | AddOutputFilterByType DEFLATE application/xml
21 | AddOutputFilterByType DEFLATE font/opentype
22 | AddOutputFilterByType DEFLATE font/otf
23 | AddOutputFilterByType DEFLATE font/ttf
24 | AddOutputFilterByType DEFLATE image/svg+xml
25 | AddOutputFilterByType DEFLATE image/x-icon
26 | AddOutputFilterByType DEFLATE text/css
27 | AddOutputFilterByType DEFLATE text/html
28 | AddOutputFilterByType DEFLATE text/javascript
29 | AddOutputFilterByType DEFLATE text/plain
30 | AddOutputFilterByType DEFLATE text/xml
31 |
32 | # Remove browser bugs (only needed for ancient browsers)
33 | BrowserMatch ^Mozilla/4 gzip-only-text/html
34 | BrowserMatch ^Mozilla/4\.0[678] no-gzip
35 | BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
36 | Header append Vary User-Agent
37 |
38 | # cache
39 | AddType application/x-font-woff .woff
40 | AddType video/mp4 .mp4 .m4v
41 |
42 | ExpiresActive on
43 | # Set the default expiry times.
44 | ExpiresDefault "access plus 2 days"
45 | ExpiresByType image/jpg "access plus 1 month"
46 | ExpiresByType image/svg+xml "access 1 month"
47 | ExpiresByType image/gif "access plus 1 month"
48 | ExpiresByType image/jpeg "access plus 1 month"
49 | ExpiresByType image/png "access plus 1 month"
50 | ExpiresByType image/webp "access plus 1 month"
51 | ExpiresByType video/mp4 "access plus 1 month"
52 | ExpiresByType font/opentype "access plus 1 month"
53 | ExpiresByType font/otf "access plus 1 month"
54 | ExpiresByType font/ttf "access plus 1 month"
55 | ExpiresByType application/x-font-woff "access plus 1 month"
56 | ExpiresByType application/font-woff2 "access plus 1 month"
57 | ExpiresByType text/css "access plus 1 month"
58 | ExpiresByType text/javascript "access plus 1 month"
59 | ExpiresByType application/javascript "access plus 1 month"
60 | ExpiresByType application/x-shockwave-flash "access plus 1 month"
61 | ExpiresByType image/ico "access plus 1 month"
62 | ExpiresByType image/x-icon "access plus 1 month"
63 | ExpiresByType text/html "access plus 600 seconds"
--------------------------------------------------------------------------------
/static/img/buildwith.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/buildwith.png
--------------------------------------------------------------------------------
/static/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/favicon.ico
--------------------------------------------------------------------------------
/static/img/icons/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/icons/favicon.ico
--------------------------------------------------------------------------------
/static/img/icons/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/icons/icon-128.png
--------------------------------------------------------------------------------
/static/img/icons/icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/icons/icon-16.png
--------------------------------------------------------------------------------
/static/img/icons/icon-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/icons/icon-256.png
--------------------------------------------------------------------------------
/static/img/icons/icon-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/icons/icon-32.png
--------------------------------------------------------------------------------
/static/img/icons/icon-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/icons/icon-48.png
--------------------------------------------------------------------------------
/static/img/icons/icon-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/icons/icon-64.png
--------------------------------------------------------------------------------
/static/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/logo.png
--------------------------------------------------------------------------------
/static/img/logo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/logo.webp
--------------------------------------------------------------------------------
/static/img/resource/antv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/antv.png
--------------------------------------------------------------------------------
/static/img/resource/any-rule.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/any-rule.ico
--------------------------------------------------------------------------------
/static/img/resource/apifox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/apifox.png
--------------------------------------------------------------------------------
/static/img/resource/atoolbox.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/atoolbox.ico
--------------------------------------------------------------------------------
/static/img/resource/axios.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/axios.ico
--------------------------------------------------------------------------------
/static/img/resource/bilibili.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/bilibili.ico
--------------------------------------------------------------------------------
/static/img/resource/bootcdn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/bootcdn.png
--------------------------------------------------------------------------------
/static/img/resource/coding.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/coding.png
--------------------------------------------------------------------------------
/static/img/resource/component party.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/img/resource/coolify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/coolify.png
--------------------------------------------------------------------------------
/static/img/resource/coolors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/coolors.png
--------------------------------------------------------------------------------
/static/img/resource/css-inspiration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/css-inspiration.png
--------------------------------------------------------------------------------
/static/img/resource/cssfx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/cssfx.png
--------------------------------------------------------------------------------
/static/img/resource/cypress.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/cypress.png
--------------------------------------------------------------------------------
/static/img/resource/dbyun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/dbyun.png
--------------------------------------------------------------------------------
/static/img/resource/digitalocean.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/digitalocean.png
--------------------------------------------------------------------------------
/static/img/resource/electron.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/electron.ico
--------------------------------------------------------------------------------
/static/img/resource/es6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/es6.png
--------------------------------------------------------------------------------
/static/img/resource/figma.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/figma.png
--------------------------------------------------------------------------------
/static/img/resource/fresh.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/fresh.ico
--------------------------------------------------------------------------------
/static/img/resource/gitee.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/gitee.ico
--------------------------------------------------------------------------------
/static/img/resource/github.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/github.ico
--------------------------------------------------------------------------------
/static/img/resource/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/github.png
--------------------------------------------------------------------------------
/static/img/resource/google_fonts.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/google_fonts.ico
--------------------------------------------------------------------------------
/static/img/resource/graphQL.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/img/resource/hoppscotch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/hoppscotch.png
--------------------------------------------------------------------------------
/static/img/resource/igoutu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/igoutu.png
--------------------------------------------------------------------------------
/static/img/resource/javascript.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/img/resource/jest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/jest.png
--------------------------------------------------------------------------------
/static/img/resource/jsdelivr.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/jsdelivr.webp
--------------------------------------------------------------------------------
/static/img/resource/juejin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/juejin.png
--------------------------------------------------------------------------------
/static/img/resource/loading.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/loading.ico
--------------------------------------------------------------------------------
/static/img/resource/mdn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/mdn.png
--------------------------------------------------------------------------------
/static/img/resource/naiveUI.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/img/resource/namae.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/namae.png
--------------------------------------------------------------------------------
/static/img/resource/netlify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/netlify.png
--------------------------------------------------------------------------------
/static/img/resource/nuxt.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/static/img/resource/ossinsight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/ossinsight.png
--------------------------------------------------------------------------------
/static/img/resource/prisma.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/prisma.png
--------------------------------------------------------------------------------
/static/img/resource/quick reference.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/img/resource/railway.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/railway.png
--------------------------------------------------------------------------------
/static/img/resource/remix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/remix.png
--------------------------------------------------------------------------------
/static/img/resource/roadmap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/roadmap.png
--------------------------------------------------------------------------------
/static/img/resource/runoob.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/runoob.png
--------------------------------------------------------------------------------
/static/img/resource/rust-logo-blk.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/img/resource/rust.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/img/resource/shields.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/shields.png
--------------------------------------------------------------------------------
/static/img/resource/stackblitz.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/stackblitz.png
--------------------------------------------------------------------------------
/static/img/resource/strapi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/strapi.png
--------------------------------------------------------------------------------
/static/img/resource/supabase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/supabase.png
--------------------------------------------------------------------------------
/static/img/resource/swc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/swc.png
--------------------------------------------------------------------------------
/static/img/resource/swr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/swr.png
--------------------------------------------------------------------------------
/static/img/resource/taro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/taro.png
--------------------------------------------------------------------------------
/static/img/resource/terminalgif.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/terminalgif.ico
--------------------------------------------------------------------------------
/static/img/resource/twind.svg:
--------------------------------------------------------------------------------
1 |
63 |
--------------------------------------------------------------------------------
/static/img/resource/typeorm.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/typeorm.ico
--------------------------------------------------------------------------------
/static/img/resource/typescript.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/typescript.png
--------------------------------------------------------------------------------
/static/img/resource/typing-svg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/typing-svg.png
--------------------------------------------------------------------------------
/static/img/resource/uiverse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/uiverse.png
--------------------------------------------------------------------------------
/static/img/resource/vben-admin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/vben-admin.png
--------------------------------------------------------------------------------
/static/img/resource/vite.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/static/img/resource/webgradients.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/webgradients.png
--------------------------------------------------------------------------------
/static/img/resource/webpack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/webpack.png
--------------------------------------------------------------------------------
/static/img/resource/zhubai.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xxsoftware/blog/b3fea6bebbd27dd914ae1b495a09923e7daa61d3/static/img/resource/zhubai.png
--------------------------------------------------------------------------------
/static/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "東雲研究所的小站",
3 | "short_name": "東雲研究所的小站",
4 | "theme_color": "#12affa",
5 | "background_color": "#424242",
6 | "display": "standalone",
7 | "scope": "./",
8 | "start_url": "./index.html",
9 | "related_applications": [
10 | {
11 | "platform": "webapp",
12 | "url": "https://blog.xxsoftware.fun/manifest.json"
13 | }
14 | ],
15 | "icons": [
16 | {
17 | "src": "img/icons/icon-16.png",
18 | "sizes": "16x16",
19 | "type": "image/png"
20 | },
21 | {
22 | "src": "img/icons/icon-32.png",
23 | "sizes": "32x32",
24 | "type": "image/png"
25 | },
26 | {
27 | "src": "img/icons/icon-48.png",
28 | "sizes": "48x48",
29 | "type": "image/png"
30 | },
31 | {
32 | "src": "img/icons/icon-64.png",
33 | "sizes": "64x64",
34 | "type": "image/png"
35 | },
36 | {
37 | "src": "img/icons/icon-128.png",
38 | "sizes": "128x128",
39 | "type": "image/png"
40 | },
41 | {
42 | "src": "img/icons/icon-256.png",
43 | "sizes": "256x256",
44 | "type": "image/png"
45 | }
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/static/svg/juejin.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | // This file is not used in compilation. It is here just for a nice editor experience.
3 | "extends": "@tsconfig/docusaurus/tsconfig.json",
4 | "compilerOptions": {
5 | "lib": ["DOM", "ESNext"],
6 | "baseUrl": ".",
7 | "resolveJsonModule": true,
8 |
9 | // Duplicated from the root config, because TS does not support extending
10 | // multiple configs and we want to dogfood the @tsconfig/docusaurus one
11 | "allowUnreachableCode": false,
12 | "exactOptionalPropertyTypes": false,
13 | "noFallthroughCasesInSwitch": true,
14 | "noImplicitOverride": true,
15 | "noImplicitReturns": true,
16 | "noPropertyAccessFromIndexSignature": false,
17 | "noUncheckedIndexedAccess": true,
18 | "strict": true,
19 | "alwaysStrict": true,
20 | "noImplicitAny": false,
21 | "noImplicitThis": true,
22 | "strictBindCallApply": true,
23 | "strictFunctionTypes": true,
24 | "strictNullChecks": true,
25 | "strictPropertyInitialization": true,
26 | "useUnknownInCatchVariables": true,
27 | "noUnusedLocals": false,
28 | "noUnusedParameters": false,
29 | "importsNotUsedAsValues": "remove",
30 |
31 | "moduleResolution": "NodeNext",
32 |
33 | // This is important. We run `yarn tsc` in website so we can catch issues
34 | // with our declaration files (mostly names that are forgotten to be
35 | // imported, invalid semantics...). Because we don't have end-to-end type
36 | // tests, removing this would make things much harder to catch.
37 | "skipLibCheck": false,
38 | "types": ["jest"]
39 | },
40 | "exclude": ["src/sw.js"]
41 | }
42 |
--------------------------------------------------------------------------------