diff --git a/.gitignore b/.gitignore index 773bfd6..411fb0d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,37 +1,5 @@ -# Compiled source # -################### -*.com -*.class -*.dll -*.exe -*.o -*.so -# Packages # -############ -# it's better to unpack these files and commit the raw source -# git has its own built in compression methods -*.7z -*.dmg -*.gz -*.iso -*.jar -*.rar -*.tar -*.zip - -# Logs and databases # -###################### -*.log -*.sql -*.sqlite - -# OS generated files # -###################### -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.db +node_modules/ +src/.vuepress/.cache/ +src/.vuepress/.temp/ +src/.vuepress/dist/ diff --git a/README.md b/README.md index b31859b..2e63d3b 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,8 @@ -
+# SongBlog - +个人互联网记忆 -# GitHub Pages +## 访问地址 -_Create a site or blog from your GitHub repositories with GitHub Pages._ +[个人博客网站](https://linzsong.github.io/SongBlog/) -
- - - -## Step 3: Customize your homepage - -_Nice work setting the theme! :sparkles:_ - -You can customize your homepage by adding content to either an `index.md` file or the `README.md` file. GitHub Pages first looks for an `index.md` file. Your repository has an `index.md` file so we can update it to include your personalized content. - -### :keyboard: Activity: Create your homepage - -1. Browse to the `index.md` file in the `my-pages` branch. -1. In the upper right corner, open the file editor. -1. Type the content you want on your homepage. You can use Markdown formatting on this page. -1. (optional) You can also modify `title:` or just ignore it for now. We'll discuss it in the next step. -1. Commit your changes to the `my-pages` branch. -1. Wait about 20 seconds then refresh this page (the one you're following instructions from). [GitHub Actions](https://docs.github.com/en/actions) will automatically update to the next step. - - diff --git a/package.json b/package.json new file mode 100644 index 0000000..65f71c3 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "song", + "version": "1.0.0", + "description": "A project of vuepress-theme-hope", + "license": "MIT", + "scripts": { + "docs:build": "vuepress build src", + "docs:clean-dev": "vuepress dev src --clean-cache", + "docs:dev": "vuepress dev src", + "docs:update-package": "yarn dlx vp-update" + }, + "devDependencies": { + "@vuepress/client": "2.0.0-beta.67", + "vue": "^3.3.4", + "vuepress": "2.0.0-beta.67", + "vuepress-theme-hope": "2.0.0-beta.237" + } +} diff --git a/src/.vuepress/config.ts b/src/.vuepress/config.ts new file mode 100644 index 0000000..3c006b0 --- /dev/null +++ b/src/.vuepress/config.ts @@ -0,0 +1,15 @@ +import { defineUserConfig } from "vuepress"; +import theme from "./theme.js"; + +export default defineUserConfig({ + base: "/SongBlog/", + + lang: "zh-CN", + title: "Song", + description: "Cap的博客", + + theme, + + // Enable it with pwa + // shouldPrefetch: false, +}); diff --git a/src/.vuepress/navbar.ts b/src/.vuepress/navbar.ts new file mode 100644 index 0000000..cad7b51 --- /dev/null +++ b/src/.vuepress/navbar.ts @@ -0,0 +1,58 @@ +import { navbar } from "vuepress-theme-hope"; + +export default navbar([ + // "/", + // "/demo/", + { + text: "博文", + icon: "pen-to-square", + prefix: "/", + children: [ + { + text: "安全", + icon: "", + prefix: "Safety/", + children: [ + { text: "XSS", icon: "", link: "XSS" }, + { text: "CSRF", icon: "", link: "CSRF" }, + ], + }, + { + text: "Node", + icon: "", + prefix: "node/", + children: [ + { + text: "EventLoop", + icon: "", + link: "Event Loop", + }, + '如何在node中更好的使用集群' + ], + }, + // { text: "火龙果", icon: "pen-to-square", link: "dragonfruit" }, + // "tomato", + ], + }, + { + text: "随笔", + icon: "disease", + prefix: "/", + children: [ + { + text: "诗", + icon: "", + prefix: "Poem/", + children: [ + { + text: "月", + icon: "", + link: "月", + }, + ], + }, + // { text: "火龙果", icon: "pen-to-square", link: "dragonfruit" }, + // "tomato", + ], + }, +]); diff --git a/src/.vuepress/public/PastedGraphic.png b/src/.vuepress/public/PastedGraphic.png new file mode 100644 index 0000000..ec429d4 Binary files /dev/null and b/src/.vuepress/public/PastedGraphic.png differ diff --git a/src/.vuepress/public/assets/icon/apple-icon-152.png b/src/.vuepress/public/assets/icon/apple-icon-152.png new file mode 100644 index 0000000..434fcc3 Binary files /dev/null and b/src/.vuepress/public/assets/icon/apple-icon-152.png differ diff --git a/src/.vuepress/public/assets/icon/chrome-192.png b/src/.vuepress/public/assets/icon/chrome-192.png new file mode 100644 index 0000000..6645f52 Binary files /dev/null and b/src/.vuepress/public/assets/icon/chrome-192.png differ diff --git a/src/.vuepress/public/assets/icon/chrome-512.png b/src/.vuepress/public/assets/icon/chrome-512.png new file mode 100644 index 0000000..a146f00 Binary files /dev/null and b/src/.vuepress/public/assets/icon/chrome-512.png differ diff --git a/src/.vuepress/public/assets/icon/chrome-mask-192.png b/src/.vuepress/public/assets/icon/chrome-mask-192.png new file mode 100644 index 0000000..530977a Binary files /dev/null and b/src/.vuepress/public/assets/icon/chrome-mask-192.png differ diff --git a/src/.vuepress/public/assets/icon/chrome-mask-512.png b/src/.vuepress/public/assets/icon/chrome-mask-512.png new file mode 100644 index 0000000..a4f90ae Binary files /dev/null and b/src/.vuepress/public/assets/icon/chrome-mask-512.png differ diff --git a/src/.vuepress/public/assets/icon/guide-maskable.png b/src/.vuepress/public/assets/icon/guide-maskable.png new file mode 100644 index 0000000..75449b6 Binary files /dev/null and b/src/.vuepress/public/assets/icon/guide-maskable.png differ diff --git a/src/.vuepress/public/assets/icon/ms-icon-144.png b/src/.vuepress/public/assets/icon/ms-icon-144.png new file mode 100644 index 0000000..2464124 Binary files /dev/null and b/src/.vuepress/public/assets/icon/ms-icon-144.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592273834835-a00be586-dd93-4431-8107-55e677ecad1e.png b/src/.vuepress/public/assets/images/CLI/1592273834835-a00be586-dd93-4431-8107-55e677ecad1e.png new file mode 100644 index 0000000..09bb351 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592273834835-a00be586-dd93-4431-8107-55e677ecad1e.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592275210087-a6a4830c-f19d-4198-af84-fa91d6ff872b.png b/src/.vuepress/public/assets/images/CLI/1592275210087-a6a4830c-f19d-4198-af84-fa91d6ff872b.png new file mode 100644 index 0000000..151cc42 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592275210087-a6a4830c-f19d-4198-af84-fa91d6ff872b.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592275571887-8e420077-90fe-4304-b60d-b8a219fa1197.png b/src/.vuepress/public/assets/images/CLI/1592275571887-8e420077-90fe-4304-b60d-b8a219fa1197.png new file mode 100644 index 0000000..5f70d6b Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592275571887-8e420077-90fe-4304-b60d-b8a219fa1197.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592277241475-dd841cc7-0b31-41c0-bb18-fc9479fb8ef6.png b/src/.vuepress/public/assets/images/CLI/1592277241475-dd841cc7-0b31-41c0-bb18-fc9479fb8ef6.png new file mode 100644 index 0000000..3a20c36 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592277241475-dd841cc7-0b31-41c0-bb18-fc9479fb8ef6.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592278053195-f7af78ee-7269-4e5f-945c-a0640e544a4b.png b/src/.vuepress/public/assets/images/CLI/1592278053195-f7af78ee-7269-4e5f-945c-a0640e544a4b.png new file mode 100644 index 0000000..98a7e3a Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592278053195-f7af78ee-7269-4e5f-945c-a0640e544a4b.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592279427634-8d98b159-7f81-4609-b4c7-5f87cbd04759.png b/src/.vuepress/public/assets/images/CLI/1592279427634-8d98b159-7f81-4609-b4c7-5f87cbd04759.png new file mode 100644 index 0000000..9d86494 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592279427634-8d98b159-7f81-4609-b4c7-5f87cbd04759.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592279490903-39bd3753-cdb7-4f7b-9c5a-78351856de51.png b/src/.vuepress/public/assets/images/CLI/1592279490903-39bd3753-cdb7-4f7b-9c5a-78351856de51.png new file mode 100644 index 0000000..7530421 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592279490903-39bd3753-cdb7-4f7b-9c5a-78351856de51.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592279566390-b69cca3f-ee89-4382-9d37-fe7b582a7379.png b/src/.vuepress/public/assets/images/CLI/1592279566390-b69cca3f-ee89-4382-9d37-fe7b582a7379.png new file mode 100644 index 0000000..c068ea4 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592279566390-b69cca3f-ee89-4382-9d37-fe7b582a7379.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592279746317-3387c5f5-3bd6-4f5b-9630-bd2ff802a784.png b/src/.vuepress/public/assets/images/CLI/1592279746317-3387c5f5-3bd6-4f5b-9630-bd2ff802a784.png new file mode 100644 index 0000000..69a13bf Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592279746317-3387c5f5-3bd6-4f5b-9630-bd2ff802a784.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592279952534-e2b7dcb7-327b-4fa7-b98b-ab7af4ccad78.png b/src/.vuepress/public/assets/images/CLI/1592279952534-e2b7dcb7-327b-4fa7-b98b-ab7af4ccad78.png new file mode 100644 index 0000000..a2833f3 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592279952534-e2b7dcb7-327b-4fa7-b98b-ab7af4ccad78.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592289135206-6e4dd1c0-9d5f-4127-80ab-3a313a603acd.png b/src/.vuepress/public/assets/images/CLI/1592289135206-6e4dd1c0-9d5f-4127-80ab-3a313a603acd.png new file mode 100644 index 0000000..12f9494 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592289135206-6e4dd1c0-9d5f-4127-80ab-3a313a603acd.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592289281093-6738e48c-96a0-41af-b5d2-5aa1bc977d56.png b/src/.vuepress/public/assets/images/CLI/1592289281093-6738e48c-96a0-41af-b5d2-5aa1bc977d56.png new file mode 100644 index 0000000..312e216 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592289281093-6738e48c-96a0-41af-b5d2-5aa1bc977d56.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592289415824-e66f3cb7-ad13-4607-a9bd-44769a8db297.png b/src/.vuepress/public/assets/images/CLI/1592289415824-e66f3cb7-ad13-4607-a9bd-44769a8db297.png new file mode 100644 index 0000000..4c080be Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592289415824-e66f3cb7-ad13-4607-a9bd-44769a8db297.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592289717866-af46a642-40e5-49a7-9b47-c2ecbe146b4f.png b/src/.vuepress/public/assets/images/CLI/1592289717866-af46a642-40e5-49a7-9b47-c2ecbe146b4f.png new file mode 100644 index 0000000..1e19d60 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592289717866-af46a642-40e5-49a7-9b47-c2ecbe146b4f.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592289879274-f9b03ed5-6b7e-44b2-b2a8-a522027a4e42.png b/src/.vuepress/public/assets/images/CLI/1592289879274-f9b03ed5-6b7e-44b2-b2a8-a522027a4e42.png new file mode 100644 index 0000000..aff3030 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592289879274-f9b03ed5-6b7e-44b2-b2a8-a522027a4e42.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592290344614-5cd1e3d3-f6e8-4797-838c-e99f2d1c6236.png b/src/.vuepress/public/assets/images/CLI/1592290344614-5cd1e3d3-f6e8-4797-838c-e99f2d1c6236.png new file mode 100644 index 0000000..69d2a44 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592290344614-5cd1e3d3-f6e8-4797-838c-e99f2d1c6236.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592290489914-b984706f-ee96-4aeb-aaab-26b58ccf4b17.png b/src/.vuepress/public/assets/images/CLI/1592290489914-b984706f-ee96-4aeb-aaab-26b58ccf4b17.png new file mode 100644 index 0000000..d889f97 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592290489914-b984706f-ee96-4aeb-aaab-26b58ccf4b17.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592291351981-8177457e-206b-4838-b1e5-be48ed695939.png b/src/.vuepress/public/assets/images/CLI/1592291351981-8177457e-206b-4838-b1e5-be48ed695939.png new file mode 100644 index 0000000..db25144 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592291351981-8177457e-206b-4838-b1e5-be48ed695939.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592291720360-2c65826f-d079-4f0e-8795-2a586b41add7.png b/src/.vuepress/public/assets/images/CLI/1592291720360-2c65826f-d079-4f0e-8795-2a586b41add7.png new file mode 100644 index 0000000..3c55a9e Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592291720360-2c65826f-d079-4f0e-8795-2a586b41add7.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592292123998-d056c182-7ceb-431d-bf65-76effda2ce3e.png b/src/.vuepress/public/assets/images/CLI/1592292123998-d056c182-7ceb-431d-bf65-76effda2ce3e.png new file mode 100644 index 0000000..5bebc79 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592292123998-d056c182-7ceb-431d-bf65-76effda2ce3e.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592292214309-5c9f7c48-34ba-4e48-b5f9-252cf3a0cdda.png b/src/.vuepress/public/assets/images/CLI/1592292214309-5c9f7c48-34ba-4e48-b5f9-252cf3a0cdda.png new file mode 100644 index 0000000..7696323 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592292214309-5c9f7c48-34ba-4e48-b5f9-252cf3a0cdda.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592447032476-de54a496-2339-45de-bce2-fc959eba94aa.png b/src/.vuepress/public/assets/images/CLI/1592447032476-de54a496-2339-45de-bce2-fc959eba94aa.png new file mode 100644 index 0000000..1dfa836 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592447032476-de54a496-2339-45de-bce2-fc959eba94aa.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592821714718-521138eb-2782-4eb9-b9f5-2454126ea0b9.png b/src/.vuepress/public/assets/images/CLI/1592821714718-521138eb-2782-4eb9-b9f5-2454126ea0b9.png new file mode 100644 index 0000000..5e56423 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592821714718-521138eb-2782-4eb9-b9f5-2454126ea0b9.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592821714838-2df4f723-224b-43d4-8e7f-95ff996fa376.png b/src/.vuepress/public/assets/images/CLI/1592821714838-2df4f723-224b-43d4-8e7f-95ff996fa376.png new file mode 100644 index 0000000..78fba24 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592821714838-2df4f723-224b-43d4-8e7f-95ff996fa376.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592821714985-e0570af1-ec3c-49ec-b588-154b237e80ee.png b/src/.vuepress/public/assets/images/CLI/1592821714985-e0570af1-ec3c-49ec-b588-154b237e80ee.png new file mode 100644 index 0000000..8da8612 Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592821714985-e0570af1-ec3c-49ec-b588-154b237e80ee.png differ diff --git a/src/.vuepress/public/assets/images/CLI/1592821715227-63ec4bda-4aab-4e80-8ee2-241462f6f6c8.png b/src/.vuepress/public/assets/images/CLI/1592821715227-63ec4bda-4aab-4e80-8ee2-241462f6f6c8.png new file mode 100644 index 0000000..2fe63db Binary files /dev/null and b/src/.vuepress/public/assets/images/CLI/1592821715227-63ec4bda-4aab-4e80-8ee2-241462f6f6c8.png differ diff --git a/src/.vuepress/public/assets/images/cover1.jpg b/src/.vuepress/public/assets/images/cover1.jpg new file mode 100644 index 0000000..06f33cf Binary files /dev/null and b/src/.vuepress/public/assets/images/cover1.jpg differ diff --git a/src/.vuepress/public/assets/images/cover2.jpg b/src/.vuepress/public/assets/images/cover2.jpg new file mode 100644 index 0000000..edf5b38 Binary files /dev/null and b/src/.vuepress/public/assets/images/cover2.jpg differ diff --git a/src/.vuepress/public/assets/images/cover3.jpg b/src/.vuepress/public/assets/images/cover3.jpg new file mode 100644 index 0000000..ad79fc9 Binary files /dev/null and b/src/.vuepress/public/assets/images/cover3.jpg differ diff --git a/src/.vuepress/public/favicon.ico b/src/.vuepress/public/favicon.ico new file mode 100644 index 0000000..e1fa72f Binary files /dev/null and b/src/.vuepress/public/favicon.ico differ diff --git a/src/.vuepress/public/logo.png b/src/.vuepress/public/logo.png new file mode 100644 index 0000000..86018d8 Binary files /dev/null and b/src/.vuepress/public/logo.png differ diff --git a/src/.vuepress/public/logo.svg b/src/.vuepress/public/logo.svg new file mode 100644 index 0000000..7622164 --- /dev/null +++ b/src/.vuepress/public/logo.svg @@ -0,0 +1 @@ + diff --git a/src/.vuepress/public/me.png b/src/.vuepress/public/me.png new file mode 100644 index 0000000..8e68b48 Binary files /dev/null and b/src/.vuepress/public/me.png differ diff --git a/src/.vuepress/public/met_william_morris_1875.jpeg b/src/.vuepress/public/met_william_morris_1875.jpeg new file mode 100644 index 0000000..cb99d4e Binary files /dev/null and b/src/.vuepress/public/met_william_morris_1875.jpeg differ diff --git a/src/.vuepress/sidebar.ts b/src/.vuepress/sidebar.ts new file mode 100644 index 0000000..4bb5420 --- /dev/null +++ b/src/.vuepress/sidebar.ts @@ -0,0 +1,136 @@ +import { sidebar } from "vuepress-theme-hope"; + +export default sidebar({ + "/": [ + "" /* / */, + + // "", + { + text: "碎碎念", + icon: "", + prefix: "碎碎念/", + children: "structure", + }, + { + text: "随笔打油诗", + icon: "", + prefix: "Poem/", + children: "structure", + }, + { + text: "安全", + icon: "shield-heart", + collapsible: true, + prefix: "Safety/", + children: "structure", + }, + { + text: "算法", + icon: "shield-heart", + collapsible: true, + prefix: "算法/", + children: "structure", + }, + { + text: "低代码", + icon: "laptop-code", + collapsible: true, + prefix: "LowCode/", + children: "structure", + }, + { + text: "工程化相关", + icon: "code-compare", + collapsible: true, + prefix: "CLI/", + children: "structure", + }, + { + text: "小程序", + icon: "comments", + collapsible: true, + prefix: "MiniProgram/", + children: "structure", + }, + + { + text: "杂库", + icon: "warehouse", + prefix: "Other/", + collapsible: true, + children: "structure", + }, + { + text: "Docker", + icon: "docker", + collapsible: true, + prefix: "Docker/", + children: "structure", + }, + { + text: "Node", + icon: "nodejs", + prefix: "Node/", + collapsible: true, + children: "structure", + }, + { + text: "Vue", + icon: "vuejs", + prefix: "Vue/", + collapsible: true, + children: "structure", + }, + { + text: "Webpack", + icon: "cube", + prefix: "Webpack", + collapsible: true, + children: "structure", + }, + { + text: "NestJS", + icon: "cube", + prefix: "NestJS", + collapsible: true, + children: "structure", + }, + { + text: "GraphQL", + icon: "cube", + prefix: "GraphQL", + collapsible: true, + children: "structure", + }, + { + text: "Monorepo", + icon: "cube", + prefix: "Monorepo", + collapsible: true, + children: "structure", + }, + { + text: "微前端", + icon: "cube", + prefix: "微前端", + collapsible: true, + children: "structure", + }, + { + text: "CSS", + icon: "", + prefix: "CSS", + collapsible: true, + children: "structure", + }, + { + text: "Web3", + icon: "", + prefix: "Web3", + collapsible: true, + children: "structure", + }, + // "intro", + // "slides", + ], +}); diff --git a/src/.vuepress/styles/config.scss b/src/.vuepress/styles/config.scss new file mode 100644 index 0000000..f91061d --- /dev/null +++ b/src/.vuepress/styles/config.scss @@ -0,0 +1,3 @@ +// you can change config here +$colors: #c0392b, #d35400, #f39c12, #27ae60, #16a085, #2980b9, #8e44ad, #2c3e50, + #7f8c8d !default; diff --git a/src/.vuepress/styles/index.scss b/src/.vuepress/styles/index.scss new file mode 100644 index 0000000..f6af387 --- /dev/null +++ b/src/.vuepress/styles/index.scss @@ -0,0 +1 @@ +// place your custom styles here diff --git a/src/.vuepress/styles/palette.scss b/src/.vuepress/styles/palette.scss new file mode 100644 index 0000000..356a428 --- /dev/null +++ b/src/.vuepress/styles/palette.scss @@ -0,0 +1,2 @@ +// you can change colors here +$theme-color: #096dd9; diff --git a/src/.vuepress/theme.ts b/src/.vuepress/theme.ts new file mode 100644 index 0000000..fd2c7e2 --- /dev/null +++ b/src/.vuepress/theme.ts @@ -0,0 +1,100 @@ +import { hopeTheme } from "vuepress-theme-hope"; +import navbar from "./navbar.js"; +import sidebar from "./sidebar.js"; + +export default hopeTheme({ + // hostname: "https://mister-hope.github.io", + + author: { + name: "Song", + url: "", + }, + + iconAssets: "fontawesome-with-brands", + + logo: "/me.png", + + repo: "linzsong/SongBlog", + + docsDir: "src", + + // navbar + navbar, + + // sidebar + sidebar, + + // footer: "默认页脚", + + displayFooter: false, + + blog: { + description: "响应式前端开发", + intro: "/intro.html", + medias: { + GitHub: "https://github.com/linzsong", + }, + }, + + // encrypt: { + // config: { + // "/demo/encrypt.html": ["1234"], + // }, + // }, + + // page meta + // metaLocales: { + // editLink: "在 GitHub 上编辑此页", + // }, + + plugins: { + blog: true, + git: false, + // comment: { + // // You should generate and use your own comment service + // provider: "Waline", + // serverURL: "https://waline-comment.vuejs.press", + // }, + + // all features are enabled for demo, only preserve features you need here + mdEnhance: { + align: true, + attrs: true, + chart: true, + codetabs: true, + demo: true, + echarts: true, + figure: true, + flowchart: true, + gfm: true, + imgLazyload: true, + imgSize: true, + include: true, + katex: true, + mark: true, + mermaid: true, + playground: { + presets: ["ts", "vue"], + }, + presentation: ["highlight", "math", "search", "notes", "zoom"], + stylize: [ + { + matcher: "Recommended", + replacer: ({ tag }) => { + if (tag === "em") + return { + tag: "Badge", + attrs: { type: "tip" }, + content: "Recommended", + }; + }, + }, + ], + sub: true, + sup: true, + tabs: true, + vPre: true, + vuePlayground: true, + }, + }, +}); diff --git "a/src/CLI/gitlab\345\256\236\347\216\260CI\350\207\252\345\212\250\345\214\226.md" "b/src/CLI/gitlab\345\256\236\347\216\260CI\350\207\252\345\212\250\345\214\226.md" new file mode 100644 index 0000000..e5f3755 --- /dev/null +++ "b/src/CLI/gitlab\345\256\236\347\216\260CI\350\207\252\345\212\250\345\214\226.md" @@ -0,0 +1,118 @@ +--- +title: 'gitlab实现CI自动化' # 当前页面内容标题,默认为 Markdown 文件中的第一个 h1 标签内容 +shortTitle: '自动化' # 当前页面的短标题 +description: 'gitlab实现CI自动化' # 当前页面内容描述 +icon: '' # 当前页面图标的 FontClass 或文件路径 (建议填写)。 +author: { + name: 'Song' +} +isOriginal: true # 当前文章是否为原创。 +date: 2020-06-22 # 写作时间。 +category: '基建' # 分类 +tag: 'CI/CD gitlab' # 标签 +sticky: 1 # 是否在列表中置顶。当填入数字时,数字越大,排名越靠前 +star: false # 是否收藏在博客主题的文章列表中。当填入数字时,数字越大,排名越靠前。 +article: true # 是否将该文章添加至文章列表中 +timeline: true # 是否将该文章添加至时间线中 +image: '' # 设置预览图 (分享图),请填入绝对路径 +editLink: false # 设置横幅图片 (宽屏分享图),请填入绝对路径。 +--- + +## 实现过程 + +1. 准备好gitlab,需要注意版本信息,需要与runner匹配兼容 +2. 准备好gitlab-runner ,需要注意版本信息,需要与gitlab匹配兼容 +3. 登录gitlab查看gitlab的CI注册地址与token +4. 运行gitlab-runner的注册功能将gitlab-runner注册到gitlab中 +5. 在该项目中的根目录增加 .gitlab-ci.yaml文件 +6. gitlab会自动检测gitlab-ci.yaml这个文件,并根据内容执行自动CI + +![123](/assets/images/CLI/1592821714718-521138eb-2782-4eb9-b9f5-2454126ea0b9.png#width=888) + +### gitlab-runner安装 + +    gitlab-runner是gitlab的运行组件,也就是gitlab触发的所有ci,gitlab都会交给gitlab-runner去执行自动过程,gitlab-runner会自动克隆一份当前项目的当前代码,执行yaml定义的CI操作 +**注意版本号,公司内部全部最新即可** + +#### 方法一 + +> 因为当前系统是使用docker部署,并且运行等资源消耗较高,这里直接安装,不要使用docker运行runner + +##### 开始安装gitlab-runner yum存储库 + +```bash +curl -L [https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh](https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh) | sudo bash + + +# 检查你的gitlab版本号 +yum --showduplicates list  gitlab-ci-multi-runner    # gitlab 8 或者9 版本对应的版本系列 +yum --showduplicates list  gitlab-runner  # 高版本对应的runner系列,这里使用这个即可 +# 安装最新版 +yum -y install gitlab-runner +``` + +#### 方法二 + +##### 下载gitlab-runner + +```bash +# Linux x86-64 +$ sudo wget -O /usr/local/bin/gitlab-runner [https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64](https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64) +# Linux x86 +$ sudo wget -O /usr/local/bin/gitlab-runner [https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-386](https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-386) +# Linux arm +$ sudo wget -O /usr/local/bin/gitlab-runner [https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm](https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm) +# 授权 +$ sudo chmod +x /usr/local/bin/gitlab-runner +$ sudo cp /usr/local/bin/gitlab-runner /usr/bin +# 创建gitlab-runner用户 +$ sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash +# 安装gitlab-runner +$ sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner +# 启动 +$ sudo gitlab-runner start +``` + +### gitlab-runner启动参数修改 + +    runner默认使用程序用户gitlab-runner运行,一些比如容器操作,挂载目录删除,修改等等,都需要使用root来操作,所以需要做一些权限修改 + +```bash +# 根据返回结果,找到需要service文件 +systemctl status gitlab-runner +  ● gitlab-runner.service - GitLab Runner +     Loaded: loaded (/etc/systemd/system/gitlab-runner.service; enabled; vendor preset: disabled)   # 注意这一行 +     Active: active (running) since Wed 2019-08-07 05:11:29 EDT; 2 weeks 5 days ago +   Main PID: 310156 (gitlab-runner) +# 将以上文件中的 --user gitlab-runner 修改为root +vi /etc/systemd/system/gitlab-runner.service +  修改 +  ExecStart=/usr/lib/gitlab-runner/gitlab-runner "run" "--working-directory" "/home/gitlab-runner" "--config" "/etc/gitlab-runner/config.toml" "--service" "gitlab-runner" "--syslog" "--user" "gitlab-runner" +  为 +  ExecStart=/usr/lib/gitlab-runner/gitlab-runner "run" "--working-directory" "/home/gitlab-runner" "--config" "/etc/gitlab-runner/config.toml" "--service" "gitlab-runner" "--syslog" "--user" "root" +# 启动gitlab-runner +systemctl start gitlab-runner +``` + +### 注册gitlab-runner + +    gitlab中获取地址与token,gitlab-runner使用该信息进行注册,gitlab中显示注册节点 + +1. 查看地址与token,注意需要有该项目的管理员权限 + +2. ![33](/assets/images/CLI/1592821714838-2df4f723-224b-43d4-8e7f-95ff996fa376.png#) + +3. 记录地址与token信息 + +4. ![22](/assets/images/CLI/1592821714985-e0570af1-ec3c-49ec-b588-154b237e80ee.png#) + +5. 使用此信息进行runner注册 +**注册runner** +sudo gitlab-runner register +输入上述地址 +输入上述token +输入说明 +输入tag     # 注 此tag将会频繁使用,建议使用有意义,并且有针对性的tag,gitlab-ci.yaml使用此tag标识的机器推送CI数据 +输入shell   # 注 此处是runner运行所支持的环境,可以是 docker shell virtualbox k8s 等等多种类型,这里使用shell +6.    检查成功是否成功注册 +7.    ![11](/assets/images/CLI/1592821715227-63ec4bda-4aab-4e80-8ee2-241462f6f6c8.png#) diff --git a/src/CLI/serverless.md b/src/CLI/serverless.md new file mode 100644 index 0000000..c05afe4 --- /dev/null +++ b/src/CLI/serverless.md @@ -0,0 +1,20 @@ +--- + title: 'serverless' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2020-07-20 + category: '' + tag: 'severless' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1595232634838-b444c4b4-8829-4499-a33a-8cf823b711c7.png#align=left&display=inline&height=395&name=image.png&originHeight=395&originWidth=720&size=329705&status=done&style=none&width=720) diff --git "a/src/CLI/\344\272\247\345\223\201\346\250\241\345\235\227\346\216\245\345\205\245-\344\270\215\350\257\246\347\273\206\346\214\207\345\215\227.md" "b/src/CLI/\344\272\247\345\223\201\346\250\241\345\235\227\346\216\245\345\205\245-\344\270\215\350\257\246\347\273\206\346\214\207\345\215\227.md" new file mode 100644 index 0000000..c719796 --- /dev/null +++ "b/src/CLI/\344\272\247\345\223\201\346\250\241\345\235\227\346\216\245\345\205\245-\344\270\215\350\257\246\347\273\206\346\214\207\345\215\227.md" @@ -0,0 +1,189 @@ +--- +title: '产品模块接入说明' # 当前页面内容标题,默认为 Markdown 文件中的第一个 h1 标签内容 +shortTitle: '工程接入' # 当前页面的短标题 +description: 'emotibot的产品模块接入说明' # 当前页面内容描述 +icon: '' # 当前页面图标的 FontClass 或文件路径 (建议填写)。 +author: { + name: 'Song' +} +isOriginal: true # 当前文章是否为原创。 +date: 2022-08-23 # 写作时间。 +category: '基建🤺' # 分类 +tag: 'cli' # 标签 +sticky: 1 # 是否在列表中置顶。当填入数字时,数字越大,排名越靠前 +star: false # 是否收藏在博客主题的文章列表中。当填入数字时,数字越大,排名越靠前。 +article: true # 是否将该文章添加至文章列表中 +timeline: true # 是否将该文章添加至时间线中 +image: '' # 设置预览图 (分享图),请填入绝对路径 +editLink: false # 设置横幅图片 (宽屏分享图),请填入绝对路径。 +--- + +## 说明 + +产品模块接入是指在 `BF2020` 中,增加一个服务模块。以中控模块(ccm)为例,如图: +![image.png](/assets/images/CLI/1592273834835-a00be586-dd93-4431-8107-55e677ecad1e.png) +在选择企业之后,左侧会出现企业有权限使用的产品服务,现有九个。我们的产品模块接入,就是需要在之后增加一个产品模块入口。 + +## 准备阶段 + +我们使用 `emoti-cli` 搭建新的UI工程。使用方式参考[README.md](https://gitlab.emotibot.com/emoti_frontend/emoti-cli) +> 注意:这里需要使用的是bf2020_product模版 + +并且还需要在 `emotibot_deploy` 部署工程中加入新的模块。 + +我们需要知道中控模块UI工程和现有的产品UI工程的关系。 +下图表示每个产品对应一个代码工程。 +![image.png](/assets/images/CLI/1592275210087-a6a4830c-f19d-4198-af84-fa91d6ff872b.png) + +产品模块是如何被访问及如何请求后台服务的?下图表示一个请求的过程: +![image.png](/assets/images/CLI/1592275571887-8e420077-90fe-4304-b60d-b8a219fa1197.png#align=left&display=inline&height=381&name=image.png&originHeight=446&originWidth=819&size=34526&status=done&style=none&width=699) + +总结:新的产品UI工程是独立的,它们通过`emotibot_deploy`部署工程的设置相互关联。 + +## UI工程 + +通过`emoti-cli`模版搭建的项目就已经能进行本地开发了。 +入口文件是: +![image.png](/assets/images/CLI/1592277241475-dd841cc7-0b31-41c0-bb18-fc9479fb8ef6.png#align=left&display=inline&height=97&name=image.png&originHeight=180&originWidth=386&size=14474&status=done&style=none&width=207) + +- bf2020_auth.js: 定义了一些基础接口,例如获取产品列表,获取会话维度等 +- FrameworkProduct.vue: 基础界面,包含右侧产品列表,顶部工具栏等 +- request.js: 封装的请求接口 + +业务代码开发完毕,就可以进行构建部署了 + +### docker构建设置 + +![image.png](/assets/images/CLI/1592278053195-f7af78ee-7269-4e5f-945c-a0640e544a4b.png#align=left&display=inline&height=359&name=image.png&originHeight=876&originWidth=354&size=55816&status=done&style=none&width=145) +上图的docker目录是模版工程带入的,docker_new是中控模块现在采用的。两者的主要区别是docker_new使用的是新的基础镜像,是参照运维同学优化后的镜像来的,体积会更小。(采用哪种方式还需要确认下) + +#### nginx.conf.template + +设置upstream,其中server以变量存在: +![image.png](/assets/images/CLI/1592279427634-8d98b159-7f81-4609-b4c7-5f87cbd04759.png#align=left&display=inline&height=172&name=image.png&originHeight=172&originWidth=828&size=20128&status=done&style=none&width=828) +设置listen,端口也是以变量存在: +![image.png](/assets/images/CLI/1592279566390-b69cca3f-ee89-4382-9d37-fe7b582a7379.png#align=left&display=inline&height=150&name=image.png&originHeight=150&originWidth=584&size=20708&status=done&style=none&width=584) +设置location: +![image.png](/assets/images/CLI/1592279490903-39bd3753-cdb7-4f7b-9c5a-78351856de51.png#align=left&display=inline&height=403&name=image.png&originHeight=558&originWidth=974&size=84900&status=done&style=none&width=703) + +> 以上的修改使用到了两个变量。分别为CCM_BACKEND_URL / CCM_UI_FONT_PORT + +这两个变量我们需要在 `entrypoint.sh`  中声明,并且需要在deploy工程中定义 +![image.png](/assets/images/CLI/1592279746317-3387c5f5-3bd6-4f5b-9630-bd2ff802a784.png#align=left&display=inline&height=274&name=image.png&originHeight=450&originWidth=946&size=40511&status=done&style=none&width=576) + +### API + +![image.png](/assets/images/CLI/1592279952534-e2b7dcb7-327b-4fa7-b98b-ab7af4ccad78.png#align=left&display=inline&height=198&name=image.png&originHeight=198&originWidth=1010&size=50660&status=done&style=none&width=1010) +对于API的URL设置,我加了个前缀,需要注意的是需要以 `./` 相对路径的方式 + +## emotibot-loadbalance + +对照准备阶段的请求流程图:新的请求进来,需要通过loadbalance的转发,所以还需要修改loadbalance的配置。 + +### nginx.conf.template + +增加upstream,这里有使用 `LB_CCM_UI_URL` 变量 +![image.png](/assets/images/CLI/1592289281093-6738e48c-96a0-41af-b5d2-5aa1bc977d56.png#align=left&display=inline&height=656&name=image.png&originHeight=656&originWidth=800&size=88260&status=done&style=none&width=800) +增加对ccm.html的location转发 +![image.png](/assets/images/CLI/1592289135206-6e4dd1c0-9d5f-4127-80ab-3a313a603acd.png#align=left&display=inline&height=510&name=image.png&originHeight=510&originWidth=986&size=70294&status=done&style=none&width=986) + +### entrypoint.sh + +这里需要声明变量的引用,并且需要在deploy工程中定义 +![image.png](/assets/images/CLI/1592289415824-e66f3cb7-ad13-4607-a9bd-44769a8db297.png#align=left&display=inline&height=196&name=image.png&originHeight=196&originWidth=962&size=27578&status=done&style=none&width=962) + +## emotibot_deploy + +从上文我们提到,emotibot_deploy的修改点 + +- 增加新模块 +- 增加变量定义 + +### 增加新模块 + +![image.png](/assets/images/CLI/1592289717866-af46a642-40e5-49a7-9b47-c2ecbe146b4f.png#align=left&display=inline&height=339&name=image.png&originHeight=806&originWidth=418&size=47054&status=done&style=none&width=176) +增加了一个 `10-ccm` 的目录,里面定义了关于ccm的docker容器。单机部署主要就是修改以下三个文件 + +- dev.env-定义的环境变量,可以在module.yaml文件中使用 +- module.yaml-创建容器的文件 +- port.yaml-定义各个容器暴露的端口 + +#### dev.env + +定义一些变量,这里定义了中控的名称和端口 +![image.png](/assets/images/CLI/1592290344614-5cd1e3d3-f6e8-4797-838c-e99f2d1c6236.png#align=left&display=inline&height=214&name=image.png&originHeight=326&originWidth=708&size=30152&status=done&style=none&width=464) + +#### module.yaml + +增加一个中控容器 +![image.png](/assets/images/CLI/1592289879274-f9b03ed5-6b7e-44b2-b2a8-a522027a4e42.png#align=left&display=inline&height=337&name=image.png&originHeight=544&originWidth=1028&size=70407&status=done&style=none&width=636) + +#### port.yaml + +定义了两个容器的端口,需要唯一 +![image.png](/assets/images/CLI/1592290489914-b984706f-ee96-4aeb-aaab-26b58ccf4b17.png#align=left&display=inline&height=201&name=image.png&originHeight=358&originWidth=402&size=25085&status=done&style=none&width=226) + +### 变量定义 + +上文,我们在两个地方使用到了环境变量。一个是在UI工程的docker构建目录下的 nginx.conf.template文件中;一个是在 loadbalance的nginx.conf.template文件中 +UI工程的变量为CCM_BACKEND_URL / CCM_UI_FONT_PORT,这两个变量我们在 `module.yaml` 文件中 `ccm-ui` 模块下的environment的选项中定义。如上图的module.yaml + +> nginx.cong.template中使用的变量必须在对应模块的environment中声明,而environment中使用的变量需要在dev.env文件中声明。nginx.cong.template中不可以直接使用dev.env中的变量 + +loadbalance的变量为LB_CCM_UI_URL,同样,这个变量也需要module.yaml文件中load-balance模块下的environment选项中定义。需要注意的是一个是在1-bf文件下,一个在新增的文件下(10-ccm) +![image.png](/assets/images/CLI/1592291351981-8177457e-206b-4838-b1e5-be48ed695939.png#align=left&display=inline&height=756&name=image.png&originHeight=756&originWidth=1154&size=191821&status=done&style=none&width=1154) + +上图定义的是load-balance转发到新模块的地址端口,表示本地的8909端口,而8909端口我们已经在port.yaml文件中设置了。 + +## init-db + +(在部署工程下) +这里需要设置的是products接口返回的数据,只有返回了新的产品,才能在右边的产品列表中展示入口. +![image.png](/assets/images/CLI/1592291720360-2c65826f-d079-4f0e-8795-2a586b41add7.png#align=left&display=inline&height=366&name=image.png&originHeight=572&originWidth=1006&size=115718&status=done&style=none&width=644) + +步骤如下: + +1. 在1-bf文件夹下的init-db文件夹下的sql文件夹下的auth文件夹下新增一个 `.sql` 文件 + + + +```sql +-- +migrate Up + +INSERT INTO `products` (`id`, `code`, `sort`, `position`, `icon`, `route`, `is_link`, `status`, `create_time`) VALUES ('11', 'ccm', '1', 'menu_product', 'color-zhongkong', 'ccm', '1', '1', '2020-06-10 13:46:52'); + +INSERT INTO `ent_prods`(`ent_id`, `prod_id`) VALUES +(1, 11); + +-- +migrate Down +``` + +2. 在docker目录下打个包并且push + +![image.png](/assets/images/CLI/1592292123998-d056c182-7ceb-431d-bf65-76effda2ce3e.png#align=left&display=inline&height=42&name=image.png&originHeight=42&originWidth=594&size=6302&status=done&style=none&width=594) + +3. 修改infra目录下的init-db.yaml中的image + +![image.png](/assets/images/CLI/1592292214309-5c9f7c48-34ba-4e48-b5f9-252cf3a0cdda.png#align=left&display=inline&height=95&name=image.png&originHeight=186&originWidth=1110&size=25329&status=done&style=none&width=569) + +4. 服务器重新部署 + +## consul注册 + +这一步的操作 +主要就是在entrypoint.sh中加入如下代码,主动去curl注册一个新的模块 + +```bash +curl ${CCM_CONSUL_URL}/v1/agent/service/register -X PUT -i -H "Content-Type:application/json" -d '{"ID":"ccm","Name":"ccm","Tags":[],"Address":"ccm-ui","Port":8909,"EnableTagOverride":false}' +``` + +> Address填写的是容器名,即定义的container_name。也可以填写当前的ip。port就是端口 + +CCM_CONSUL_URL变量需要在deploy工程的environment选项中声明 +![image.png](/assets/images/CLI/1592447032476-de54a496-2339-45de-bce2-fc959eba94aa.png#align=left&display=inline&height=371&name=image.png&originHeight=542&originWidth=992&size=77489&status=done&style=none&width=679) +CONSUL_HOST和CONSUL_PORT可以理解已经在全局环境中定义了。直接使用就好了。 + +完成之后重新部署项目, +[http://172.16.103.21:8500/ui/dc1/services](http://172.16.103.21:8500/ui/dc1/services) +通过这个地址就可以看到模块是否已经注册上去。 +> 这里的 `172.16.103.21:8500` 是中控开发环境的ip和端口,需要替换成自己部署服务的ip和端口 diff --git "a/src/CLI/\345\205\263\344\272\216\346\216\250\350\277\233PC\347\253\257\351\241\271\347\233\256\346\250\241\346\235\277\347\273\237\344\270\200\346\226\271\346\241\210\346\225\264\347\220\206.md" "b/src/CLI/\345\205\263\344\272\216\346\216\250\350\277\233PC\347\253\257\351\241\271\347\233\256\346\250\241\346\235\277\347\273\237\344\270\200\346\226\271\346\241\210\346\225\264\347\220\206.md" new file mode 100644 index 0000000..910bbfe --- /dev/null +++ "b/src/CLI/\345\205\263\344\272\216\346\216\250\350\277\233PC\347\253\257\351\241\271\347\233\256\346\250\241\346\235\277\347\273\237\344\270\200\346\226\271\346\241\210\346\225\264\347\220\206.md" @@ -0,0 +1,121 @@ +--- + title: '关于推进PC端项目模板统一方案整理' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2022-08-23 + category: '基建' + tag: 'cli' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ### 背景 + +前端已经在新的PC端项目上使用`vite+vue3+ts+pinia+vue-router`技术栈进行开发(UI库不一样),但是并没有形成一个统一配置模板。未来开发人员切换项目的时候可能会有一定的熟悉成本,而且在在新的PC端项目开始的时候,还需要再来一次项目初始化。尽早对PC端模板达成一致,可以尽早在项目中实践。 + +### 期望 + +- PC端能统一模板 +- 整理出一套模板后,再在此基础上开发gyenno-cli,达到脚本自动化初始化项目 + +### 如何形成较稳定的统一模板 + +PS.形成统一模版是默认基于当前`Vite, Vue3, TypeScript, Pinia`技术栈进行的,也就是默认技术栈已经统一。 + +#### 方案1.根据公司现有的PC端项目去做整理归纳 + +因为是针对新技术栈的模板,所以无法根据历史项目(vue2)去抽取模板,且未来都会基于vue3+ts生态开发新项目。 + +如果是根据现在进行的基于新技术栈开发的PC端项目(`基础数据服务` 和 `睿云管理后台`)去抽取的话: + +--- + +优: + +1. 更贴合业务的开发模版 +2. 开发人员更熟悉 + +--- + +劣: + +1. 有试错成本(新技术栈的试错) +2. 随着项目去走,抽取的周期会长 + +#### 方案2.找到成熟稳定的开源项目来抽取模板, + +有一个稳定的项目且基于新技术栈开发,对于新技术不熟悉的开发人员都是不错的参考。 + +1. 可以参考更好的方法函数封装(例如基于ts的axios封装请求库) +2. 可以参考更全面的TS封装和写法指导 +3. 可以参考一些业务场景的解决方案 + +但是如何评价这个项目的代码质量或者封装抽取组件思维是否达标,也是仁者见仁,更多的还需要不断的深入才能给出答案。 + +个人认为:有这么一个项目存在,现阶段来说是利大于弊的。可以在过渡阶段起到较大的作用。也能帮助提升形成模板的效率。且随着新技术栈的开发经验提升,也可以不断优化模板。 + +### 基于第2种方案 + +基于第二种方案的尝试,有以下这些模板,技术栈和当前的`基础数据服务` 和 `睿云管理后台`两个项目匹配,UI库的不同不影响基础模版。 + +#### vue-vben-admin开源模板(Vite, Vue3, Ant-Design-Vue, TypeScript, Pinia) + +- [完整版Github](https://github.com/vbenjs/vue-vben-admin.git) +- [精简版Github](https://github.com/vbenjs/vben-admin-thin-next) +- [完整版演示地址](https://vvbin.cn/next/#/login?redirect=/dashboard) +- [中文文档地址](https://vvbin.cn/doc-next/) + +> 区分完整版和精简版,完整版功能更多,插件更多;精简版可根据自己的需求添加插件 + + +##### 文档层面 + +有介绍较全的中文文档,覆盖了从浅到深的介绍。 + +有演示站点,方便调试 + +##### 源码层面 + +项目在持续的更新,文件层级清晰,源码部分有注释 + +##### 其他点 + +- Issues较多(主要集中在功能问题) +- UI库是基于Ant Design + +#### vue-pure-admin开源模板(Vite, Vue3, TypeScript, Tailwind CSS, element-plus) + +- [完整版Github](https://github.com/xiaoxian521/vue-pure-admin) +- [精简版Github](https://github.com/xiaoxian521/pure-admin-thin) +- [完整版演示地址](https://vue-pure-admin.vercel.app/#/login) +- [中文文档地址](https://pure-admin-doc.vercel.app/) + +在文档/源码层面大同小异 + +##### 亮点 + +1. 使用了`Tailwind CSS`,对css这块有一定的参考价值 + +#### vue-naive-admin开源模板(Vite, Vue 3, Pinia, Naive UI) + +- [Github](https://github.com/zclzone/vue-naive-admin) +- [中文文档地址](https://zclzone.github.io/vue-naive-admin-docs/) + +##### 亮点 + +1. 相对精简,适用于中小型项目 +2. 原子化 css 解决方案 + +### 结论 + +1. 我们现阶段目标,是生成一个PC端的基础模版,这样在未来的新项目就可以应用起来且可以不断丰富。 +2. `基础数据服务` 和 `睿云管理后台` 已经在进行开发中,结合方案1和方案2,基于这两个项目再配合参考 上述`vue-vben-admin`等模板 ,可以较全面且快速的生成一个基础试错模板。 +3. 对`vue-vben-admin`等模板保持借鉴参考的态度,取其精华去其糟粕。 diff --git "a/src/CLI/\345\211\215\347\253\257\351\241\271\347\233\256\350\216\267\345\217\226\351\203\250\347\275\262\345\267\245\347\250\213environment\351\205\215\347\275\256\351\200\211\351\241\271\345\200\274\345\217\257\350\241\214\346\226\271\346\241\210\344\270\200.md" "b/src/CLI/\345\211\215\347\253\257\351\241\271\347\233\256\350\216\267\345\217\226\351\203\250\347\275\262\345\267\245\347\250\213environment\351\205\215\347\275\256\351\200\211\351\241\271\345\200\274\345\217\257\350\241\214\346\226\271\346\241\210\344\270\200.md" new file mode 100644 index 0000000..aa759ce --- /dev/null +++ "b/src/CLI/\345\211\215\347\253\257\351\241\271\347\233\256\350\216\267\345\217\226\351\203\250\347\275\262\345\267\245\347\250\213environment\351\205\215\347\275\256\351\200\211\351\241\271\345\200\274\345\217\257\350\241\214\346\226\271\346\241\210\344\270\200.md" @@ -0,0 +1,90 @@ +--- + title: '前端项目获取部署工程environment配置选项值可行方案一' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2020-09-16 + category: '' + tag: 'emotibot' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ### 背景: +H5聊天机器人配置功能,希望在demo-test环境中生效,但是暂时还不希望跟着BF2.1.4版本。demo-test环境又是和2.1.4版本同步的,所以如果要在两个环境中差别维护H5配置这个功能,在升级或更新时需要额外的精力对这个模块做处理。所以希望通过能否在部署工程中配置个开关,对这个功能部署时控制是否展示。 + +### 技术实现原理: +原理和设置nginx配置文件一样,前端先定好一个模版,通过变量注入的方式来获取对应变量值。理解前端工程从构建到启动容器这个阶段,就很好理解这种实现方式。 + +### 文件说明: +#### common_variable.js.template +获取配置变量的模版文件,和nginx.conf.template在同一目录下,这个文件的作用就是作为一个js文件在项目加载的时候一起加载,定义为一个自执行函数,如下图:把获取到ADMIN_IS_OPEN_H5_CHATBOT变量放入到deployVariable对象中,并且将对象挂载到window下。这样在项目中就可以通过访问window.deployerVariable访问到想要的变量。且deployerVariable为只读。 +![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1600227029044-3abfa65c-aa67-407a-a267-9cad05a59a01.png#align=left&display=inline&height=202&name=image.png&originHeight=404&originWidth=1000&size=57857&status=done&style=none&width=500) + +#### run_nginx.sh +这个文件是启动容器时执行的一个脚本。通过执行这个文件,将配置变量传给nginx文件。如下图: +![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1600227373479-edf149a8-1ce4-454a-b595-edfa191fd60c.png#align=left&display=inline&height=497&name=image.png&originHeight=994&originWidth=864&size=139034&status=done&style=none&width=432) +意思是以nginx.conf.template为模版文件写入nginx.conf文件,并且将前缀为$$ 的变量注入,这样在nginx.conf.template文件中定义的同名变量就能被在部署工程中定义的具体的值取代。 + + +#### Dockerfile +打包docker镜像的配置文件,里面定义基础镜像,切换工作目录,拷贝文件等操作步骤。 + +#### index.html +前端单页面应用的入口文件,一切的起源。包括我们的公共组件,element,vue都是通过这个文件加载。如下图: +![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1600227832447-5aa44d39-53c9-46d2-80b0-09991d96cc6b.png#align=left&display=inline&height=457&name=image.png&originHeight=914&originWidth=1578&size=252472&status=done&style=none&width=789) +### 操作步骤: +我们涉及到修改的就是以上四个文件 +#### 第一步: +在docker/data/conf文件夹下增加common_variable.js.template文件,这个模板文件代码如上。需要添加变量的话只要在 +```javascript +(function(window){ + const deployVariable = { + ADMIN_IS_OPEN_H5_CHATBOT: "${ADMIN_IS_OPEN_H5_CHATBOT}" + //在这里添加新的变量,如:需要添加is_open_image + ADMIN_IS_OPEN_IMAGE: "..." + } + Object.freeze(deployVariable); + window.deployVariable = deployVariable + Object.defineProperty(window, 'deployVariable', { + writable: false + }) +})(window) +``` + +#### 第二步: +在Dockerfile文件中加入 +![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1600238533099-95fdfc2c-c34c-4471-bec2-0842dc58ecea.png#align=left&display=inline&height=287&name=image.png&originHeight=574&originWidth=1562&size=117100&status=done&style=none&width=781) +Dockerfile的执行是在run_nginx.sh脚本之前,加入这一行的目的为了将我们的模板文件,拷贝到docker容器的相关目录下,为下一步做准备。 + +#### 第三步: +在run_nginx.sh脚本中加入如下代码 +![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1600238748977-04c5c64f-6f8f-4900-abfe-74ea826e6390.png#align=left&display=inline&height=378&name=image.png&originHeight=756&originWidth=1512&size=113623&status=done&style=none&width=756) +如上,在运行run_nginx.sh脚本的时候,会先把工作目录切换到前端静态资源的目录 `dist/static/js` +如果已经存在common_variable.js文件会先删除,然后使用 `envsubst` 注入部署工程的变量,以template模板文件在目标目录生成common_variable.js文件。 +这时在common_variable.js文件中,你在template文件中设置的变量占位符就会替换成部署工程中的具体的值。 + +#### 第四步: +index.html文件加入如下代码 +![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1600239187818-20cfd5a8-d841-4057-a3f1-8aaaadb13277.png#align=left&display=inline&height=266&name=image.png&originHeight=532&originWidth=1548&size=178712&status=done&style=none&width=774) +上面三部我们做的操作是:在前端静态资源构建完成之后,利用模板文件在 `static/js` 目录下生成了真正的common_variabel.js文件,而部署工程的变量就是在生成真正的js文件的时候注入的(和生成真正的nginx配置文件一样的原理)。 +这一步我们就是使用这个文件,作为资源在html文件中加载。加载完之后,就可以在window上看到deployVariable这个属性了。然后在项目中需要的地方拿来用就好了 + + +### 总结: +一句话概括:我们就是先把部署工程的变量拿到放在一个js文件中,然后再通过项目加载这个js文件来将变量挂载到window上。这样我们就可以在项目中使用这些变量。 +目前在general-ui的patch/2.1.4上已经更新。 + +### 问题: +目前暴露出的问题且可优化的点: +1.这个js文件不经过webpack打包,是个没有经过混淆和压缩的'明文'代码 +2.在更新变量状态部署之后,需要重新刷新浏览器生效,因为依赖于文件是否执行 +... + diff --git "a/src/CLI/\345\274\200\345\217\221\346\236\204\345\273\272\345\210\206\344\272\253.md" "b/src/CLI/\345\274\200\345\217\221\346\236\204\345\273\272\345\210\206\344\272\253.md" new file mode 100644 index 0000000..e44f037 --- /dev/null +++ "b/src/CLI/\345\274\200\345\217\221\346\236\204\345\273\272\345\210\206\344\272\253.md" @@ -0,0 +1,105 @@ +--- + title: '开发构建分享' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2019-05-10 + category: '基建' + tag: 'cli' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ## 了解概念 +vue是一个构建用户界面的渐进式框架  +vue-cli 是vue项目的脚手架工具 +node是服务器上的JavaScript运行环境,可以理解为 Node = JavaScript+ 操作系统API +npm是包管理器,是 Node.js 平台的默认包管理工具,(yarn) +webpack 是一个静态模块打包器 (gulp grunt) + +首先简单回顾上节课我们用vue开发的流程是怎样: + +1. 先写好html文件 +2. 引入vue.js +3. 然后挂载在DOM节点上,然后编写 + +还记得这张图吗 +![](https://cdn.nlark.com/yuque/0/2019/png/297368/1556422147564-e90c91a7-5aa9-4d21-ba44-008774f99529.png#align=left&display=inline&height=217&originHeight=217&originWidth=568&status=done&width=568) +一个页面可以看成是各个组件组装而成。 +但是随着项目代码量越来越多,不可能所有的代码都是: +> 使用 `Vue.component` 来定义全局组件,紧接着用 `new Vue({ el: '#container '})` 在每个页面内指定一个容器元素。 + +这样就会造成 + +1. 组件名定义不能重复 +2. 字符串模板没有高亮提示 +3. css写入麻烦 +4. 没有构建步骤 + +所以vue就提供了单文件模板,一个以`.vue` 为后缀的文件,在这个文件里面html ,css,js分离,就像这样: +![image.png](https://cdn.nlark.com/yuque/0/2019/png/297368/1556433959745-2664f7d4-31c9-4ae4-87e9-d56504ba242c.png#align=left&display=inline&height=256&name=image.png&originHeight=256&originWidth=366&size=13730&status=done&width=366) + +并且我们还可以针对html使用类似**Pug**的模板引擎,针对js使用**Babel**或**Ty****peScript**预处理器,针对css使用**stylus**或者**lass**的预处理器,来提升我们的开发效率,但问题又来了,浏览器并不认识后缀为vue的文件,该怎么办呢? +#### Vue Loader +但这一切都离不开Vue Loader,我们之所以能使用单文件模板,是因为Vue Loader的存在,Vue Loader是什么? +> Vue Loader 是一个 [webpack](https://webpack.js.org/) 的 loader,它允许你以一种名为[单文件组件 (SFCs)](https://vue-loader.vuejs.org/zh/spec.html)的格式撰写 Vue 组件 + + +所以这就涉及到 ` Webpack` 了 +#### Webpack  +> webpack 是一个模块打包器。它的主要目标是将 JavaScript 文件打包在一起,打包后的文件用于在浏览器中使用,但它也能够胜任转换(transform)、打包(bundle)或包裹(package)任何资源(resource or asset)。 + +而loader又是干啥用的? +> loader 用于对模块的源代码进行转换。loader 可以使你在 `import` 或"加载"模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 `import` CSS文件! + +简单的说:浏览器并不认识后缀为 `.vue` 的单文件组件,那我们就需要把它转化为浏览器认识的js,HTML、css的文件,而Vue Loader 就是来干这个的 +所以,你想使用单文件模板,就一定要使用Vue Loader,要使用Vue Loader 就一定要基于webpack,那么就一定要配置webpack +如何配置那又是另外一回事了。好在Vue提供了官方脚手架:Vue Cli 它帮助我们省去了webpack的相关配置,让我们直接使用单文件组件开发并且还提供了更多功能。 +#### Node +要知道,Vue Cli 也是基于Node开发的。一切都离不开Node环境。 + +## 介绍Vue Cli +它是一个终端运行的程序,目的是为了快速构建vue的开发环境。这个程序包含了三个主要部分 +一、Cli    它是一个全局npm包,提供了终端里面的命令 +二、Cli 服务    也是个npm包,不过安装在每个项目里面,是构建于webpack-dev-server上的 +三、CLI 插件    可供选择的各种npm包 + +## 使用Vue Cli +Vue Cli现在的最新的版本是3.x,而我们公司有些项目还是使用老版本构建的 +我们会以最新的3.x的版本来操作,期间对比下老版本的工程目录 + +#### 安装 +#### 创建 +`vue create hello-world` + +![image.png](https://cdn.nlark.com/yuque/0/2019/png/297368/1557198250674-8ed61802-ba37-4d8b-84b3-0266f1888163.png#align=left&display=inline&height=254&name=image.png&originHeight=254&originWidth=390&size=9721&status=done&width=390) + + +也可以使用 `vue ui` 命令来使用图形化界面创建项目 +看创建好的目录 +#### 开发 +Axios +> Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中 + + +Router +> Vue Router 是 [Vue.js](http://cn.vuejs.org/) 官方的路由管理器 + + +Vuex +> Vuex 是一个专为 Vue.js 应用程序开发的**状态管理模式**。它采用集中式存储管理应用的所有组件的状态 + + + +#### 打包构建优化 +`npm run build`  + + + diff --git a/src/CSS/CSS.md b/src/CSS/CSS.md new file mode 100644 index 0000000..a784ec3 --- /dev/null +++ b/src/CSS/CSS.md @@ -0,0 +1,28 @@ +--- + title: 'CSS' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2020-05-22 + category: '' + tag: 'css' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + +### min-blend-mode + +这个属性指定元素的内容应该如何与其直接父级背景混合。可以选择很多值,类似: +min-blend-mode: screen | overlay等等 +可以用来做很多有意思的效果。 +[https://www.w3schools.com/cssref/pr_mix-blend-mode.asp](https://www.w3schools.com/cssref/pr_mix-blend-mode.asp) + +浏览器支持情况: +![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1590135302656-8a76406b-38c9-433d-83f4-00bf106e4447.png#align=left&display=inline&height=46&margin=%5Bobject%20Object%5D&name=image.png&originHeight=91&originWidth=1773&size=18757&status=done&style=none&width=886.5) diff --git a/src/CSS/Viewport.md b/src/CSS/Viewport.md new file mode 100644 index 0000000..bb639ec --- /dev/null +++ b/src/CSS/Viewport.md @@ -0,0 +1,68 @@ +--- +title: '100vh, 100dvh, 100lvh, 100svh' # 当前页面内容标题,默认为 Markdown 文件中的第一个 h1 标签内容 +shortTitle: '' # 当前页面的短标题 +description: '' # 当前页面内容描述 +icon: '' # 当前页面图标的 FontClass 或文件路径 (建议填写)。 +author: { + name: 'Song' +} +isOriginal: true # 当前文章是否为原创。 +date: 2024-04-16 # 写作时间。 +category: '' # 分类 +tag: '' # 标签 +sticky: 100 # 是否在列表中置顶。当填入数字时,数字越大,排名越靠前 +star: true # 是否收藏在博客主题的文章列表中。当填入数字时,数字越大,排名越靠前。 +article: true # 是否将该文章添加至文章列表中 +timeline: true # 是否将该文章添加至时间线中 +image: '' # 设置预览图 (分享图),请填入绝对路径 +editLink: false # 设置横幅图片 (宽屏分享图),请填入绝对路径。 +--- + +- [Learn These Viewport-Relative CSS Units (100vh, 100dvh, 100lvh, 100svh)](https://webdesign.tutsplus.com/learn-these-viewport-relative-css-units-100vh-100dvh-100lvh-100svh--cms-108537t) + +- [看图说话,新 CSS 单位 “svh” “dvh” 原来如此](https://juejin.cn/post/7172332295058751496?searchId=2024041610280856F4151AFCFD08FCCE78) + +## 100vh + +屏幕高度,在移动端时,视口大小受是否存在动态工具栏的影响。地址栏和标签页栏等界面。 + +![alt text](image.png) + +## 100dvh + +dynamic viewport height + +动态视口高度-[w3c文档](https://www.w3.org/TR/css-values-4/#dynamic-viewport-size) + +![alt text](image-1.png) + +> 无论工具栏是否存在,该单元总是尝试匹配视口高度.可能会导致页面滚动时跳跃和闪烁。 + +## 100lvh + +large viewport height + +大型视口高度-[w3c文档](https://www.w3.org/TR/css-values-4/#large-viewport-size) + +![alt text](image-2.png) + +不管有没有UA,都是等于最大的视口高度 + +## 100svh + +small viewport height + +小型视口高度-[w3c文档](https://www.w3.org/TR/css-values-4/#small-viewport-size) + +![alt text](image-3.png) + +在展开工具栏时,不会溢出。但是在收起工具栏时,会留白 + +## 向下兼容的写法 + +```css +.hero { + height: 100svh; + height: 100vh; +} +``` diff --git a/src/CSS/image-1.png b/src/CSS/image-1.png new file mode 100644 index 0000000..7bfb7ff Binary files /dev/null and b/src/CSS/image-1.png differ diff --git a/src/CSS/image-2.png b/src/CSS/image-2.png new file mode 100644 index 0000000..7a996c2 Binary files /dev/null and b/src/CSS/image-2.png differ diff --git a/src/CSS/image-3.png b/src/CSS/image-3.png new file mode 100644 index 0000000..d8b7f59 Binary files /dev/null and b/src/CSS/image-3.png differ diff --git a/src/CSS/image.png b/src/CSS/image.png new file mode 100644 index 0000000..033c244 Binary files /dev/null and b/src/CSS/image.png differ diff --git "a/src/CSS/\344\272\224\344\270\252\346\234\200\346\226\260CSS\347\211\271\346\200\247\345\217\212\344\275\277\347\224\250\346\221\230\350\256\260.md" "b/src/CSS/\344\272\224\344\270\252\346\234\200\346\226\260CSS\347\211\271\346\200\247\345\217\212\344\275\277\347\224\250\346\221\230\350\256\260.md" new file mode 100644 index 0000000..5056611 --- /dev/null +++ "b/src/CSS/\344\272\224\344\270\252\346\234\200\346\226\260CSS\347\211\271\346\200\247\345\217\212\344\275\277\347\224\250\346\221\230\350\256\260.md" @@ -0,0 +1,72 @@ +--- + title: '五个最新CSS特性及使用摘记' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2019-06-21 + category: '' + tag: 'css' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ## 原文地址:[https://zhuanlan.zhihu.com/p/40736286](https://zhuanlan.zhihu.com/p/40736286) +## 1. display: contents +> The element itself does not generate any boxes, but its children and pseudo-elements still generate boxes as normal. For the purposes of box generation and layout, the element must be treated as if it had been replaced with its children and pseudo-elements in the document tree. + +元素本身不产生任何边界框,而元素的子元素与伪元素仍然生成边界框,元素文字照常显示。为了同时照顾边界框和布局,处理这个元素时要想象这个元素不在元素树形结构里,而只有内容留下。包括子元素和伪元素 + +比如在一个父元素声明为flex时,子元素会依照flex规则进行显示排列,但是子元素的子元素并不会受flex规则影响,这时设置子元素的display为contents,那么子元素的子元素就会老老实实的受它爷爷辈元素的影响了。 + +## 2. @supports 查询特性 +可以理解为css的能力检测 + +```css +@support (display: contents) { + .hasContents { + display: contents + } +} +@support not(display: contents) { + .hasContents: { + display: flex + } +} +``` + +如上,可以判断是否支持所列的css属性,并且支持 `not ` `and` `or` 关键字 + +## 3. overflow-behavior +在针对滚动链接的现象时,使用这个属性,可以控制页面滚动的影响。 +这个属性有三个值 +> - `auto`:其默认值。元素(容器)的滚动会传播给其祖先元素。有点类似JavaScript中的冒泡行为一样 +> - `contain`:阻止滚动链接。滚动行为不会传播给其祖先元素,但会影响节点内的局部显示。例如,Android上的光辉效果或iOS上的回弹效果。当用户触摸滚动边界时会通知用户。注意,`overscroll-behavior:contain`在`html`元素上使用,可以阻止导航滚动操作 +> - `none`:和`contain`一样,但它也可以防止节点本身的滚动效果 + + +值得注意的是,这个属性还不是W3C标准,而是Web孵化器WICG的一个建议。不过,说不定哪一天,这个特性就进入到W3C工作组中,成为W3C的一个标准。 + +## 4. :focus-within        :placeholder-shown +:focus-within 有点类似于伪元素选择器 `:before` `:focus`  +但是它的生效条件是 **只要任意后代元素获得焦点,那么它就会被匹配** . + +:placeholder-shown  +![](https://cdn.nlark.com/yuque/0/2019/jpeg/297368/1561098417624-04ff5ed0-294e-40d4-b8f2-e14487cf5d75.jpeg#align=left&display=inline&height=275&originHeight=379&originWidth=720&size=0&status=done&width=522) +首先要先分清楚,`:placeholder-shown`和`::placeholder`是不同的两个东东。神奇的是`:placholder-shown`是W3C标准规范的一个属性,而`::placeholder`却不是。`::placeholder-shown`仍然会影响占位符文本的样式。 + +## 5. Containment +使用CSS Containment,我们可以把页面的部分圈起来,然后说“这里发生了什么,只在这里做相应的事”。这也是另一种方法,可以保护元素不受外部的变化而受影响。 + +CSS的Containment是一个新属性,使用关键字`contain`,它支持四个属性值: + +- `layout`:这个值打开该元素的布局控制。这确保所包含元素对布局目的完全不透明;外部不能影响其内部布局,反之亦然 +- `paint`:这个值打开该元素的绘制控制。这确保包含元素的后代节点不显示在其边界外,因此,如果一个元素在屏幕外或是不可见的,它的后代节点同样也被保证是不可见的 +- `size`:这个值打开该元素的尺寸控制。这确保包含元素可以无需检查其后代节点进行布局。 +- `style`:这个值打开该元素的样式控制。这确保了,对于性能这会不仅仅作用于一个元素及其后代,这些效果也不忽视包含的元素 diff --git "a/src/Docker/docker\345\244\204\347\220\206.md" "b/src/Docker/docker\345\244\204\347\220\206.md" new file mode 100644 index 0000000..112e28e --- /dev/null +++ "b/src/Docker/docker\345\244\204\347\220\206.md" @@ -0,0 +1,38 @@ +--- + title: 'docker处理' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2020-06-01 + category: '' + tag: 'docker' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ### 前端docker容器 +前端的镜像里面包含nginx服务,外部请求过来后,会先经过load-balance(以下简称LB)来进行一个转发,比如技能开发的URL为/skill/,LB会将此路径的请求转发到skill-ui容器,skill-ui容器里面nginx会依据配置,将/skillAPI/路径下的请求转发到配置好的skill_backend_url容器。 +下图是网页的一个请求: +![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1590117726141-0eb1c90c-c1c7-4b5f-8c8e-0e8923653de6.png#align=left&display=inline&height=144&name=image.png&originHeight=144&originWidth=635&size=17876&status=done&style=none&width=635) +下图是skill-ui中nginx的配置: +![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1590117547511-c3979fc2-2ad1-4ff6-9fca-a6acecfa45a0.png#align=left&display=inline&height=674&name=image.png&originHeight=674&originWidth=1446&size=90942&status=done&style=none&width=1446) + +所以可以大概得出: +![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1590119860165-04b287ed-c585-44a7-a416-32a3f59940c5.png#align=left&display=inline&height=317&name=image.png&originHeight=462&originWidth=811&size=25120&status=done&style=none&width=556) + +### docker 容器集群 +每个容器都有自己的虚拟ip,docker会有类似网关的管理者,比如skill-backend容器重启之后,它的虚拟ip会变化,这时路由管理者就会重新去注册新的ip路由表,保证与其他容器的正常连通。 +通过设置netword来将多个容器组成一个局域网 +#### 健康检查 +每个容器都可以设置一个健康检查,每隔一段时间去curl当前容器内的nginx端口服务。 + +#### swarm +比如现在有1,2,3个服务器。a服务是部署在1上的。如果a挂了,那么就会在2上重新部署a + diff --git "a/src/Docker/\345\237\272\346\234\254\346\246\202\345\277\265\345\217\212\345\270\270\347\224\250\345\221\275\344\273\244.md" "b/src/Docker/\345\237\272\346\234\254\346\246\202\345\277\265\345\217\212\345\270\270\347\224\250\345\221\275\344\273\244.md" new file mode 100644 index 0000000..5bf0608 --- /dev/null +++ "b/src/Docker/\345\237\272\346\234\254\346\246\202\345\277\265\345\217\212\345\270\270\347\224\250\345\221\275\344\273\244.md" @@ -0,0 +1,59 @@ +--- + title: '基本概念及常用命令' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2020-07-07 + category: '' + tag: 'docker' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ### 镜像image +分层存储 +### 容器container +基于镜像的最上层再加一层,作为容器的存储层。他和镜像的关系类似与**类**和**实例** +### 仓库 + + +### 常用命令 +```bash +docker run --name web2 -d -p 81:80 nginx:v2 //基于nginx:v2镜像启动名为web2的容器并映射本机81端口到容器的80端口 +docker image ls // 列出镜像 +docker image ls -a // 列出中间层镜像 +docker container ls // 列出容器 +docker rmi $(docker images -a -q) // 删除没有使用的镜像 +docker ps |grep $(name) // 筛选过滤 +``` + + +### compose +它允许用户通过一个docker-compose.yml模板文件来定义一组相关联的容器为一个项目 +#### compose中的两个重要概念 +##### 服务(service):一个应用的容器,实际上可以包括若干运行相同镜像的 +##### 项目(project):由一组关联的应用容器组成的一个完整业务单元,在docker-compose.yml文件中定义 + +### 挂载操作 +挂载是指两者中做出的修改都会影响另一方 +``pwd`:/usr/share/nginx/html:ro -v` +上面的意思是将本地的目录和容器内部的html文件关联挂载,只读 +` `pwd`"/deploy/dev/nginx.conf":/etc/nginx/conf.d/default.conf` + 上面的意思是将本地目录下的nginx.cong和容器内部的default.conf挂载关联 + + +### 进入对应容器 +```bash +docker exec -it $(容器id) sh +``` + + + + diff --git "a/src/Docker/\346\242\263\347\220\206\345\274\200\345\217\221\351\203\250\347\275\262\346\265\201\347\250\213.md" "b/src/Docker/\346\242\263\347\220\206\345\274\200\345\217\221\351\203\250\347\275\262\346\265\201\347\250\213.md" new file mode 100644 index 0000000..da321df --- /dev/null +++ "b/src/Docker/\346\242\263\347\220\206\345\274\200\345\217\221\351\203\250\347\275\262\346\265\201\347\250\213.md" @@ -0,0 +1,40 @@ +--- + title: '梳理开发部署流程' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2020-05-22 + category: '' + tag: 'docker' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ### 本地开发 +在本地开发中,是先运行deploy/dev下的run_ui.sh +这个shell命令的作用是配置好nginx,使用docker-compose配置文件的方式,本地起一个docker容器 +接着起本地项目,然后实现本地开发 +#### docker-compose:  +我们知道使用一个 `Dockerfile` 模板文件,可以让用户很方便的定义一个单独的应用容器。然而,在日常工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个 Web 项目,除了 Web 服务容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括负载均衡容器等。 +`Compose` 恰好满足了这样的需求。它允许用户通过一个单独的 `docker-compose.yml` 模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。 +#### 审核平台 +审核平台开发环境并没有用到docker,还是用的webpack的proxy代理 +#### 技能平台 +如上,是用的docker +### 技能平台vue-cli3与2的打包对比 +#### cli3没用懒加载 +![image.png](https://cdn.nlark.com/yuque/0/2019/png/297368/1571889259540-c18f93b0-d2b6-48a9-9787-d968d1d9a7c0.png#align=left&display=inline&height=182&name=image.png&originHeight=364&originWidth=1708&size=85322&status=done&style=none&width=854) + +#### cli2老项目 +![image.png](https://cdn.nlark.com/yuque/0/2019/png/297368/1571889336458-beff4987-4c18-403f-9e43-5c680054f5ec.png#align=left&display=inline&height=228&name=image.png&originHeight=456&originWidth=1562&size=162596&status=done&style=none&width=781) + + +### 新流程 + diff --git a/src/GraphQL/Apollo.md b/src/GraphQL/Apollo.md new file mode 100644 index 0000000..d7f5ac1 --- /dev/null +++ b/src/GraphQL/Apollo.md @@ -0,0 +1,145 @@ +--- + title: 'Apollo Federation' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2023-09-25 + category: '' + tag: 'GraphQL Apollo' + sticky: 1 + star: true + article: true + timeline: true + image: '' + editLink: false +--- + +## 前言 + +阿波罗联邦是一种架构模式 + +![Alt text](image.png) + +如上图,以一个router为入口,多个子图组成了一个supergraph。 + +每个子图不限技术栈,不限团队。 + +### Router + +Router其实是一个服务,也可以包含鉴权、缓存、日志等功能,可以看成一个api gateway。 + +可以通过GraphOS去使用它 + +也可以下载它的二进制文件,去本地运行它(当然,需要一些额外配置) + +### 子图 + +每个子图其实就是一个grapgql service 实例。里面需要实现对应的resolvers + +如上描述,子图可以使用任意的兼容的 GraphQL 服务器库。不限于javascript、java、py等 + +## router实现 + +基于apollo下,router的实现有两种选择 + +- Apollo Router + +- 基于Apollo Service 的`@apollo/gateway`库 + +第二种方式没啥好说的,就是按照`@apollo/gateway`的方式来组装,看文档编写。 + +如果要使用第一种,官方推荐是使用Apollo GraphOS + Apollo Router 这种方式去管理超图和子图(虽然方便但是要$),其实就是购买saas服务,虽然有免费版本。 + +想使用Apollo Router但是不想依赖GraphOS,希望本地去运营的,有没有其他方法? + +答案是: 有 + +[Local composition](https://www.apollographql.com/docs/federation/quickstart/local-composition) + +## The Rover CLI + +Rover 是用于使用 Apollo GraphOS 管理和维护图形的命令行界面。 + +它能够通过声明的yaml文件,去组装一个超图。并输出对应的超图yaml + +## supergraph的本地生成 + +```yaml +# supergraph-config.yaml +federation_version: 2 +subgraphs: + locations: + routing_url: https://flyby-locations-sub.herokuapp.com/ + schema: + subgraph_url: https://flyby-locations-sub.herokuapp.com/ + reviews: + routing_url: https://flyby-reviews-sub.herokuapp.com/ + schema: + subgraph_url: https://flyby-reviews-sub.herokuapp.com/ + +``` + +Rover的运行需要如上面的yaml,可以看到其中声明了subgraphs集合及对应的路径。 + +```bash +rover supergraph compose --config ./supergraph-config.yaml +``` + +运行了`supergraph compose`命令去进行组装操作 + +```bash +./router --supergraph=supergraph.graphql +``` + +以生成的超图作为配置文件运行Apollo Router的二进制文件就好了。 + +## subgraph的实现 + +到此,我们知道了Router的使用,以及如何构造一个超图。但是我们还不知道子图是怎么来的。我们只知道子图是一个Apollo Service 及一个长成这样的`https://flyby-locations-sub.herokuapp.com/`URL链接 + +接着往下看 + +### 基于Apollo Server构造 + +构造一个subgraph服务,需要依赖`@apollo/subgraph`和`graphql-tag`库, + +```shell +npm install @apollo/subgraph graphql-tag +``` + +然后转化类型和Query + +```ts +const typeDefs = gql` + type Book { + title: String + author: String + } + + type Query { + books: [Book] + } +`; +``` + +> 我们将模式包装在 gql 标签中,将其转换为 AST(即 DocumentNode)。我们这样做是因为下面的 buildSubgraphSchema 函数要求我们传入的模式是 DocumentNode 类型 + +然后创建server + +```ts +const server = new ApolloServer({ + schema: buildSubgraphSchema({ typeDefs, resolvers }), +}); +``` + +## 总结 + +上面一套下来,实现的就是 + +基于本地Apollo Router去实现的联邦模式 + +当然,细节问题需要进一步处理。 + diff --git a/src/GraphQL/BFF.png b/src/GraphQL/BFF.png new file mode 100644 index 0000000..a11c865 Binary files /dev/null and b/src/GraphQL/BFF.png differ diff --git "a/src/GraphQL/BFF\346\236\266\346\236\204.png" "b/src/GraphQL/BFF\346\236\266\346\236\204.png" new file mode 100644 index 0000000..0892950 Binary files /dev/null and "b/src/GraphQL/BFF\346\236\266\346\236\204.png" differ diff --git a/src/GraphQL/GraphQL-BFF.md b/src/GraphQL/GraphQL-BFF.md new file mode 100644 index 0000000..a089352 --- /dev/null +++ b/src/GraphQL/GraphQL-BFF.md @@ -0,0 +1,124 @@ +--- +title: '基于GraphQL的BFF实现' # 当前页面内容标题,默认为 Markdown 文件中的第一个 h1 标签内容 +shortTitle: '' # 当前页面的短标题 +description: '' # 当前页面内容描述 +icon: '' # 当前页面图标的 FontClass 或文件路径 (建议填写)。 +author: { + name: 'Song' +} +isOriginal: true # 当前文章是否为原创。 +date: 2024-03-19 # 写作时间。 +category: '' # 分类 +tag: 'BFF' # 标签 +sticky: 1 # 是否在列表中置顶。当填入数字时,数字越大,排名越靠前 +star: true # 是否收藏在博客主题的文章列表中。当填入数字时,数字越大,排名越靠前。 +article: true # 是否将该文章添加至文章列表中 +timeline: true # 是否将该文章添加至时间线中 +image: '' # 设置预览图 (分享图),请填入绝对路径 +editLink: false # 设置横幅图片 (宽屏分享图),请填入绝对路径。 +--- +## 背景 + +关键字: + +- 中台 +- 聚合 +- 按需 + +解决服务端单一稳定与端的差异灵活诉求之间的矛盾 + +## 技术选型 + +GraphQL: 图查询语言 + +[查看语言支持GraphQL](https://graphql.org/code/) + +- Client: Vue + Vue Apollo +- Server: NestJS + Apollo Server + +## 实现 + +### 客户端实现Demo + +```js + +npm install --save graphql graphql-tag @apollo/client + +npm install --save @vue/apollo-composable + +``` + +```js + +// graphql实例 +const httpLink = createHttpLink({ + // You should use an absolute URL here + uri: 'http://localhost:3000/graphql' +}) + +// Cache implementation +const cache = new InMemoryCache() + +// Create the apollo client +const apolloClient = new ApolloClient({ + link: httpLink, + cache +}) + +const app = createApp({ + setup() { + provide(DefaultApolloClient, apolloClient) + }, + + render: () => h(App) +}) + +``` + +### 服务端实现Demo + +#### Schema fist(单个graphql-server) + +请看具体工程演示 + +#### 联邦模式(多个graphql-server) + +基于apollo router的实现demo,请看具体工程演示 + +### BFF概览 + +[架构图概览](https://confluence.gyenno.com/pages/viewpage.action?pageId=59247288) + +## 需要进一步明确的问题 + +综上: + +加入BFF一层,是后端与前端的解耦。不用GraphQL也能解决服务端单一稳定与端的差异灵活诉求之间的矛盾。 + +只是GraphQL天然支持,对前端更友好,但是对后端挑战也更大。 + +### 技术架构问题 + +- 前端BFF还是后端BFF + +- 是不是可以不用graphQL + +- BFF内部结构如何梳理,单个BFF还是多个BFF + +- BFF与上下游的职责划分,规则定制 + +### 成本问题 + +- 作为承接页面和接口的中间商,需要额外的人力投入维护迭代 + +- 需要额外的学习成本 + +- 会有一定的试错成本 + +- 团队人员流动的替代成本 + +### 落地收益问题 + +- 当前能切入的业务点是什么? + +- 前期可以预测到,需要double time的投入,后期的效率提升又该从哪些维度评估 diff --git a/src/GraphQL/apollo_plugin.png b/src/GraphQL/apollo_plugin.png new file mode 100644 index 0000000..54229a6 Binary files /dev/null and b/src/GraphQL/apollo_plugin.png differ diff --git a/src/GraphQL/graphql-web.png b/src/GraphQL/graphql-web.png new file mode 100644 index 0000000..293f5fe Binary files /dev/null and b/src/GraphQL/graphql-web.png differ diff --git a/src/GraphQL/image.png b/src/GraphQL/image.png new file mode 100644 index 0000000..5c9902b Binary files /dev/null and b/src/GraphQL/image.png differ diff --git "a/src/GraphQL/\345\205\263\344\272\216dto\347\233\256\345\275\225\345\222\214models\347\233\256\345\275\225\347\232\204\345\214\272\345\210\253.md" "b/src/GraphQL/\345\205\263\344\272\216dto\347\233\256\345\275\225\345\222\214models\347\233\256\345\275\225\347\232\204\345\214\272\345\210\253.md" new file mode 100644 index 0000000..b16767e --- /dev/null +++ "b/src/GraphQL/\345\205\263\344\272\216dto\347\233\256\345\275\225\345\222\214models\347\233\256\345\275\225\347\232\204\345\214\272\345\210\253.md" @@ -0,0 +1,41 @@ +--- +title: 'models` 目录和 `dto` 目录的主要区别' # 当前页面内容标题,默认为 Markdown 文件中的第一个 h1 标签内容 +shortTitle: '' # 当前页面的短标题 +description: '' # 当前页面内容描述 +icon: '' # 当前页面图标的 FontClass 或文件路径 (建议填写)。 +author: { + name: 'Song' +} +isOriginal: true # 当前文章是否为原创。 +date: 2024-03-19 # 写作时间。 +category: '' # 分类 +tag: 'BFF' # 标签 +sticky: 1 # 是否在列表中置顶。当填入数字时,数字越大,排名越靠前 +star: true # 是否收藏在博客主题的文章列表中。当填入数字时,数字越大,排名越靠前。 +article: true # 是否将该文章添加至文章列表中 +timeline: true # 是否将该文章添加至时间线中 +image: '' # 设置预览图 (分享图),请填入绝对路径 +editLink: false # 设置横幅图片 (宽屏分享图),请填入绝对路径。 +--- + +**`models` 目录和 `dto` 目录的主要区别在于:** + +* **`models` 目录:** 通常用于定义与数据库表或业务实体一一对应的模型类。这些类通常包含与业务逻辑相关的所有属性和方法。 +* **`dto` 目录:** 通常用于定义用于数据传输的对象。这些类通常只包含数据本身,不包含任何业务逻辑。 + +**以下是一些更具体的区别:** + +* **粒度:** `models` 目录中的模型类通常粒度更细,每个类对应一个具体的数据库表或业务实体。`dto` 目录中的 DTO 类粒度通常更粗,一个 DTO 类可以包含多个数据库表或业务实体的数据。 +* **职责:** `models` 目录中的模型类通常负责与数据库交互,并封装业务逻辑。`dto` 目录中的 DTO 类只负责数据传输,不包含任何业务逻辑。 +* **使用场景:** `models` 目录中的模型类通常在业务层使用。`dto` 目录中的 DTO 类通常在表现层和数据访问层使用。 + +**以下是一些示例:** + +* **`models` 目录:** + * `User.model.ts`:定义用户模型,包含 `id`、`username`、`password` 等属性和方法。 + * `Order.model.ts`:定义订单模型,包含 `id`、`user_id`、`product_id` 等属性和方法。 +* **`dto` 目录:** + * `LoginDto.ts`:用于登录时传输的数据,包含 `username` 和 `password` 属性。 + * `OrderDto.ts`:用于展示订单信息的数据,包含 `id`、`user_name`、`product_name` 等属性。 + +**总之,`models` 目录和 `dto` 目录是两个不同的概念,它们在项目中扮演着不同的角色。将它们区分开来可以提高代码的清晰度和可维护性。** diff --git a/src/LowCode/tmagic-editor.md b/src/LowCode/tmagic-editor.md new file mode 100644 index 0000000..8183ea2 --- /dev/null +++ b/src/LowCode/tmagic-editor.md @@ -0,0 +1,50 @@ +--- + title: 'tmagic-editor' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2023-06-06 + category: '' + tag: 'lowcode' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + +## 可视化编辑器的实现思路 + +## 编辑器 + +- 编辑器可以理解为就是操作页面,分为左面板、工作区、头部工具栏和右面板。 + + 一般左面板是等待拖拽的组件,工作区是接收组件的地方,右面板是调整组件 + + 细节的地方,例如样式/事件等。 + + +工作区的画布如果需要理解左面板的组件,就需要拿到具体的函数去render,这里用到了 + +动态编译vue模版 + +## Runtime + +- Runtime就是指能实时显示你在工作区操作的结果,tmagic-editor为了实现render函数和编辑器解藕,所以不使用render的方式 + +Runtime的实现是新的一个工程,采用runtimeUrl的iframe嵌入方式去和编辑器互动。Runtime要渲染编辑器的组件,就需要拿到编辑器的dsl,那么就是通过监听message的方式去进行通信。 + +而且runtime也需要将dom同步给编辑器,不然编辑器不知道此刻你选择的是哪个组件 + +## DSL + +- tmagic-editor提供了三个版本的解析渲染组件(vue3/vue2/react),渲染组件**ui-xx** + + 是和Runtime对应的,每个DSL都需要对应的Runtime去解析。 + + +DSL解析的实现:通过component组件的 **is** 属性去动态渲染。例如。左面板有个type为my-button的组件,那么DSL里面就一定有一个组件名为my-button的组件。这是映射关系。 diff --git a/src/MiniProgram/Taro.md b/src/MiniProgram/Taro.md new file mode 100644 index 0000000..f919255 --- /dev/null +++ b/src/MiniProgram/Taro.md @@ -0,0 +1,68 @@ +--- + title: 'Taro' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2022-07-22 + category: '' + tag: '小程序' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + > 希望能够使用 React 语法写小程序的同时,通过「**Write once Run anywhere**」来实现跨端的。 + +##### [《小程序跨框架开发的探索与实践》演讲全文]() +### 选型参考 +#### 一、 +> ![image.png](https://cdn.nlark.com/yuque/0/2022/png/297368/1658458212376-b49929b8-a365-470c-9fa1-0acd1ed4cc25.png#clientId=u96ba7115-3cc6-4&from=paste&height=451&id=ud0ab9d1a&name=image.png&originHeight=444&originWidth=676&originalType=binary&ratio=1&rotation=0&showTitle=false&size=82176&status=done&style=none&taskId=u92fe5621-509a-4378-a80e-93634f9a3ed&title=&width=687) + +#### 二、 +![image.png](https://cdn.nlark.com/yuque/0/2022/png/297368/1658458298552-b9a62512-3add-48d3-b8ce-977e0a13b8ce.png#clientId=u96ba7115-3cc6-4&from=paste&height=543&id=u3fa7bf97&name=image.png&originHeight=602&originWidth=758&originalType=binary&ratio=1&rotation=0&showTitle=false&size=261352&status=done&style=none&taskId=u65aaf04a-a0b7-41ef-97a0-261314bcd29&title=&width=684) +### 架构 +#### 3之前的: +![image.png](https://cdn.nlark.com/yuque/0/2022/png/297368/1653530590832-13699126-4892-4797-abcb-d1739b0b407e.png#clientId=u69f0cedc-3d9c-4&from=paste&height=259&id=u5da2f416&name=image.png&originHeight=448&originWidth=796&originalType=binary&ratio=1&rotation=0&showTitle=false&size=95817&status=done&style=none&taskId=u19591826-32d3-4f6c-bd0c-fc1eb0286e6&title=&width=461) + +#### 3之后的 +![image.png](https://cdn.nlark.com/yuque/0/2022/png/297368/1658452800658-988fa750-2f13-4a19-855d-ce5c51b4cc75.png#clientId=u96ba7115-3cc6-4&from=paste&height=159&id=udb40ae85&name=image.png&originHeight=422&originWidth=1234&originalType=binary&ratio=1&rotation=0&showTitle=false&size=165055&status=done&style=none&taskId=u9678a04f-a053-4463-97f4-581ad1a7e73&title=&width=465) +##### 区别: +重编译和重运行时,而重运行时带来的好处是,模拟DOM/BOM API: +### ![image.png](https://cdn.nlark.com/yuque/0/2022/png/297368/1653536397477-589a3fed-bdab-47af-87ee-48d025fe255e.png#clientId=u69f0cedc-3d9c-4&from=paste&height=247&id=u87ed2785&name=image.png&originHeight=494&originWidth=1370&originalType=binary&ratio=1&rotation=0&showTitle=false&size=159837&status=done&style=none&taskId=uc60dad64-64b8-42cc-b01b-3454106d5d0&title=&width=685) +### mpvue +mpvue 本质上还是将 Vue 运行在了小程序,在platform目录下实现了小程序的转化。 +### Vue支持 + +- 从`v3.0.0`版本(01 Jul 2020)支持的vue3开发 + +### Taro小程序 + +1. 先基于cli为react/vue代码用webpack打包 +2. 运行时使用适配器进行适配,调用Taro实现的DOM/BOM API, +3. 渲染到小程序页面 + +### Taro DOM Tree 如何更新到页面 +#### React + +1. 先将小程序的组件模版化 +2. 将Taro Runtime 生成的Taro Dom Tree,去匹配模版 +3. 匹配遍历完Taro Tree之后,得到的就是小程序的组件Tree + +![image.png](https://cdn.nlark.com/yuque/0/2022/png/297368/1658458821752-0bb51c59-5d61-4998-8f2a-929d74689e44.png#clientId=u96ba7115-3cc6-4&from=paste&height=274&id=uabb70561&name=image.png&originHeight=359&originWidth=696&originalType=binary&ratio=1&rotation=0&showTitle=false&size=270486&status=done&style=none&taskId=u6e7485aa-b053-46f7-a46c-7c7856e76f7&title=&width=531) + +#### Vue +Vue 和 React 最大的区别就在于运行时的 CreateVuePage 方法,这个方法里进行了一些运行时的处理,比如:生命周期的对齐。其他的部分,如通过 BOM/DOM 方法构建、修改 DOM Tree 及渲染原理,都是和 React 一致的。 + +#### 更新 +无论是 React 还是 Vue ,最终都会调用 Taro DOM 方法,如:appendChild、insertChild 等。 +这些方法在修改 Taro DOM Tree 的同时,还会调用 enqueueUpdate 方法,这个方法能获取到每一个 DOM 方法最终修改的节点路径和值,如:{root.cn.[0].cn.[4].value: "1"},并通过 setData 方法更新到视图层。 +![image.png](https://cdn.nlark.com/yuque/0/2022/png/297368/1658458460741-8cb7c7e9-ab7d-4a35-910b-9a1700c76c5d.png#clientId=u96ba7115-3cc6-4&from=paste&height=265&id=uf0ed8f51&name=image.png&originHeight=364&originWidth=729&originalType=binary&ratio=1&rotation=0&showTitle=false&size=149898&status=done&style=none&taskId=u53490226-1b24-4e6e-81da-89f2b7f420d&title=&width=531.5) + +### Taro NPM +![image.png](https://cdn.nlark.com/yuque/0/2022/png/297368/1658458624594-3d7e4506-ec75-42d5-ad13-790cceb6eeea.png#clientId=u96ba7115-3cc6-4&from=paste&height=929&id=uabf7fb73&name=image.png&originHeight=929&originWidth=637&originalType=binary&ratio=1&rotation=0&showTitle=false&size=165954&status=done&style=none&taskId=u2ede6cc5-e263-48ac-bb1d-620a507cee5&title=&width=637) diff --git a/src/MiniProgram/image-2.png b/src/MiniProgram/image-2.png new file mode 100644 index 0000000..844bebf Binary files /dev/null and b/src/MiniProgram/image-2.png differ diff --git a/src/MiniProgram/image.png b/src/MiniProgram/image.png new file mode 100644 index 0000000..bae633e Binary files /dev/null and b/src/MiniProgram/image.png differ diff --git "a/src/MiniProgram/\345\276\256\344\277\241\345\260\217\347\250\213\345\272\217\345\205\263\351\224\256\346\246\202\345\277\265\344\270\200\350\247\210.md" "b/src/MiniProgram/\345\276\256\344\277\241\345\260\217\347\250\213\345\272\217\345\205\263\351\224\256\346\246\202\345\277\265\344\270\200\350\247\210.md" new file mode 100644 index 0000000..aa4b308 --- /dev/null +++ "b/src/MiniProgram/\345\276\256\344\277\241\345\260\217\347\250\213\345\272\217\345\205\263\351\224\256\346\246\202\345\277\265\344\270\200\350\247\210.md" @@ -0,0 +1,78 @@ +--- +title: '微信小程序关键概念一览' # 当前页面内容标题,默认为 Markdown 文件中的第一个 h1 标签内容 +shortTitle: '' # 当前页面的短标题 +description: '' # 当前页面内容描述 +icon: '' # 当前页面图标的 FontClass 或文件路径 (建议填写)。 +author: { + name: 'Song' +} +isOriginal: true # 当前文章是否为原创。 +date: 2024-04-03 # 写作时间。 +category: '' # 分类 +tag: '' # 标签 +sticky: 100 # 是否在列表中置顶。当填入数字时,数字越大,排名越靠前 +star: true # 是否收藏在博客主题的文章列表中。当填入数字时,数字越大,排名越靠前。 +article: true # 是否将该文章添加至文章列表中 +timeline: true # 是否将该文章添加至时间线中 +image: '' # 设置预览图 (分享图),请填入绝对路径 +editLink: false # 设置横幅图片 (宽屏分享图),请填入绝对路径。 +--- + +## 历史使命 + +微信中的webview逐渐成为移动web的入口。 + +基于微信JS API(​微信官方是没有对外暴露过如此调用的,此类 API 最初是提供给腾讯内部一些业务使用,很多外部开发者发现了之后,依葫芦画瓢地使用了,逐渐成为微信中网页的事实标准。)的能力,2015年初,微信发布了一整套网页开发工具包,称之为 JS-SDK,让所有开发者都可以使用到微信的原生能力,去完成一些之前做不到或者难以做到的事情。 + +JS-SDK逐渐暴露出一些问题,需要一个全新的系统来完成 + +- 快速的加载 + +- 更强大的能力 + +- 原生的体验 + +- 易用且安全的微信数据开放 + +- 高效和简单的开发 + +小程序呼之欲出 + +## 主题架构 + +- [参考链接](https://juejin.cn/post/7140509513852911647) + +![主题](image-2.png) + +和我们熟知的浏览器的JS线程和GUI线程互斥不同,小程序的渲染层和逻辑层是分开,两者互不影响。 +它们通过native系统层进行通行。 + +## 运行环境 + +宿主环境不同,小程序的运行环境也不同,我们所说的宿主,其实就是微信APP。 +![宿主](image.png) + +## 页面生命周期 + +![页面生命周期](https://res.wx.qq.com/wxdoc/dist/assets/img/page-lifecycle.2e646c86.png) + +## 小程序的生命周期 + +![小程序生命周期](https://res.wx.qq.com/wxdoc/dist/assets/img/life-cycle.5558d9eb.svg) + +需要知道小程序什么时候会被销毁 + +- 当小程序进入后台并被「挂起」后,如果很长时间(目前是 30 分钟)都未再次进入前台,小程序会被销毁。 +- 当小程序占用系统资源过高,可能会被系统销毁或被微信客户端主动回收。 + + +## 关于小程序更新的注意点 + +1. 开发者在后台发布新版本之后,无法立刻影响到所有现网用户,正常情况下,在全量发布 24 小时之后,新版本可以覆盖 99% 以上的用户。 +2. 小程序管理后台的「优先使用本地版本设置」和「小程序最低可用版本设置」不会影响同步更新与异步更新的选择。 + +## Skyline渲染引擎 + +- [拓展阅读](https://www.zhihu.com/question/546709238) + +相比于「双线程模型」的架构,Skyline 创建了一条渲染线程来负责 Layout, Composite 和 Paint 等渲染任务(从原来的1:1改成1:N,复用一个渲染器),并在AppService中新增渲染上下文来运行之前 WebView 承担的 JS 逻辑、DOM 树创建等逻辑。 diff --git "a/src/Monorepo/\346\235\203\351\231\220\350\256\276\350\256\241.md" "b/src/Monorepo/\346\235\203\351\231\220\350\256\276\350\256\241.md" new file mode 100644 index 0000000..ab846e4 --- /dev/null +++ "b/src/Monorepo/\346\235\203\351\231\220\350\256\276\350\256\241.md" @@ -0,0 +1,50 @@ +--- + title: '权限设计' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2023-12-28 + category: '' + tag: 'monorepo' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + +- [参考文档](https://mp.weixin.qq.com/s?__biz=MzkxNTE3ODU0NA==&mid=2247514194&idx=1&sn=c08cab11ec70f284dec66d3f69cd23d1&scene=21#wechat_redirect) + +## 思路 + +基于 Git 提供的钩子函数和基于 Gitlab 的能力。 + +## 问题 + +### 问题一 + +这里需要考虑的是只要开发者具备 Developer 权限,那么他就可以修改大仓任何目录下的代码,并且本地可以提交,这样会导致本地源码依赖出现很大的风险:会出现本地代码构建和生产环境构建不一致的情况,在研发流程意识不强的情况下很容易引发线上问题。本着对代码共享的原则,对于代码文件读权限不做控制,也允许研发修改代码,但是对修改的代码的发布会做流程上的强管控。这里就会涉及到 Gitlab 的分支保护机制以及文件 Owner 权限配置。 + +### 问题二 + +研发可以绕开 MR 的流程,直接本地合并代码到发布分支 + +## 解决思路 + +### 解决问题一 + +1. 在 GitLab 未支持文件目录权限设置之前,对于文件目录权限的控制主要依赖 Git 的钩子函数 + +2. 在 GitLab 开始支持文件目录权限设置,可以用于更细粒度的文件级别的权限控制,内部就支持文件目录和研发的权限映射关系 + +3. 强MR流程,需要对应的文件 Owner 进行确认评审 + +### 解决问题二 + +1. 分支策略,分支权限 + +2. git提交钩子函数 diff --git a/src/NestJS/AOP.webp b/src/NestJS/AOP.webp new file mode 100644 index 0000000..5387fd2 Binary files /dev/null and b/src/NestJS/AOP.webp differ diff --git a/src/NestJS/GraphQL.md b/src/NestJS/GraphQL.md new file mode 100644 index 0000000..500db30 --- /dev/null +++ b/src/NestJS/GraphQL.md @@ -0,0 +1,228 @@ +--- + title: 'GraphQL' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2022-07-22 + category: '' + tag: 'nest' + sticky: 1 + star: true + article: true + timeline: true + image: '' + editLink: false +--- + +## 初识 + +### NestJS + +nestjs是一个Node服务器端的企业级框架。 + +内置了graphql模块,提供对graphql的支持。 + +同时也依赖了@nestjs/apollo包,对应的也可以选择@nestjs/mercurius包 + +> focus on how to work with the built-in @nestjs/graphql module. The GraphQLModule can be configured to use Apollo server (with the @nestjs/apollo driver) and Mercurius (with the @nestjs/mercurius). We provide official integrations for these proven GraphQL packages to provide a simple way to use GraphQL with Nest (see more integrations here). + +### Apollo + +Apollo 是一组工具和社区成果,可帮助您在应用程序中使用 GraphQL。它以其客户端和服务器而闻名。 + +[前端开发使用GraphQL——服务端技术选型](https://cloud.tencent.com/developer/article/1831513) + +[5个Nest心动理由](https://juejin.cn/book/7226988578700525605/section/7238472356557570103) + +## 背景 + +主要解决矛盾:服务端单一稳定与端的差异灵活诉求之间的矛盾 + +### 端存在差异性 + +1. 场景数据源不一致。服务订单:app端需要所有类型的服务订单,小程序端只需要图文问诊的服务订单, +2. 页面展示字段不一致。app需要展示4个字段,小程序只需要1个字段 + +### 架构一 + +![Alt text](image.png) + +使用nestjs+graphql实现了一个graphql service,具体实现看服务端实现 + +[携程基于 GraphQL 的前端 BFF 服务开发实践](https://www.51cto.com/article/712429.html) + +那么问题就来了: + +#### 问题1: BFF操作数据是请求后端接口,还是直接连接数据库 + +如果请求后端接口,那么就会存在:GraphQL服务变成面向数据接口,而非面向数据网络 + +如果希望实践成面向数据网络,那么对现有RESTful API 的关联关系和设计需要重新评估和实践 + +> 原本 RESTful API 的接口,只是挂载到 GraphQL 的 Query 或 Mutation 的根节点下,未作其它改动。这种实践模式,只能有限发挥 GraphQL 合并请求、裁剪数据集的作用。它仍然是面向数据接口,而非面向数据网络的。 + +结论: + +还是以restful api的方式请求后端接口,并且graphql实践不会涉及到历史接口,只会在新模块中实行。 + +要做到面向数据网络,对后端api的设计要求较高,所以需要从这个层面来把控。 + +#### 问题2: 需不需要连接数据库 + +参考: +> 所有面向外部用户的 GraphQL 服务,我们会限制只能调用其他后端 API,以避免出现密集计算或者架构复杂的情况。只有面向内部用户的服务,才允许 GraphQL 服务直接访问数据库或者缓存。 + +结论: + +不需要,数据库的操作暂时不会放在其中。 + +### 架构二 + +[State of GraphQL Gateways in 2023](https://the-guild.dev/blog/state-of-graphql-gateways-in-2023) + +![Alt text](image-1.png) + +> A GraphQL gateway follows a workflow that adds functionality and acts as a proxy between the consumer and the actual GraphQL server(s) running the GraphQL schema(s) + +> The GraphQL server, on the other hand, implements the actual GraphQL schema and runs resolvers, dataloaders, and any other custom code required for fetching and connecting your data or entities. + +![Alt text](image-3.png) + +![Alt text](image-4.png) + +### 问题1: 需不需要GraphQL getaway + +需要:技术选型和demo实现? + +- [nestjs+graphql的联邦实现](https://docs.nestjs.com/graphql/federation) + +- [Apollo Router的应用](https://www.apollographql.com/docs/router) + +结论: + +目前来看如果是以前端bff来实现,还是需要GraphQL getaway的。至于哪种方式实现,需要进一步确认。 + +### 问题2: 前端BFF需不需要实现resolvers + +如果实现了,那么BFF其实就是个graphQl service? + +如果不实现,那么BFF该做的东西又是什么? + +结论: + +现在来看,这个resolvers的工作其实还是在bff,了解到不用实现resolvers的方式可以用apollo router去实现gateway,需要进一步确认。 + +### 问题3: apollo去集成客户端和服务端? + +如果我们都是使用apollo去集成客户端和服务端, + +同时BFF不实现resolvers + +那BFF要做的东西是什么?好像什么都不用做? + +结论: + +目前的demo是基于apollo去做的,应该也是较成熟和广泛的方案。 + +### 架构三 + +[GraphQL及元数据驱动架构在后端BFF中的实践](https://tech.meituan.com/2021/05/06/bff-graphql.html) + +![Alt text](image-2.png) + +这是后端BFF的实现架构 + +## 实现 + +### 客户端接入 + +[Vue Apollo](https://apollo.vuejs.org/) + +```js + +npm install --save graphql graphql-tag @apollo/client + +npm install --save @vue/apollo-composable + +``` + +```js + +// graphql实例 +const httpLink = createHttpLink({ + // You should use an absolute URL here + uri: 'http://localhost:3000/graphql' +}) + +// Cache implementation +const cache = new InMemoryCache() + +// Create the apollo client +const apolloClient = new ApolloClient({ + link: httpLink, + cache +}) + +const app = createApp({ + setup() { + provide(DefaultApolloClient, apolloClient) + }, + + render: () => h(App) +}) + +``` + +### 服务端实现 + +[nestjs的示例代码](https://github.com/nestjs/nest/tree/master/sample/) + +对于GraphQL的使用 + +两种模式: + +- code first: 使用TypeScript装饰器和类来直接定义GraphQL类型 +- schema first: 显式定义GraphQL类型 + +schema好处: +显式的schema文件,更方便不同团队的参与。简单明了 + +#### 构建超图的Federation模式 + +```js +@Module({ + imports: [ + GraphQLModule.forRoot({ + driver: ApolloGatewayDriver, + gateway: { + supergraphSdl: new IntrospectAndCompose({ + subgraphs: [ + { name: 'users', url: 'http://localhost:3000/graphql' }, + { name: 'posts', url: 'http://localhost:3001/graphql' }, + ], + }), + }, + }), + ], + controllers: [AppController], + providers: [AppService], +}) + +``` + +## 阶段总结 + +参考图 + +![Alt text](image-6.png) + +思考问题: 单个BFF还是多个BFF需要进一步明确?多个bff的架构及实现又该是怎样 + +- 学习成本/人力成本 + + 例如:后端服务构造完业务数据关系后,需要转化为对应的图关系,BFF需要应用这层图关系并且实现resolvers,然后在clien端再去联调 + +- 编码质量/子图设计/性能 等问题如何保证 diff --git a/src/NestJS/NestJS.md b/src/NestJS/NestJS.md new file mode 100644 index 0000000..6a060d1 --- /dev/null +++ b/src/NestJS/NestJS.md @@ -0,0 +1,73 @@ +--- +title: 'Nest指东南西北' # 当前页面内容标题,默认为 Markdown 文件中的第一个 h1 标签内容 +shortTitle: '' # 当前页面的短标题 +description: '' # 当前页面内容描述 +icon: '' # 当前页面图标的 FontClass 或文件路径 (建议填写)。 +author: { + name: 'Song' +} +isOriginal: true # 当前文章是否为原创。 +date: 2024-1-20 # 写作时间。 +category: '' # 分类 +tag: '' # 标签 +sticky: 100 # 是否在列表中置顶。当填入数字时,数字越大,排名越靠前 +star: true # 是否收藏在博客主题的文章列表中。当填入数字时,数字越大,排名越靠前。 +article: true # 是否将该文章添加至文章列表中 +timeline: true # 是否将该文章添加至时间线中 +image: '' # 设置预览图 (分享图),请填入绝对路径 +editLink: false # 设置横幅图片 (宽屏分享图),请填入绝对路径。 +--- + +## 一些文档 + +- [官方文档](https://docs.nestjs.com/) +- [翻译文档](http://nestjs.inode.club/) +- [翻译文档](https://nest.nodejs.cn/) + +## 概念 + +### MVC + +![mvc](./mvc.webp) + +### IOC + +依赖自动注入,不需要手动处理依赖关系及注入操作,一切都是根据装饰器声明自动扫描并创建注入。 + +那么nest是如何实现IoC的呢 --- Reflect MetaData + +[Nest 的实现原理?理解了 reflect metadata 就懂了](https://juejin.cn/post/7125066863150628900?searchId=2024011118224195363138895777B5DE8C) + +> Nest 的 Controller、Module、Service 等等所有的装饰器都是通过 Reflect.meatdata 给类或对象添加元数据的,然后初始化的时候取出来做依赖的扫描,实例化后放到 IOC 容器里。实例化对象还需要构造器参数的类型,这个开启 ts 的 emitDecoratorMetadata 的编译选项之后, ts 就会自动添加一些元数据,也就是 design:type、design:paramtypes、design:returntype 这三个,分别代表被装饰的目标的类型、参数的类型、返回值的类型。 + +### AOP + +![AOP](./AOP.webp) + +面向切面编程 + +- Middleware +- Guard +- Interceptor +- Pipe +- ExceptionFilter + +它们的调用顺序为: + +Middleware -> Guard -> Interceptor -> Pipe -> ExceptionFilter + +nest继承了express的概念,所以最外层会调用Middleware, +进入具体路由之后,接着调用Guard,例如权限判断,然后会调用Interceptor拦截器 +完成之后,调用Pipe对参数做处理,没问题后,才会进入Controller,然后匹配抛出的异常执行ExceptionFilter + +## 控制器 + +### 作用域 + +TODO + +## 模块(Modules) + +- 模块的生命周期是怎样的 +- 一个共享模块被修改值后,是否会影响到所有使用这个模块的其他模块 +- 同一模块被导入,又被导出的意义是什么 diff --git a/src/NestJS/image-1.png b/src/NestJS/image-1.png new file mode 100644 index 0000000..7d33771 Binary files /dev/null and b/src/NestJS/image-1.png differ diff --git a/src/NestJS/image-2.png b/src/NestJS/image-2.png new file mode 100644 index 0000000..f3b9879 Binary files /dev/null and b/src/NestJS/image-2.png differ diff --git a/src/NestJS/image-3.png b/src/NestJS/image-3.png new file mode 100644 index 0000000..23126a7 Binary files /dev/null and b/src/NestJS/image-3.png differ diff --git a/src/NestJS/image-4.png b/src/NestJS/image-4.png new file mode 100644 index 0000000..fa64043 Binary files /dev/null and b/src/NestJS/image-4.png differ diff --git a/src/NestJS/image-5.png b/src/NestJS/image-5.png new file mode 100644 index 0000000..6022ffd Binary files /dev/null and b/src/NestJS/image-5.png differ diff --git a/src/NestJS/image-6.png b/src/NestJS/image-6.png new file mode 100644 index 0000000..03f48b5 Binary files /dev/null and b/src/NestJS/image-6.png differ diff --git a/src/NestJS/image.png b/src/NestJS/image.png new file mode 100644 index 0000000..cce9f4b Binary files /dev/null and b/src/NestJS/image.png differ diff --git a/src/NestJS/mvc.webp b/src/NestJS/mvc.webp new file mode 100644 index 0000000..5bca127 Binary files /dev/null and b/src/NestJS/mvc.webp differ diff --git "a/src/NestJS/\344\275\234\347\224\250\345\237\237.md" "b/src/NestJS/\344\275\234\347\224\250\345\237\237.md" new file mode 100644 index 0000000..b50818d --- /dev/null +++ "b/src/NestJS/\344\275\234\347\224\250\345\237\237.md" @@ -0,0 +1,59 @@ +--- +title: '作用域' # 当前页面内容标题,默认为 Markdown 文件中的第一个 h1 标签内容 +shortTitle: '' # 当前页面的短标题 +description: '' # 当前页面内容描述 +icon: '' # 当前页面图标的 FontClass 或文件路径 (建议填写)。 +author: { + name: 'Song' +} +isOriginal: true # 当前文章是否为原创。 +date: 2024-04-18 # 写作时间。 +category: '' # 分类 +tag: '' # 标签 +sticky: 100 # 是否在列表中置顶。当填入数字时,数字越大,排名越靠前 +star: true # 是否收藏在博客主题的文章列表中。当填入数字时,数字越大,排名越靠前。 +article: true # 是否将该文章添加至文章列表中 +timeline: true # 是否将该文章添加至时间线中 +image: '' # 设置预览图 (分享图),请填入绝对路径 +editLink: false # 设置横幅图片 (宽屏分享图),请填入绝对路径。 +--- + +## 作用域 + +### Provider 作用域 + +默认使用单例作用域 + +| 类型 | 说明 | | +| -------- | -------- | -------- | +| DEFAULT | 该提供者的单一实例在整个应用程序中共享。实例的生命周期直接与应用程序的生命周期相关联。一旦应用程序启动,所有单例提供者都已被实例化。默认情况下使用单例作用域。 | | +| REQUEST | 为每个传入请求创建一个该提供者的新实例。实例会在请求完成处理后进行垃圾回收 | | +| TRANSIENT | 瞬态提供者在不同的消费者之间不共享。每个注入瞬态提供者的消费者都会收到一个新的、专用的实例。 | | + +```ts +@Injectable({ scope: Scope.REQUEST }) +``` + +## Controller 作用域 + +控制器也可以具有作用域,该作用域适用于在该控制器中声明的所有请求方法处理程序。与提供者作用域类似,控制器的作用域声明了其生命周期。对于基于请求的控制器,为每个传入请求创建一个新实例,并在请求处理完成后进行垃圾回收。 + +```ts + +@Controller({ + path: 'cats', + scope: Scope.REQUEST, +}) +export class CatsController {} + +``` + +## 作用域层级 + +- REQUEST作用域会向注入链上传播,A依赖B,B依赖C,假设B是REQUEST作用域,那么A也会成为REQUEST作用域,但是C不受影响 + +- TRANSIENT作用域不会影响依赖链上 + +## 持久性提供者 + +TODO diff --git "a/src/NestJS/\345\255\246\344\271\240\350\267\257\345\276\204.md" "b/src/NestJS/\345\255\246\344\271\240\350\267\257\345\276\204.md" new file mode 100644 index 0000000..191e94d --- /dev/null +++ "b/src/NestJS/\345\255\246\344\271\240\350\267\257\345\276\204.md" @@ -0,0 +1,37 @@ +--- +title: '学习路径' # 当前页面内容标题,默认为 Markdown 文件中的第一个 h1 标签内容 +shortTitle: '' # 当前页面的短标题 +description: '' # 当前页面内容描述 +icon: '' # 当前页面图标的 FontClass 或文件路径 (建议填写)。 +author: { + name: 'Song' +} +isOriginal: true # 当前文章是否为原创。 +date: 2024-03-21 # 写作时间。 +category: '' # 分类 +tag: '' # 标签 +sticky: 100 # 是否在列表中置顶。当填入数字时,数字越大,排名越靠前 +star: true # 是否收藏在博客主题的文章列表中。当填入数字时,数字越大,排名越靠前。 +article: true # 是否将该文章添加至文章列表中 +timeline: true # 是否将该文章添加至时间线中 +image: '' # 设置预览图 (分享图),请填入绝对路径 +editLink: false # 设置横幅图片 (宽屏分享图),请填入绝对路径。 +--- + +## 基础概念 + +### 理清技术栈的关系 + +### apollo server 中的plugin + +### NestJS核心概念 + +### GraphQL和Nest的关系 + +## 实现细节 + +### 理解module + +### http模块的封装 + +### 不在模型中的返回值类型 diff --git "a/src/NestJS/\350\243\205\351\245\260\345\231\250.md" "b/src/NestJS/\350\243\205\351\245\260\345\231\250.md" new file mode 100644 index 0000000..107e967 --- /dev/null +++ "b/src/NestJS/\350\243\205\351\245\260\345\231\250.md" @@ -0,0 +1,233 @@ +--- +title: '装饰器' # 当前页面内容标题,默认为 Markdown 文件中的第一个 h1 标签内容 +shortTitle: '' # 当前页面的短标题 +description: '' # 当前页面内容描述 +icon: '' # 当前页面图标的 FontClass 或文件路径 (建议填写)。 +author: { + name: 'Song' +} +isOriginal: true # 当前文章是否为原创。 +date: 2024-04-15 # 写作时间。 +category: '' # 分类 +tag: '' # 标签 +sticky: 101 # 是否在列表中置顶。当填入数字时,数字越大,排名越靠前 +star: true # 是否收藏在博客主题的文章列表中。当填入数字时,数字越大,排名越靠前。 +article: true # 是否将该文章添加至文章列表中 +timeline: true # 是否将该文章添加至时间线中 +image: '' # 设置预览图 (分享图),请填入绝对路径 +editLink: false # 设置横幅图片 (宽屏分享图),请填入绝对路径。 +--- + +> 拷贝自 [Nest通关秘籍](https://juejin.cn/book/7226988578700525605/section/7234726536342372412) + +

Nest 的功能都是大多通过装饰器来使用的,这节我们就把所有的装饰器过一遍。

+

我们创建个新的 nest 项目:

+
nest new all-decorator -p npm
+
+

+

Nest 提供了一套模块系统,通过 @Module声明模块:

+

+

通过 @Controller、@Injectable 分别声明其中的 controller 和 provider:

+

+

+

这个 provider 可以是任何的 class:

+

+

注入的方式可以是构造器注入:

+

+

或者属性注入:

+

+

属性注入要指定注入的 token,可能是 class 也可能是 string。

+

你可以通过 useFactory、useValue 等方式声明 provider:

+

+

这时候也需要通过 @Inject 指定注入的 token:

+

+

+

这些注入的依赖如果没有的话,创建对象时会报错。但如果它是可选的,你可以用 @Optional 声明一下,这样没有对应的 provider 也能正常创建这个对象。

+

+

如果模块被很多地方都引用,为了方便,可以用 @Global 把它声明为全局的,这样它 exports 的 provider 就可以直接注入了:

+

+

filter 是处理抛出的未捕获异常的,通过 @Catch 来指定处理的异常:

+

+

然后通过 @UseFilters 应用到 handler 上:

+

+

+

除了 filter 之外,interceptor、guard、pipe 也是这样用:

+

+

当然,pipe 更多还是单独在某个参数的位置应用:

+

+

这里的 @Query 是取 url 后的 ?bbb=true,而 @Param 是取路径中的参数,比如 /xxx/111 种的 111

+

+

+

此外,如果是 @Post 请求,可以通过 @Body 取到 body 部分:

+

+

我们一般用 dto 的 class 来接受请求体里的参数:

+

+

nest 会实例化一个 dto 对象:

+

用 postman 发个 post 请求:

+

+

可以看到 nest 接受到了 body 里的参数:

+

+

除了 @Get、@Post 外,还可以用 @Put、@Delete、@Patch、@Options、@Head 装饰器分别接受 put、delete、patch、options、head 请求:

+

+

handler 和 class 可以通过 @SetMetadata 指定 metadata:

+

+

然后在 guard 或者 interceptor 里取出来:

+

+

+

你可以通过 @Headers 装饰器取某个请求头 或者全部请求头:

+

+

+

通过 @Ip 拿到请求的 ip:

+

+

通过 @Session 拿到 session 对象:

+

+

但要使用 session 需要安装一个 express 中间件:

+
npm install express-session
+
+

在 main.ts 里引入并启用:

+

+

指定加密的密钥和 cookie 的存活时间。

+

然后刷新页面:

+

+

会返回 set-cookie 的响应头,设置了 cookie,包含 sid 也就是 sesssionid。

+

之后每次请求都会自动带上这个 cookie:

+

+

这样就可以在 session 对象里存储信息了。

+

+

+

@HostParam 用于取域名部分的参数:

+

我们再创建个 controller:

+
nest g controller aaa --no-spec --flat
+
+

+

这样指定 controller 的生效路径:

+
import { Controller, Get, HostParam } from '@nestjs/common';
+
+@Controller({ host: ':host.0.0.1', path: 'aaa' })
+export class AaaController {
+    @Get('bbb')
+    hello() {
+        return 'hello';
+    }
+}
+
+

controller 除了可以指定某些 path 生效外,还可以指定 host:

+

+

然后再访问下:

+

+

这时候你会发现只有 host 满足 xx.0.0.1 的时候才会路由到这个 controller。

+

host 里的参数就可以通过 @HostParam 取出来:

+
import { Controller, Get, HostParam } from '@nestjs/common';
+
+@Controller({ host: ':host.0.0.1', path: 'aaa' })
+export class AaaController {
+    @Get('bbb')
+    hello(@HostParam('host') host) {
+        return host;
+    }
+}
+
+

+

前面取的这些都是 request 里的属性,当然也可以直接注入 request 对象:

+

+

通过 @Req 或者 @Request 装饰器,这俩是同一个东西:

+

+

注入 request 对象后,可以手动取任何参数:

+

+

当然,也可以 @Res 或者 @Response 注入 response 对象,只不过 response 对象有点特殊:

+

+

当你注入 response 对象之后,服务器会一直没有响应:

+

+

因为这时候 Nest 就不会再把 handler 返回值作为响应内容了。

+

你可以自己返回响应:

+

+

+

Nest 这么设计是为了避免你自己返回的响应和 Nest 返回的响应的冲突。

+

如果你不会自己返回响应,可以通过 passthrough 参数告诉 Nest:

+

+

+

除了注入 @Res 不会返回响应外,注入 @Next 也不会:

+

+

当你有两个 handler 来处理同一个路由的时候,可以在第一个 handler 里注入 next,调用它来把请求转发到第二个 handler:

+

+

Nest 不会处理注入 @Next 的 handler 的返回值。

+

handler 默认返回的是 200 的状态码,你可以通过 @HttpCode 修改它:

+

+

+

当然,你也可以修改 response header,通过 @Header 装饰器:

+

+

+

此外,你还可以通过 @Redirect 装饰器来指定路由重定向的 url:

+

+

+

或者在返回值的地方设置 url:

+
@Get('xxx')
+@Redirect()
+async jump() {
+    return {
+      url: 'https://www.baidu.com',
+      statusCode: 302
+    }  
+}
+
+

你还可以给返回的响应内容指定渲染引擎,不过这需要先这样设置:

+

+
import { NestFactory } from '@nestjs/core';
+import { AppModule } from './app.module';
+import { NestExpressApplication } from '@nestjs/platform-express';
+import { join } from 'path';
+
+async function bootstrap() {
+  const app = await NestFactory.create<NestExpressApplication>(AppModule);
+
+  app.useStaticAssets(join(__dirname, '..', 'public'));
+  app.setBaseViewsDir(join(__dirname, '..', 'views'));
+  app.setViewEngine('hbs');
+
+  await app.listen(3000);
+}
+bootstrap();
+
+
+

分别指定静态资源的路径和模版的路径,并指定模版引擎为 handlerbars。

+

当然,还需要安装模版引擎的包 hbs:

+
npm install --save hbs
+
+

然后准备图片和模版文件:

+

+

+

在 handler 里指定模版和数据:

+

+

就可以看到渲染出的 html 了:

+

+

案例代码在小册仓库

+

总结

+

这节我们梳理了下 Nest 全部的装饰器

+ +

把这些装饰器用熟,就掌握了 nest 大部分功能了。

diff --git a/src/Node/Event Loop.md b/src/Node/Event Loop.md new file mode 100644 index 0000000..aa3402a --- /dev/null +++ b/src/Node/Event Loop.md @@ -0,0 +1,68 @@ +--- + title: 'Event Loop' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2023-07-18 + category: '' + tag: 'node' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + # 事件循环 + +## 官网简化图 + +``` + ┌───────────────────────────┐ +┌─>│ timers │ +│ └─────────────┬─────────────┘ +│ ┌─────────────┴─────────────┐ +│ │ pending callbacks │ +│ └─────────────┬─────────────┘ +│ ┌─────────────┴─────────────┐ +│ │ idle, prepare │ +│ └─────────────┬─────────────┘ ┌───────────────┐ +│ ┌─────────────┴─────────────┐ │ incoming: │ +│ │ poll │<─────┤ connections, │ +│ └─────────────┬─────────────┘ │ data, etc. │ +│ ┌─────────────┴─────────────┐ └───────────────┘ +│ │ check │ +│ └─────────────┬─────────────┘ +│ ┌─────────────┴─────────────┐ +└──┤ close callbacks │ + └───────────────────────────┘ + +``` + +## 概念理解 + +![Alt text](image.png) + +- 事件循环在node服务启动的时候就会运行 + +- node中的异步处理,是交给libuv来处理的 + +- 只有在调用堆栈为空后,事件循环才会开始发挥作用。 + + + +## 事件循环中,执行顺序遵循的一定规则 + +![Alt text](image-1.png) + +- 微任务队列中,nextTick的队列优先级大于promise队列 + +- 进行一次循环前,会优先执行微任务队列。且每个阶段完成后也会执行微任务队列 + +## 参考文章 + +1. [A Complete Visual Guide to Understanding the Node.js Event Loop](https://www.builder.io/blog/visual-guide-to-nodejs-event-loop) diff --git "a/src/Node/FS\347\233\270\345\205\263.md" "b/src/Node/FS\347\233\270\345\205\263.md" new file mode 100644 index 0000000..d8ff53f --- /dev/null +++ "b/src/Node/FS\347\233\270\345\205\263.md" @@ -0,0 +1,28 @@ +--- + title: 'FS相关' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2020-05-15 + category: '' + tag: 'node' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + fs模块是唯一一个同时提供同步和异步API的模块 + +- fs.readdirSync(__dirname) 同步读取一个文件 +- fs.readdir('.',function(err, files){}) 异步读取文件 + + +### Stream +流,比起使用fs.readFile和rs.writeFile方法对文件进行读写操作, +使用Stream API来进行读写操作可以避免想上面的方法一样需要一次分配内存来进行操作,stream可以分段处理,这样在读取大文件或者上传大文件时有足够的操作空间。 diff --git a/src/Node/image-1.png b/src/Node/image-1.png new file mode 100644 index 0000000..33e7db9 Binary files /dev/null and b/src/Node/image-1.png differ diff --git a/src/Node/image-2.png b/src/Node/image-2.png new file mode 100644 index 0000000..1d78125 Binary files /dev/null and b/src/Node/image-2.png differ diff --git a/src/Node/image-3.png b/src/Node/image-3.png new file mode 100644 index 0000000..67c8278 Binary files /dev/null and b/src/Node/image-3.png differ diff --git a/src/Node/image-4.png b/src/Node/image-4.png new file mode 100644 index 0000000..9e7d918 Binary files /dev/null and b/src/Node/image-4.png differ diff --git a/src/Node/image-5.png b/src/Node/image-5.png new file mode 100644 index 0000000..3e7fcae Binary files /dev/null and b/src/Node/image-5.png differ diff --git a/src/Node/image-6.png b/src/Node/image-6.png new file mode 100644 index 0000000..9f52215 Binary files /dev/null and b/src/Node/image-6.png differ diff --git a/src/Node/image.png b/src/Node/image.png new file mode 100644 index 0000000..e5d5035 Binary files /dev/null and b/src/Node/image.png differ diff --git a/src/Node/node_modules.md b/src/Node/node_modules.md new file mode 100644 index 0000000..fde614c --- /dev/null +++ b/src/Node/node_modules.md @@ -0,0 +1,97 @@ +--- + title: 'node_modules' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2022-03-23 + category: '' + tag: 'node' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + +原文链接[https://zhuanlan.zhihu.com/p/137535779](https://zhuanlan.zhihu.com/p/137535779) +### package +包含了package.json,使用package.json定义的一个package。通常是对应一个module,也可以不包含module。 +### module +能被require的,就是一个module,只有当module里面包含package.json的时候,它才叫package。 + +### Dependency Hell +依赖地狱。 +当A,C都依赖B,A依赖的是B的1.0.0版本。C依赖的是B的2.0.0版本。在B版本能支持多版本共存的情况下,npm如何解决保证让A,C都加载到自己想要的版本? +npm的解决方式是通过加载依赖时路径的查找算法和node_modules的目录结构这两者来配合解决的 +#### 查找算法 +递归向上查找node_modules里面的package +> eg. +> 如果在 `'/home/ry/projects/foo.js'` 文件里调用了 `require('bar.js')`,则 Node.js 会按以下顺序查找: +> - `/home/ry/projects/node_modules/bar.js` +> - `/home/ry/node_modules/bar.js` +> - `/home/node_modules/bar.js` +> - `/node_modules/bar.js` + +-- 递归向上 +-- 就近原则 +#### node_modules的目录结构 +![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1589538479785-9b9033a3-d0b6-40bb-95d7-bf9cdcbf86c5.png#height=310&id=z1aGJ&name=image.png&originHeight=982&originWidth=460&originalType=binary&ratio=1&rotation=0&showTitle=false&size=66172&status=done&style=none&title=&width=145) + +### nest mode(npm v2) +根据上面dependency hell的解决方案,我们可以想到如果acorn-jsx和acorn-dynamic-import同时依赖一个package的不同版本,只要在他们自己的目录下维护就好了。因为是就近原则。但是如果此时有另外一个模块,也依赖这个同acorn-jsx相同版本的package,那么就会导致同时存在两个相同版本的package。 +如果依赖的过多,会导致大量空间被浪费。这就是臭名昭著的node_modules hell + +### flat mode(npm v3) +同样,这个模式,是利用向上递归查找的原则,解决nest mode的重复依赖问题。它把重复的依赖提取为公共依赖,放到上一层的node_modules。 +但是,如果有四个模块,其中两个依赖了1.0.0版本,另外两个依赖了2.0.0版本,那么不论是把1.0.0放到上一层还是把2.0.0放到上一层,都会造成某个版本依赖两次。这时你可能会想:为啥不把1.0.0和2.0.0都放到上一层,这不就只要install一次吗。如果都放到上一层,我怎么保证我拿到的是1.0.0版本还是2.0.0版本? 这叫做doppelgangers +### 版本重复问题 +版本重复及同时存在多个版本,会出现什么问题? +#### 全局types冲突 +一些package会修改全局的类型定义,全局的types形成了命名冲突。解决方式就是自己控制包含哪些加载的 +#### 破坏单例模式 + +### Phantom dependecy +对比以上flat mode会比nest mode节省很多空间,同时也带来了phantom dependecy的问题。什么是phantom dependecy? +我们把一个库使用了不属于其dependencies里的package称之为phantom dependecy。我理解:A,C模块依赖1.0.0,现在把1.0.0提升一层,那么在AC的dependencies里面肯定没有1.0.0 +另外,在同一个库里面,有可能引用的依赖不在dependencies里面而是在devDependencies里面,我们本地开发运行没有问题,但是发布的话别人下载安装依赖就会有问题了。 +并且在使用monorepo管理项目的情况下,问题更加严重。一个package不但可能引入dev环境下的phantom dependecy,也有可能引入其他package的依赖。 +**在基于npm或者yarn的node_modules的结构下,doppelganger 和 phantom dependency这两个问题似乎并没有太好的解决方式。** + +### Semver(语意化版本) +semver的提出主要用于控制每个依赖package的影响范围,能实现系统的平滑升级和过度。 +![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1589783983490-06c804e9-d48f-485c-a09f-45500df54c76.png#height=80&id=PFxRL&name=image.png&originHeight=80&originWidth=388&originalType=binary&ratio=1&rotation=0&showTitle=false&size=7724&status=done&style=none&title=&width=388) +前面加个^表示npm install 的时候都会安装符合0.18.0约束的最新依赖。 +问题是。并不是所有的库都会遵循。所以。。。 +如果直接写死axios的版本依赖,但是不能保证axios的依赖也是写死。所以,packge-lock.json和yarn的lock文件就是实现这样的方式。 +如上图的package.json里面声明的axios依赖,我们在生成的package-lock.json文件中可以看到。axios所有的依赖及其依赖的依赖的版本都在lock文件中锁定了。这样其他人来使用这个package就能复现版本。 +**但是当我们第一次安装创建项目时或者第一次安装某个依赖的时候,此时即使第三方库里含有lock文件。但是npm install 并不会去读取三方依赖的lock,所以还是有可能触发bug。** +> ### Resolutions 救火队长 +> 如果你某天安装了一个新的webpack-cli,却发现这个webpack-cli并不能正常工作,经过一番定位发现,是该cli的一个上游依赖portfinder的最近一个版本有bug,但是该cli的作者在休假,没办法及时修复这个cli,但项目赶着上线该怎么处理?yarn提供了一个叫做[https://classic.yarnpkg.com/en/docs/selective-version-resolutions/](https://classic.yarnpkg.com/en/docs/selective-version-resolutions/)的机制,使得你可以忽略dependency的限制,强行将portfinder锁定为某个没有bug的版本,以解燃眉之急 +> npm本身没有提供resolution机制,但是可以通过`npm-froce-resolution`这个库实现类似机制 + + +### determinism +determinism指的是在给定package.json和lock文件下,每次重新install都会得到同样的node_modules的拓扑结构。 + +### PNPM +相比于yarn尽可能的将package放到root level,pnpm则是只将显式写明的dependency的依赖写入root-level的node_modules,这避免了业务里错误的引入隐式依赖的问题,即解决了phantom dependency + +pnpm不仅仅能保证一个项目里的所有package的每个版本是唯一的,甚至能保证你使得你不同的项目之间也可以公用唯一的版本(只需要公用store即可),这样可以极大的节省了磁盘空间。核心就在于pnpm不再依赖于node的递归向上查找node_modules的算法,因为该算法强依赖于node_modules的物理拓扑结构,这也是导致不同项目的项目难以复用node_modules的根源。(还有一种干法,就是使用代码的地方写死依赖的版本号,这是deno的干法) + +### cargo(全局store的包管理系统) + +## CJM/ESM + +1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。 +2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。 +3. CommonJs 是单个值导出,ES6 Module可以导出多个 +4. CommonJs 是动态语法可以写在判断里,ES6 Module 静态语法只能写在顶层 +5. CommonJs 的 this 是当前模块,ES6 Module的 this 是 undefined + + + diff --git "a/src/Node/\344\270\255\351\227\264\344\273\266.md" "b/src/Node/\344\270\255\351\227\264\344\273\266.md" new file mode 100644 index 0000000..af8e407 --- /dev/null +++ "b/src/Node/\344\270\255\351\227\264\344\273\266.md" @@ -0,0 +1,26 @@ +--- + title: '中间件' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2020-05-15 + category: '' + tag: 'node' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ### connect +对node http模块的封装,能更简洁更方便的调用功能 + +### express +对connect的封装,基于更上层的调用 + + diff --git "a/src/Node/\345\246\202\344\275\225\345\234\250node\344\270\255\346\233\264\345\245\275\347\232\204\344\275\277\347\224\250\351\233\206\347\276\244.md" "b/src/Node/\345\246\202\344\275\225\345\234\250node\344\270\255\346\233\264\345\245\275\347\232\204\344\275\277\347\224\250\351\233\206\347\276\244.md" new file mode 100644 index 0000000..a1fc22b --- /dev/null +++ "b/src/Node/\345\246\202\344\275\225\345\234\250node\344\270\255\346\233\264\345\245\275\347\232\204\344\275\277\347\224\250\351\233\206\347\276\244.md" @@ -0,0 +1,431 @@ +--- + title: '如何在node中更好的使用集群' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2023-09-01 + category: '' + tag: 'node' + sticky: 10 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + # 如何通过集群扩展 Node.js 应用程序 + +- [原文链接](https://www.digitalocean.com/community/tutorials/how-to-scale-node-js-applications-with-clustering#prerequisites) +- [演示代码](https://github.com/Capchen/cluster_demo) + +## 介绍 + +当你在一个多核CPU的系统上运行一个node程序,默认情况下会以单核的模式去创建一个进程。因为Node.js是以单线程的方式执行javascript代码,所以应用的所有请求都必须由单核上的线程去处理。如果应用程序有 CPU 密集型任务,操作系统必须安排它们共享单个 CPU,直到完成。如果单个进程收到太多请求,可能会导致该进程不堪重负,从而导致性能下降。如果进程崩溃了,用户也不能继续访问你的应用了。 + +Node.js引入了cluster模块去解决这个问题,它会在同一台计算机上创建同一个应用程序的多个副本并让它们同时运行。同时它也使用了[round-robin 算法](https://en.wikipedia.org/wiki/Round-robin_scheduling)去实现负载均衡。如果一个实例崩溃了,剩下运行中的实例依然可以为用户提供服务。得益于负载均衡,应用的性能也会显著提高。 + +在本教程汇总,你将会在一台拥有四个或更多个 CPU 的机器上,使用 Node.js 的集群(cluster)模块来扩展一个应用程序。您将创建一个不使用集群的应用程序,然后将该应用改进到使用集群模块。你还将使用 pm2 模块来将应用程序扩展到多个 CPU。你将使用负载测试工具来比较使用集群和不使用集群的应用程序的性能,以及评估 pm2 模块的表现。 + +## 准备 + +要跟随学习本教程,你需要: + +1. 大于或等于4核系统 + + a. 如果您使用的是 Ubuntu 22.04 远程服务器,您可以按照我们的[初始服务器设置](https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-22-04)来设置您的系统. + +2. 在您的开发环境中设置 Node.js(最好大于16)。 + +3. 对express的基本了解 + +## 步骤一:创建目录 + +在这一步中,你将创建项目的目录并下载应用程序所需的依赖。在第二步中,你将使用 Express 构建应用程序。然后在第三步中,你将使用内置的 node-cluster 模块将其扩展到多个 CPU,在第四步中会使用 loadtest 软件包进行压力测试。接着你将使用 pm2 软件包扩展当前应用,并在第五步中再次进行压力测试。 + +首先,创建一个目录。您可以将其命名为 cluster_demo 或您喜欢的任何目录名称,然后进入目录,接着对其初始化 + +``` bash +# 创建目录 +mkdir cluster_demo + +# 进入目录 +cd cluster_demo + +# 初始化 +npm init -y +``` + +-y 选项告诉 NPM 接受所有默认选项。 + +``` javascript +{ + "name": "cluster_demo", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} +``` + +这些属性需要与项目保持一致: + +- name:npm 包的名称。 +- version:你的包的版本号。 +- main:项目的入口点。 + +在 package.json 文件中,添加启用对 ES 模块的支持: + +``` javascript +{ + ... + "author": "", + "license": "ISC", + "type": "module" +} + +``` + +接着,下载一些依赖包 + +- express:一个用于在 Node.js 中构建 Web 应用程序的框架。 +- loadtest:一种负载测试工具,可用于生成应用程序的流量以测量其性能。 +- pm2:一种自动将应用程序扩展到多个 CPU 的工具。 + +``` zsh +npm install express + +npm install -g loadtest pm2 +``` + +## 步骤二:创建不使用集群的应用 + +在这一步中,您将创建一个包含单个路由的示例程序,该路由将在每个用户访问时启动CPU密集型任务。该程序不会使用集群模块,因此您可以访问在一个CPU上运行应用程序的单个实例的性能影响。在本教程的后面,您将比较这种方法与集群模块的性能。 + +首先,创建`index.js`文件 + +``` js +import express from "express"; + +const port = 3000; +const app = express(); + +console.log(`worker pid=${process.pid}`); +``` + +在第一行,导入express包。在第二行中,将端口变量设置为端口3000,应用程序的服务器将侦听该端口。接下来,将app变量设置为Express的实例。之后,您可以使用内置的进程模块在控制台中记录应用程序进程的进程ID。 + +然后,新增一个路由,它是一个CPU 密集的循环操作 + +``` js +... +app.get("/heavy", (req, res) => { + let total = 0; + for (let i = 0; i < 5_000_000; i++) { + total++; + } + res.send(`The result of the CPU intensive task is ${total}\n`); +}); +``` + +在/ heavy路径中,你定义了一个循环,将总变量增加了500万次。然后使用res. send ()方法发送包含总变量中的值的响应。虽然CPU受限任务的示例是任意的,但它演示了CPU受限任务,而没有增加复杂性。你也可以为路由使用其他名称,但本教程使用/ heavy表示繁重的性能任务。 + +接下来,调用 Express 模块的 Listen() 方法,让服务器监听存储在 port 变量中的端口 3000: + +```js +... +app.listen(port, () => { + console.log(`App listening on port ${port}`); +}); +``` + +`index.js`完整代码如下: + +```js +import express from "express"; + +const port = 3000; +const app = express(); + +console.log(`worker pid=${process.pid}`); + +app.get("/heavy", (req, res) => { + let total = 0; + for (let i = 0; i < 5_000_000; i++) { + total++; + } + res.send(`The result of the CPU intensive task is ${total}\n`); +}); + +app.listen(port, () => { + console.log(`App listening on port ${port}`); +}); +``` +启动服务 + +`node index.js` + +输出显示正在运行的进程的进程 ID 以及确认服务器正在侦听端口 3000 的消息。 要测试应用程序是否正常工作,请打开另一个终端并运行以下命令: + +```bash +worker pid=11023 +App listening on port 3000 +``` + +可以在浏览器中直接打开 +`http://localhost:3000/heavy` + +也可以在终端中执行curl +`curl http://localhost:3000/heavy` + +当你使用node命令运行index. js文件时,操作系统(OS)会创建一个进程。进程是操作系统为运行程序所做的抽象。操作系统为程序分配内存,并在进程列表中创建一个对应运行程序的进程ID。 + +程序二进制文件会被定位并加载到为进程分配的内存中。从那里开始执行。运行时,它对系统中的其他进程没有任何感知,并且在该进程中发生的任何事情都不会影响其他进程。 + +由于您在具有多个CPU的服务器上运行Node.js应用程序只有一个进程,因此它将接收并处理所有传入请求。在此图中,所有传入请求都被定向到在单个CPU上运行的进程,而其他CPU保持空闲: + +![Alt text](image-2.png) + +## 步骤三:创建集群应用 + +在这一步中,你将添加集群(cluster)模块,以创建同一程序的多个实例,以处理更多的负载并提高性能。当你使用集群模块运行进程时,你可以在你的机器上的每个 CPU 上运行多个进程: + +![Alt text](image-3.png) + +在这个图示中,请求通过主进程中的负载均衡器,然后使用循环轮询算法将请求分发到各个进程之间。 + +现在来创建`primary.js`文件 + +``` js +import cluster from "cluster"; +import os from "os"; +import { dirname } from "path"; +import { fileURLToPath } from "url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +``` + +在前两行中,导入了集群(cluster)和操作系统(os)模块。在接下来的两行中,导入了 dirname 和 fileURLToPath,它们用于将 __dirname 变量的值设置为执行 index.js 文件所在目录的绝对路径。因为当使用 ES 模块时,__dirname 并未定义,它默认只在 CommonJS 模块中定义。 + +接下来,添加以下代码来引用index.js文件: + +```js +const cpuCount = os.cpus().length; + +console.log(`The total number of CPUs is ${cpuCount}`); +console.log(`Primary pid=${process.pid}`); +cluster.setupPrimary({ + exec: __dirname + "/index.js", +}); +``` + +首先,我们将 cpuCount 变量设置为你的计算机上的 CPU 数量,应该是四个或更多。接下来,在控制台中打印了 CPU 数量和主进程的进程 ID,这个主进程将接收所有的请求,并使用负载均衡器将它们分发给工作进程。 + +随后,你使用集群(cluster)模块的 setupPrimary() 方法引用了 index.js 文件,以便在每个工作进程中执行。 + +然后,添加以下代码来创建进程: + +```js +... +for (let i = 0; i < cpuCount; i++) { + cluster.fork(); +} +cluster.on("exit", (worker, code, signal) => { + console.log(`worker ${worker.process.pid} has been killed`); + console.log("Starting another worker"); + cluster.fork(); +}); +``` + +以上代码会循环迭代cpu的数量,并在每次迭代中调用集群(cluster)模块的 fork() 方法。同时使用集群模块的 on() 方法附加了 exit 事件,以便监听进程何时发出 exit 事件,通常是当进程终止时。当触发 exit 事件时,你会记录已终止的工作进程的进程 ID,然后调用 fork() 方法创建一个新的工作进程,以替换已终止的进程。 + +完整代码如下: + +```js +import cluster from "cluster"; +import os from "os"; +import { dirname } from "path"; +import { fileURLToPath } from "url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +const cpuCount = os.cpus().length; + +console.log(`The total number of CPUs is ${cpuCount}`); +console.log(`Primary pid=${process.pid}`); +cluster.setupPrimary({ + exec: __dirname + "/index.js", +}); + +for (let i = 0; i < cpuCount; i++) { + cluster.fork(); +} +cluster.on("exit", (worker, code, signal) => { + console.log(`worker ${worker.process.pid} has been killed`); + console.log("Starting another worker"); + cluster.fork(); +}); +``` + +现在可以`node primary.js`启动服务,观察输出.可以看到服务以集群的方式启动了。 +![Alt text](image-4.png) + +## 步骤四:使用测试工具对比性能 + +在这一步中,你将使用 loadtest 软件包对你构建的两个程序生成流量。你将比较使用集群(cluster)模块的 primary.js 程序与不使用集群的 index.js 程序的性能。你会注意到使用集群模块的程序在特定时间内执行得更快,可以处理更多的请求,而不使用集群的程序则不如此。 + +### 对单核的测试 + +首先,先启动单核服务`node index.js` + +接下来,在终端中运行测试命令(在步骤一中已经下载了全局loadtest) + +```bash +loadtest -n 1200 -c 200 -k http://localhost:3000/heavy + +# -n 1200:这个参数指定了要发送的请求数量,即压测将模拟发送 1200 个请求到指定的 URL。 + +#-c 200:这个参数指定了并发连接数,即同时发送的请求数量。在这个命令中,将同时发送 200 个请求。 + +#-k:这是一个选项,表示使用 HTTP Keep-Alive 连接。Keep-Alive 允许单个连接复用,而不必为每个请求建立新的连接。 +``` + +可以看到测试报告: +![Alt text](image-5.png) + +> Max requests: 1200:设置的最大请求数。 +> Concurrency level: 200:并发连接数,即同时发送的请求数量。 +> Running on cores: 4:表示测试是在一个拥有 4 个 CPU 核心的机器上运行的。 +> Agent: keepalive:测试时使用的代理(Agent),这里是 HTTP Keep-Alive 连接。 +> Completed requests: 1200:完成的请求数量,与设置的最大请求数一致。 +> Total errors: 0:总共的错误请求数量,这里是 0,表示没有错误的请求。 +> Total time: 2.486 s:总共花费的时间,单位是秒。 +> Mean latency: 1100.3 ms:平均延迟,即请求从发送到接收的平均时间,单位是毫秒。 +> Effective rps: 483:每秒的有效请求数,即成功完成的请求数。 +> Percentage of the requests served within a certain time:显示在特定时间内完成的请求的百分比和相应的时间。 +> 50% 1240 ms:50% 的请求在 1240 毫秒内完成。 +> 90% 1596 ms:90% 的请求在 1596 毫秒内完成。 +> 95% 1607 ms:95% 的请求在 1607 毫秒内完成。 +> 99% 1680 ms:99% 的请求在 1680 毫秒内完成。 +> 100% 1723 ms (longest request):所有请求中,最长的请求花费了 1723 毫秒。 + +### 对多核的测试 + +先启动单核服务`node index.js`,同样执行 + +```bash +loadtest -n 1200 -c 200 -k http://localhost:3000/heavy +``` + +可以将测试报告的结果数据和单核模式下对比。 + +这个响应证实了扩展已经生效,你的应用程序可以在短时间内处理更多的请求而无需延迟。如果你将计算机升级为更多的 CPU,该应用程序将自动扩展到相应数量的 CPU,并进一步提高性能。 + +需要提醒的是,由于你的网络和处理器速度的不同,终端输出中的指标会有所不同。总时间和平均延迟会显著下降,总时间会迅速增加。 + +在下一步中,我们将使用 pm2 代替集群模块。 + +## 步骤五:使用 pm2 + +### 使用pm2启动 + +到目前为止,你已经使用集群(cluster)模块根据你计算机上的 CPU 数量创建了工作进程。你还添加了在工作进程终止时重新启动它的能力。在这一步中,你将设置一个替代方案,通过使用建立在集群模块之上的 pm2 进程管理器来自动扩展你的应用程序。这个进程管理器包含一个负载均衡器,并可以自动创建与你计算机上的 CPU 数量相同的工作进程。它还允许你监控这些进程,并且如果有一个进程终止,它可以自动产生一个新的工作进程。 + +在终端中,使用以下命令启动 pm2 集群: + +```bash +pm2 start index.js -i 0 +``` + +-i 选项接受你想要 pm2 创建的工作进程数量。如果你传递参数 0,pm2 将会自动创建与你计算机上的 CPU 数量相同的工作进程。 + +启动后可以看到类似表格 +![Alt text](image-6.png) + +表格包含了每个工作进程的进程 ID、状态、CPU 利用率和内存消耗,你可以用它来了解进程的行为。 + +当使用 pm2 启动集群时,该软件包会在后台运行,并且甚至在重新启动系统后会自动重新启动。 + +如果你想要从工作进程中读取日志,你可以使用以下命令: + +`pm2 logs` + +如果你想要检查进程的状态,你可以使用以下命令: +`pm2 ls` + +使用pm2启动服务之后,你可以再尝试下运行测试命令,去查看服务的性能。 + +### 使用配置文件 + +为了改进你使用 pm2 的工作流程,你可以生成一个配置文件,以传递应用程序的配置设置。这种方法将允许你在启动或重新启动集群时无需传递选项。 + +为了使用配置文件,删除当前的集群: + +```bash +pm2 delete index.js +``` + +接下来,生成配置文件: + +```bash +pm2 ecosystem +``` + +可以看到`ecosystem.config.js`文件在当前目录被生成。 +需要注意的是,需要修改这个文件的后缀名来启动对ES模块的支持。 + +```cjs +// ecosystem.config.cjs +module.exports = { + apps : [{ + script: 'index.js', + watch: '.', + name: "cluster_app", + instances: 0, + exec_mode: "cluster", + }], + + deploy : { + production : { + user : 'SSH_USERNAME', + host : 'SSH_HOSTMACHINE', + ref : 'origin/master', + repo : 'GIT_REPOSITORY', + path : 'DESTINATION_PATH', + 'pre-deploy-local': '', + 'post-deploy' : 'npm install && pm2 reload ecosystem.config.cjs --env production', + 'pre-setup': '' + } + } +}; +``` + +启动命令:`pm2 start ecosystem.config.cjs` + +其他指令: + +| Command | Description | +| ---- | ---- | +| pm2 start app_name | 启动 | +| pm2 restart app_name | 先删除再启动 | +| pm2 reload app_name | 重启集群 | +| pm2 stop app_name | 停止集群 | +| pm2 delete app_name | 删除集群 | + +ok,您现在可以使用 pm2 模块和 cluster 模块扩展您的应用程序了。 + +## 总结 + +在本教程中,你使用了集群(cluster)模块来扩展你的应用程序。首先,你创建了一个不使用集群模块的程序。然后,你创建了一个使用集群模块的程序,将应用程序扩展到你机器上的多个 CPU。随后,你对比了使用集群模块和不使用集群模块的应用程序的性能。最后,你使用了 pm2 软件包作为集群模块的替代方案,将应用程序扩展到多个 CPU 上。 + +要进一步学习,你可以访问集群模块的文档页面,了解更多关于该模块的信息。 + +Node.js 还附带了 worker_threads 模块,允许你将 CPU 密集型任务分配给工作线程,以便它们可以更快地完成。你可以尝试我们关于如何在 Node.js 中使用多线程的教程。你还可以通过专门的 Web Workers 在前端优化 CPU 绑定的任务,你可以通过遵循如何使用 Web Workers 处理 CPU 绑定任务的教程来实现。如果你想学习如何避免 CPU 绑定任务影响应用程序的请求/响应循环,请查阅如何使用 Node.js 和 BullMQ 处理异步任务的教程。 diff --git "a/src/Node/\346\267\261\345\205\245\346\265\205\345\207\272.md" "b/src/Node/\346\267\261\345\205\245\346\265\205\345\207\272.md" new file mode 100644 index 0000000..7f3a829 --- /dev/null +++ "b/src/Node/\346\267\261\345\205\245\346\265\205\345\207\272.md" @@ -0,0 +1,50 @@ +--- + title: '深入浅出' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2023-02-08 + category: '' + tag: 'node' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ## 文件模块 +node引入模块三个步骤: + +1. 路径解析 +2. 文件定位 +3. 编译执行 + +--- + +例如require('element') 时,在node_modules中找到的是个文件夹,node会将目录当一个包来处理。 +(查找的兜底是index文件名) + +1. 在当前目录下找到package.json +2. 通过JSON.parse()解析出包描述对象,从中取出main属性制定的文件名 + +--- + +## 异步I/O +### 系统的异步io +非阻塞i/o会让CPU处理状态判断。通过轮询处理 +轮询的演进: + +1. read +2. select +3. poll +4. epoll + +通过线程池来完成异步I/O +### node的异步io +在进程启动的时候,node便会创建一个类似while(true)的循环,每一次循环为一次tick + diff --git "a/src/Other/GSAP\344\275\277\347\224\250\346\214\207\345\215\227.md" "b/src/Other/GSAP\344\275\277\347\224\250\346\214\207\345\215\227.md" new file mode 100644 index 0000000..7422d94 --- /dev/null +++ "b/src/Other/GSAP\344\275\277\347\224\250\346\214\207\345\215\227.md" @@ -0,0 +1,223 @@ +--- + title: '5分钟上手GSAP' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2023-11-27 + category: '' + tag: 'js annimation' + star: false + article: true + timeline: true + image: '' + editLink: false +--- + +## 什么是GSAP + +动效库 + +> GSAP 允许您轻松地为 JS 可以触摸的任何东西设置动画。提供丝般流畅的性能和无与伦比的支持,让您可以专注于有趣的事情。 + +- [速查表](https://gsap.com/community/cheatsheet/) + +### Core + +基础的动效控制,能满足大部分动效场景。例如旋转,缩放,抛物线运动 + +### Plugins + +能力拓展,能满足特殊动效场景。例如跟随鼠标滚轮的滚动动效 + +## 动起来 + +在 GSAP 中,Tweens 是指在动画中改变一个或多个属性的过程。以下是一个简单的 Tween 示例: + +```js +gsap.to('.box', { + x: 200, // 将元素的 x 坐标移动到 200 + opacity: 0.5, // 改变元素的不透明度 + duration: 1, // 动画持续时间 + ease: 'power2.inOut', // 缓动函数 +}); +``` + +to/from/fromTo这三个方法都能返回一个Tween实例 + +假设现在`.box`这个dom节点,在x轴上的偏移量为0`{x: 0}` + +### to + +偏移量从0到200 + +```js +gsap.to(".box", { x: 200 }) +``` + +### from + +偏移量从200回到0 + +```js +gsap.from(".box", { x: 200 }) +``` + +### fromTo + +```js +gsap.fromTo(".box", { x: 200 }, {x: 0}) +``` + +偏移量从200到0 + +### set + +```js +gsap.set(".box", {x: 100}); +``` + +设置偏移量为100 + +set 是一瞬间的事 + +## Easing + +控制动画运动的速率 + +[配置字典](https://gsap.com/resources/getting-started/Easing) + +## Staggers + +错落有致,控制一组元素动画的交错配置 + +```js +gsap.from(".box", { + x: -150, + duration: 1, + opacity: 0, + stagger: 0.2, +}) +``` + +## Apple Watch 实战 + +[示例网站](https://www.apple.com.cn/apple-watch-series-9/) + +1. 字往上提的渐入效果 + +## 排队动起来(timelines) + +如果我们需要控制一系列复杂的动画,涉及到多个元素, +仅仅依靠延迟和上面说到的交错配置,其实是不够的。 + +那么Timelines来了。 + +Timelines 允许你将多个 Tweens 组织在一起,以便在时间上进行更复杂的控制。以下是一个创建 Timeline 的示例: + +```js +const timeline = gsap.timeline(); +timeline.to('.box', { x: 200, duration: 1 }) + .to('.box', { rotation: 360, duration: 1, ease: 'elastic' }); +``` + +[查看官网例子](https://gsap.com/resources/getting-started/timelines) + +## 控制动画(control) + +从上面的示例可以看到,进行一个动画,我们没办法更精细的控制动画什么时候开始,什么时候结束。但我们可以拿到一个基础的Tween实例,一个timeline实例 + +有这两个实例,我们就可以控制对应的动画了。 + +可以在官网上看,这两个动画实例支持什么方法 +[实例方法文档](https://gsap.com/docs/v3/GSAP/Timeline) + +### 基础控制 + +```js +// store the tween or timeline in a variable +let tween = gsap.to("#logo", {duration: 1, x: 100}); + +//pause +tween.pause(); + +//resume (honors direction - reversed or not) +tween.resume(); + +//reverse (always goes back towards the beginning) +tween.reverse(); + +//jump to exactly 0.5 seconds into the tween +tween.seek(0.5); + +//jump to exacty 1/4th into the tween 's progress: +tween.progress(0.25); + +//make the tween go half-speed +tween.timeScale(0.5); + +//make the tween go double-speed +tween.timeScale(2); + +//immediately kill the tween and make it eligible for garbage collection +tween.kill(); + +// You can even chain control methods +// Play the timeline at double speed - in reverse. +tween.timeScale(2).reverse(); + +``` + +### 回调 + +- onComplete: 当动画完成时调用。 +- onStart: 当动画开始时调用。 +- onUpdate: 每次动画更新时调用(在动画活动期间的每一帧)。 +- onRepeat: 每次动画重复时调用。 +- onReverseComplete: 当动画在反向播放时再次达到起点时调用。 + +```js +gsap.to(".class", { + duration: 1, + x: 100, + // arrow functions are handy for concise callbacks + onComplete: () => console.log("the tween is complete") +}) + +// If your function doesn't fit neatly on one line, no worries. +// you can write a regular function and reference it +gsap.timeline({onComplete: tlComplete}); // <- no () after the reference! + +function tlComplete() { + console.log("the tl is complete"); + // more code +} +``` + +## 花式的动起来(plugins) + +插件提供了额外的能力,我们也可以按需引入 + +[插件一览](https://gsap.com/resources/Plugins/) + +需要注意的是,这里区分收费版,高级的使用需要付费。 + +### scrollTrigger + +看官网代码 + +> 辅助线 markers: true + +### Draggable + +看官网代码 + +## 总结 + +优秀的JS动画库,对现代浏览器更友好。 + +一些高级功能需要付费,但是基础的动画支持已经满足大部分场景。 + +上手简单,应用不难,值得star diff --git "a/src/Other/Hello\347\256\227\346\263\225\347\254\224\350\256\260.md" "b/src/Other/Hello\347\256\227\346\263\225\347\254\224\350\256\260.md" new file mode 100644 index 0000000..89ed4e7 --- /dev/null +++ "b/src/Other/Hello\347\256\227\346\263\225\347\254\224\350\256\260.md" @@ -0,0 +1,318 @@ +--- + title: 'Hello算法笔记' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2023-01-16 + category: '' + tag: '算法' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + +### 数据结构 + +> 可以根据逻辑结构和物理结构来区分 + +- 按照逻辑结构 + + 1. 线性数据结构: 数组、链表、栈、队列、哈希表
2.非线形数据结构:树、图、堆、哈希表 + +- 按照物理结构(在计算机内存中的存储方式) + +1. 连续空间存储 +2. 离散空间存储 + +### 数组 + +> 将相同类型元素存储在连续内存空间的数据结构,为什么数组的索引从0开始?因为索引本质上表示的是内存地址的偏移量,首个地址的偏移量是0,所以索引自然为0 + +#### 优点 + +- 快速查找,直接访问对应索引元素 + +#### 缺点 + +- 插入和删除的效率比较低(插入的时候需要后挪,删除的时候需要前移,时间复杂度O(N)) + +### 链表 + +> 一种线形数据结构,由一个个节点连接。节点中包含包含:当前节点的值value和指向下一个节点的地址next + +#### 优点 + +- 插入和删除的效率高,只需要改变原来一个节点的指针 + +#### 缺点 + +- 访问节点的效率低,不能直接访问想要的节点,只能通过前置节点才能知道下一个节点 +- 内存占用多,因为一个节点的内容除了包含值还有下个节点的饮用地址 + +尾节点的next字段值为null + +#### 链表类型 + +1. 单向链表 +2. 环形链表 +3. 双向链表 + +### 栈 + +> 先进后出,叠在一起的盘子,要拿最低下的盘子,需要把上面的盘子一个个拿走 + +`javascript`可以使用数组模拟栈操作,也可以使用链表 + +### 队列 + +> 先进先出 + +`javascript`可以使用数组模拟栈操作,也可以使用链表 + +#### 队列类型 + +- 双向队列,两端都可以添加和删除元素 + +### 哈希表(HashMap) +> +> 哈希表通过键值之前的映射,实现高效的元素查找。时间复杂度0(1),在`js`中可以使用Map类型来实现 +> 避免hash冲突可以是用Symbol来指定唯一key + +### 二叉树 + +> 二叉树是一种非线性数据结构,表示祖先与后代之间的派生关系,体现一分为二的分治逻辑。类似于链表,二叉树也是节点关联。节点包含一个值,两个指针,左指针和右指针 + +```js +/* 初始化二叉树 */ +// 初始化结点 +let n1 = new TreeNode(1), + n2 = new TreeNode(2), + n3 = new TreeNode(3), + n4 = new TreeNode(4), + n5 = new TreeNode(5); +// 构建引用指向(即指针) +n1.left = n2; +n1.right = n3; +n2.left = n4; +n2.right = n5; +``` + +#### 完美二叉树 + +所有的节点都被填满,所有节点的度为2 + +![perfect_binary_tree.png](https://cdn.nlark.com/yuque/0/2023/png/297368/1673599957981-dd5ebabd-dc47-4354-9585-9bb7a7a0987f.png#averageHue=%23fcfcfc&clientId=u0140c98f-87b6-4&crop=0&crop=0&crop=1&crop=1&from=ui&height=421&id=uc32e59ad&margin=%5Bobject%20Object%5D&name=perfect_binary_tree.png&originHeight=720&originWidth=1280&originalType=binary&ratio=1&rotation=0&showTitle=false&size=65603&status=done&style=none&taskId=ufbc03126-fd63-48cd-9467-678d1542e3b&title=&width=748) + +#### 完全二叉树 + +只有最底层的节点没有被填满,且最底层节点尽量靠左填充 + +![complete_binary_tree.png](https://cdn.nlark.com/yuque/0/2023/png/297368/1673599986964-bee60a94-8066-425b-b4a7-5b27faa77e35.png#averageHue=%23fcfcfc&clientId=u0140c98f-87b6-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=u9ff4532f&margin=%5Bobject%20Object%5D&name=complete_binary_tree.png&originHeight=720&originWidth=1280&originalType=binary&ratio=1&rotation=0&showTitle=false&size=56261&status=done&style=none&taskId=u016e14ce-9b65-4cd5-9d31-168accc43e8&title=) + +#### 完满二叉树 + +除了叶结点之外,其余所有的节点都有两个子节点 + +![full_binary_tree.png](https://cdn.nlark.com/yuque/0/2023/png/297368/1673600134537-41e2ee79-8d19-41e3-a6ec-11a7886ffd6d.png#averageHue=%23fdfdfd&clientId=u0140c98f-87b6-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=u5e1654b3&margin=%5Bobject%20Object%5D&name=full_binary_tree.png&originHeight=720&originWidth=1280&originalType=binary&ratio=1&rotation=0&showTitle=false&size=50414&status=done&style=none&taskId=u72b2150c-d4e4-41f8-9858-5f5444558c4&title=) + +#### 平衡二叉树 + +任意节点的左子树和右子树的高度之差绝对值 ![](https://cdn.nlark.com/yuque/__latex/0fc7b821521f1860f7e5cd7b8d1de90a.svg#card=math&code=%E2%89%A4%201&id=UpfLg) + + +![balanced_binary_tree.png](https://cdn.nlark.com/yuque/0/2023/png/297368/1673600405243-c786c52d-c6ee-4f45-bb70-76bbc42ff9ae.png#averageHue=%23fbfbfb&clientId=u0140c98f-87b6-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=ue20bb64d&margin=%5Bobject%20Object%5D&name=balanced_binary_tree.png&originHeight=720&originWidth=1280&originalType=binary&ratio=1&rotation=0&showTitle=false&size=63985&status=done&style=none&taskId=u2baf4d36-2183-4326-9791-503870f2f83&title=) + + +#### 层序遍历(广度优先) + +从上到下,从左到右 + + + +#### 前序/中序/后序(深度优先) + +其体现着一种:先走到头,再回头继续的遍历方式 + +- 前序访问: // 访问优先级:根结点 -> 左子树 -> 右子树 +- 中序访问: // 访问优先级:左子树 -> 根结点 -> 右子树 +- 后序访问: // 访问优先级:左子树 -> 右子树 -> 根结点 + +![binary_tree_dfs.png](https://cdn.nlark.com/yuque/0/2023/png/297368/1673600801905-58a2a830-92bc-4a60-ab44-d070cd98e9bd.png#averageHue=%23fcfbfa&clientId=u0140c98f-87b6-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=u36dffa4a&margin=%5Bobject%20Object%5D&name=binary_tree_dfs.png&originHeight=720&originWidth=1280&originalType=binary&ratio=1&rotation=0&showTitle=false&size=174973&status=done&style=none&taskId=ub495513e-404d-4aae-afc6-d7f0f187cfc&title=) + + + +#### 二叉搜索树 + +二叉搜索树需要满足: + +1. 对于根节点,左子树中所有的节点的值 < 根节点的值 < 右子树所有的节点值 +2. 任意节点的左子树和右子树也是满足条件1,即也是二叉搜索树 + +对于以上,可以想到先排序,然后二分 + + + +#### AVL树 +> +> AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影响二叉树中序遍历序列的前提下,使失衡结点重新恢复平衡**。换言之,旋转操作既可以使树保持为「二叉搜索树」,也可以使树重新恢复为「平衡二叉树」。 + + + +#### 堆 + +一颗限定条件下的「完全二叉树」。
根据成立条件: + +- Max Heap 大顶堆: 任意节点的值 ≥ 其子节点的值 +- Min Heap 小顶堆: 任意节点的值 ≤ 其子节点的值 + + + +### 查找算法 + + + +#### 线形查找 + +遍历数据结构+判断是否命中
时间复杂度: O(n)
空间复杂度: O(1) + + +##### 优点 + +- 线形查找通用性极佳:没有跳跃访问的元素,数组和链表都可以 + + +##### 缺点 + +- 线形查找的时间复杂度太高,n足够大时,查找销量低 + + +#### 二分查找 + +利用数据的有序性,通过每轮缩小一半的搜索区间来查找
使用二分法的两个前置条件: + +- 数据必须有序性 +- 仅适用于数组,因为在循环的过程中需要跳跃 + +> 如果为了满足数据有序性,去做数据的排序,是得不偿失。排序算法的时间复杂度一般为O(nlogn) + + + +##### 优点 + +- 时间复杂度低 +- 不需要额外的空间 + + +##### 缺点 + +- 仅适用于有序数据 +- 仅适用于数组 +- 在小数据量情况下,线形查找的性能更好 + + +#### hash查找 + +借助一个哈希数据结构,根据键值对的映射,实现O(1)的查找,以空间换时间
在`js`中使用Map类型 + + +##### 优点 + +- 查找、插入、删除操作的平均时间复杂度都为 O(1) + + +##### 缺点 + +- 需要使用额外空间 +- 建立hash需要时间,不适合高频增删改,低频查找 +- 数据量小,线形查找更快 + + +#### + + + +### 排序算法 + + + +#### 冒泡排序 + +```javascript +function bubbleSort(arrs) { + for (let i = nums.length; i > 0; i--) { + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j+1]) { + const tmp = nums[j]; + nums[j] = nums[j+1] + nums[j+1] = tmp + } + } + } +} +``` + + + +#### 插入排序 + +```javascript +function insertSort(nums) { + for (let i = 1; i < nums.length; i++) { + let tmp = nums[i] + let j = i - 1 + while(j >= 0 && nums[i] < nums[j]) { + nums[i] = nums[j] + j-- + } + nums[j+1] = tmp + } +} +``` + + + +#### 快速排序 + +```javascript +// 先计算一次分割点,然后递归 +function partition(nums, left, right) { + // 取第一个元素为基准值 + // i 从左往右找到一个 大于基准值的 + // j 从右往左找到一个 小于基准值的 + let i = left + let j = right + const base = nums[i] + while(i < j) { + while(i < j && nums[j] > nums[left]) { + j-- + } + while(i < j && nums[i] <= nums[left]) { + i++ + } + swap(nums, i, j) + } + // i,j重叠 + swap(nums, i, left) + return i +} +function swap(nums, i, j) { + const tmp = nums[i] + nums[i] = nums[j] + nums[j] = tmp +} +// +function quickSort(nums, left, right) { + if (left >= right) { + return + } + const pivot = partition(nums, left, right) + quickSort(nums, left, pivot - 1) + quickSort(nums, pivot + 1, right) +} +``` diff --git "a/src/Other/JS\345\276\252\347\216\257\345\260\217\350\256\260.md" "b/src/Other/JS\345\276\252\347\216\257\345\260\217\350\256\260.md" new file mode 100644 index 0000000..f613a08 --- /dev/null +++ "b/src/Other/JS\345\276\252\347\216\257\345\260\217\350\256\260.md" @@ -0,0 +1,70 @@ +--- + title: 'JS循环小记' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2019-08-01 + category: '' + tag: 'javascript' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ### 循环类型 +在ECMA262标准第三版中定义了四种类型的循环 +#### for +#### while +#### do...while... +#### for...in... + +for in 循环比其他几种要慢,这是因为每次迭代时都会搜索实例和原型对象 + +```javascript +for (const key in object) { + if (object.hasOwnProperty(key)) { + const element = object[key]; + + } +} +``` +上面代码是vscode快速补全插件带出来的forin循环,加了hasOwnProperty判断 + +```javascript +Object.prototype.objCustom = function() {}; +Array.prototype.arrCustom = function() {}; + +let iterable = [3, 5, 7]; +iterable.foo = 'hello'; + +for (let i in iterable) { + console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom" +} + +for (let i in iterable) { + if (iterable.hasOwnProperty(i)) { + console.log(i); // logs 0, 1, 2, "foo" + } +} +``` +上面代码就直观了。 +所以除非明确需要迭代一个属性数量未知的对象,否则避免使用forin,用它来遍历数组也是得不偿失 + +至于 `for...of`  +> [`for...in`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/for...in) 语句以原始插入顺序迭代对象的[可枚举属性](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Enumerability_and_ownership_of_properties)。 +> `for...of` 语句遍历[可迭代对象](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Iterators_and_Generators#Iterables)定义要迭代的数据。 +> + +```javascript +for (let i of iterable) { + console.log(i); // logs 3, 5, 7 +} +``` + +该循环迭代并记录`iterable`作为[可迭代对象](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Iterators_and_Generators#Iterables)定义的迭代值,这些是数组元素 `3`, `5`, `7`,而不是任何对象的**属性**。 diff --git a/src/Other/Promise.md b/src/Other/Promise.md new file mode 100644 index 0000000..5334fc4 --- /dev/null +++ b/src/Other/Promise.md @@ -0,0 +1,70 @@ +--- + title: 'Promise' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2019-09-22 + category: '' + tag: 'javascript' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ## 新建一个Promise实例的时候,发生了什么 + +```javascript +const chenjian = new Promise(function(resolved, rejected) { + setTimeout( () => { + resolved() + },1000) + }) +chenjian.then(function(){}) +``` + +new一个实例的时候,Promise构造函数使用内部的doResolve方法运行了它的入参,所以为啥里面的代码会同步执行。代码如下 + +```javascript +function Promise(fn) { + if (typeof this !== 'object') { + throw new TypeError('Promises must be constructed via new'); + } + if (typeof fn !== 'function') { + throw new TypeError('Promise constructor\'s argument is not a function'); + } + this._deferredState = 0; + this._state = 0; + this._value = null; + this._deferreds = null; + if (fn === noop) return; + doResolve(fn, this); +} +function doResolve(fn, promise) { + var done = false; + // 这就是为啥生成实例时会同步运行,因为这里运行了fn + var res = tryCallTwo(fn, function (value) { + if (done) return; + done = true; + resolve(promise, value); + }, function (reason) { + if (done) return; + done = true; + reject(promise, reason); + }); + if (!done && res === IS_ERROR) { + done = true; + reject(promise, LAST_ERROR); + } +} +``` + +这里我们使用的是setTimeout来模拟一个异步,过1秒之后会运行resolved方法,resolved和rejected方法,在Promise函数内部都有定义,他们的作用是改变函数的状态。因为是异步,所以并不会立刻改变状态。 +根据事件循环机制,计时器的回调会在计时结束之后被推入消息队列。 +在等待1s的时候,程序还是会继续运行。运行了then方法,那么来看看运行then方法时发生了什么: +then方法会新建一个Promise实例,然后判断状态变成了成功还是失败,成功就执行then里面的成功回调,并且判断成功回调是否有返回值,没有返回值执行结束,有返回值那么就去重新从resolve方法来一遍,但是注意的是promise是新的实例。 diff --git "a/src/Other/Set\346\225\260\346\215\256\347\261\273\345\236\213\347\233\270\345\205\263.md" "b/src/Other/Set\346\225\260\346\215\256\347\261\273\345\236\213\347\233\270\345\205\263.md" new file mode 100644 index 0000000..2a38b37 --- /dev/null +++ "b/src/Other/Set\346\225\260\346\215\256\347\261\273\345\236\213\347\233\270\345\205\263.md" @@ -0,0 +1,54 @@ +--- + title: 'Set数据类型相关' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2019-06-14 + category: '' + tag: 'javascript' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + 类似于数组,但是值都是唯一的。 +`const a = new Set([1,2,3,3,4])` +##### Set 内部判断两个值是否不同,使用的算法叫做“Same-value-zero equality”  +了解下各比较算法 + +1.比较熟悉的 == 和 ===,前者会在类型不相等时,依据规则进行类型转换。而后者则是不允许类型转化来比较。 +2. `Object.is()` 方法也是比较两者是否相等,它使用的是**SameValue算法。**与 == 和 ===的区别如下 + +```javascript +Object.is(NaN, NaN) // true +Object.is(0,-0) // false +Object.is(null, null) // true +NaN === NaN // false +null === null // true +0 === -0 // true +``` +3.我们Set内部使用的是**SameValueZero算法,**而它的不同之处 + +```javascript +const a = new Set([0, -0, NaN, null]) +a.has(NaN) +// true +a.has(NaN) +// true +a.add(NaN) +Set(3) {0, NaN, null} +a.has(NaN) +// true +// 可以看到NaN在使用SameValueZero算法的时候NaN是相等的,这和SameValue表现一样, +// 0和-0也认为他们是相等,这就和Object.is()的结论不一致,也就是和SameValue表现不一致 +``` + +总结:Set数据类型保证其值唯一不重复是依据SameValueZero算法实现 + + diff --git "a/src/Other/node\345\255\246\344\271\240\347\254\224\350\256\260.md" "b/src/Other/node\345\255\246\344\271\240\347\254\224\350\256\260.md" new file mode 100644 index 0000000..c3609d2 --- /dev/null +++ "b/src/Other/node\345\255\246\344\271\240\347\254\224\350\256\260.md" @@ -0,0 +1,69 @@ +--- + title: 'node学习笔记' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2019-05-21 + category: '' + tag: 'node' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ### commonJS规范 +#### 模块 +一个文件为一个模块,对模块的定义分为: 模块引用,模块定义,模块标识三个部分 +##### 模块引用 +`const chenjian = require(./chenjian.js)`  +##### 模块定义 +上下文提供了export对象来导出模块,在模块中还存在一个module对象,它代表模块自身,所以export是 +module的属性。 +` export.say = function (){alert('hellow, i am good man')}` +##### 模块标识 +模块标识其实就是传递给require的参数 + +![image.png](https://cdn.nlark.com/yuque/0/2019/png/297368/1558345821818-aa74a9b4-861b-4087-a477-f4571de85cbd.png#align=left&display=inline&height=242&name=image.png&originHeight=242&originWidth=556&size=24200&status=done&width=556) + + +### node 对模块的实现 + node中模块的实现还是和commonsJS有些不同的 +引入一个文件,node会进行 +1.文件路径分析 +2.文件定位 +3.文件编译 +node中的那些内置模块在node进程运行的时候就已经编译到内存中了,所以内置模块的获取和访问是最快的 + +node对模块的访问会和浏览器一样对对象进行缓存,但是浏览器缓存的对象是文件,而node则是编译和执行之后的 +对象 + + +1.**node对CommonJs的实现**:事实上,在编译的过程中,Node对获取的JavaScript文件内容进行了头尾包装。在头部添加了(function (exports, require, module, __filename, __dirname) {\n,在尾部添加了\n});。 +一个正常的JavaScript文件会被包装成如下的样子 + +``` +(function (exports, require, module, __filename, __dirname) { + var math = require('math'); + exports.area = function (radius) { + return Math.PI * radius * radius; + }; +}); +``` + +这就是这些变量并没有定义在每个模块文件中却存在的原因。在执行之后,模块的exports属性被返回给了调用方。 + +2.Node调用process.dlopen()方法进行加载和执行。在Node的架构下,dlopen()方法在Windows和*nix平台下分别有不同的实现,通过libuv兼容层进行了封装。 + +3.**模块类型的层级关系:** + +内建模块(纯c/c++编写的模块) ===> 核心模块(部分c/c++编写,由javascript封装的模块,Node的buffer、crypto、evals、fs、os等模块都是部分通过C/C++编写的。) ===> 文件模块(指开发者自己创建的文件) + +### node + + diff --git "a/src/Other/\344\272\214\345\217\211\346\240\221\344\270\216\345\233\276.md" "b/src/Other/\344\272\214\345\217\211\346\240\221\344\270\216\345\233\276.md" new file mode 100644 index 0000000..5850c0d --- /dev/null +++ "b/src/Other/\344\272\214\345\217\211\346\240\221\344\270\216\345\233\276.md" @@ -0,0 +1,35 @@ +--- + title: '二叉树与图' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2020-06-21 + category: '' + tag: 'javascript' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ### 深度遍历 +#### 前序遍历 +根结点-->左子树-->右子树 +#### 中序遍历 +左子树-->根结点-->右子树 +#### 后续遍历 +左子树-->右子树-->根结点 +### 二叉查找树 + +- 若任意节点的左子树不为空,则左子树上所有节点的值均小于它的根节点的值 +- 若任意节点的右子树不为空,则右子树上所有节点的值均大于它的根节点的值 +- 任意节点的左、右子树也分别为二叉查找树 +- 没有键值相等的节点 + +### 图 +![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1592559974987-faca2775-3a91-4424-a4e8-f54a4875e57e.png#align=left&display=inline&height=159&margin=%5Bobject%20Object%5D&name=image.png&originHeight=318&originWidth=680&size=62602&status=done&style=none&width=340) diff --git "a/src/Other/\345\206\205\345\255\230\357\274\214\344\272\213\344\273\266\345\276\252\345\214\226\346\234\272\345\210\266.md" "b/src/Other/\345\206\205\345\255\230\357\274\214\344\272\213\344\273\266\345\276\252\345\214\226\346\234\272\345\210\266.md" new file mode 100644 index 0000000..68aec3c --- /dev/null +++ "b/src/Other/\345\206\205\345\255\230\357\274\214\344\272\213\344\273\266\345\276\252\345\214\226\346\234\272\345\210\266.md" @@ -0,0 +1,117 @@ +--- + title: '内存,事件循化机制' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2019-05-29 + category: '' + tag: 'javascript' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ## 垃圾回收 +### 引用计数 +这个算法主要是定义"对象有没有被其他对象引用"为依据,来决定是否回收。 +但是如果两个对象之前互相引用,那么就会回收不了 + +### 标记清除 +这个算法是定义"对象能否被获得"为依据,能否被获得指的是从根元素,也可以理解为全局,去获得被引用的元素,垃圾回收器将定期从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和收集所有不能获得的对象。 + +现在所有浏览器的垃圾回收机制都是基于标记清除的算法来实现优化。 + +### Event Loop +JavaScript 的并发模型基于“事件循环” + +首先要有队列queue,栈stack,堆heap的概念 + +![](https://cdn.nlark.com/yuque/0/2019/svg/297368/1558924186766-88436107-7963-4a90-8ea8-a01f3cf988b1.svg#align=left&display=inline&height=270&originHeight=270&originWidth=294&status=done&width=294) + +> 一个 web worker 或者一个跨域的 `iframe` 都有自己的栈,堆和消息队列。两个不同的运行时只能通过 [`postMessage`](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage) 方法进行通信。如果另一运行时侦听 `message` 事件,则此方法会向其添加消息。  + + +事件循环就是会不断判断队列中是否有可执行的事件,来运行。 +有了这个概念之后,开始思考,运行一段js代码的时候,事件循环机制是如何工作的。 +众所周知,js是单线程作业,这里在浏览器环境中要明白浏览器是多进程的,js脚本执行是浏览器渲染进程中的一个线程。和它同为线程的还有GUI渲染线程,定时器触发线程等 + +![image.png](https://cdn.nlark.com/yuque/0/2019/png/297368/1558946584182-57e7ce52-1c84-4d42-b722-42063d59dc8a.png#align=left&display=inline&height=125&name=image.png&originHeight=125&originWidth=644&size=14418&status=done&width=644) + +![image.png](https://cdn.nlark.com/yuque/0/2019/png/297368/1558947011843-960e8536-6e22-4300-88fc-9c6218d24033.png#align=left&display=inline&height=447&name=image.png&originHeight=447&originWidth=221&size=43107&status=done&width=221) + +分析下面这段代码 + +```javascript +function foo(b) { + var a = 10; + return a + b + 11; +} +function bar(x) { + var y = 3; + return foo(x * y); +} +console.log(bar(7)); // 返回 42 +``` +  +函数声明两个函数,函数声明提升。bar函数返回了foo函数 +最后一行执行bar方法 + +函数调用形成一个栈帧 + +此时的消息队列中,是没有任何事件的,因为js引擎并没有异步操作或者注册了什么事件(即没有向消息队列中添加消息)。只有上面的同步任务 +所以此时的执行栈里面是bar函数,bar函数上面又压了个foo函数,当foo函数运行结束之后,这个函数(参数和局部变量:指向堆的一系列指针)就会被抛出栈,然后等bar运行完毕,同样也会被抛出栈,当栈为空时,意味着同步任务的一个函数执行完了,事件循环机制就去拉取消息队列中的第一个任务执行。 + +现在有下面这段代码 + +```javascript +console.log('script start'); + +setTimeout(function() { + console.log('setTimeout'); +}, 0); + +Promise.resolve().then(function() { + console.log('promise1'); +}).then(function() { + console.log('promise2'); +}); + +console.log('script end'); +``` +   +先说执行结果 + +![image.png](https://cdn.nlark.com/yuque/0/2019/png/297368/1558949568434-7c66239b-a30b-482a-8eb1-3f3f14ef2d7d.png#align=left&display=inline&height=124&name=image.png&originHeight=124&originWidth=157&size=3526&status=done&width=157) + +这涉及到宏任务(macrotask)和微任务(microtask) + +setTimeout 众所周知是定时器,上面代码的意思是立即插入一个匿名回调函数, +promise 众所周知是异步处理函数,后面接了两个异步回调 + +回到我们的队列,栈,堆中来 + +js执行,此时我们第一个遇到的是同步代码( script start),然后遇到setTimeout, +什么时候将setTimeout的回调推入消息队列呢?计时器线程会计时,等倒计时结束,**事件触发线程**就会负责推入,这里为0,表示以最快的速度推入,就是代码运行到这里就推入了。但是推入之后不一定立马执行,因为此时我们的执行栈不为空, +然后继续执行同步任务。遇到promise,也会向消息队列中添加任务。但是和setTimeout不同的是,promise添加的是微任务,所以会有两个队列来进行分工,macrotask在一个队列中,microtask在另一个队列中。 + +所有会有下面的 +![image.png](https://cdn.nlark.com/yuque/0/2019/png/297368/1558950918438-3cfbb34f-5b61-4f8f-8672-1d2f9e9187ec.png#align=left&display=inline&height=515&name=image.png&originHeight=515&originWidth=728&size=20185&status=done&width=728) + +那么这两个队列的执行顺序是怎样的? +当执行栈(starck)为空时就会将microtask里面的任务都执行,那么相信结果就很明显了 +执行完 start 和end ,这时strack里面是空的,那么就执行第一个promise回调,执行完第一个promise回调,遇到了第二个promise回调,那就会将第二个执行回调放到microtask里面,执行完之后,栈为空,那就再去运行微任务队列,输出promise2,这时microtask里面是没有任务了,那将由macrotask接手接下来的操作,执行setTimeout +的回调,输出setTimeout + +那么哪些是macrotask,哪些是microtask呢 + +#### macrotask +![image.png](https://cdn.nlark.com/yuque/0/2019/png/297368/1559006520108-f7d173c5-0684-4689-bfe5-f214fdd7f97e.png#align=left&display=inline&height=179&name=image.png&originHeight=179&originWidth=384&size=7532&status=done&width=384) +#### microtask + +![image.png](https://cdn.nlark.com/yuque/0/2019/png/297368/1559006540001-43a2cf9a-3a9a-42be-8c1c-06dca67e1286.png#align=left&display=inline&height=194&name=image.png&originHeight=194&originWidth=779&size=21387&status=done&width=779) diff --git "a/src/Other/\345\206\222\346\263\241\357\274\214\346\217\222\345\205\245\357\274\214\345\277\253\351\200\237\346\216\222\345\272\217.md" "b/src/Other/\345\206\222\346\263\241\357\274\214\346\217\222\345\205\245\357\274\214\345\277\253\351\200\237\346\216\222\345\272\217.md" new file mode 100644 index 0000000..8e851b5 --- /dev/null +++ "b/src/Other/\345\206\222\346\263\241\357\274\214\346\217\222\345\205\245\357\274\214\345\277\253\351\200\237\346\216\222\345\272\217.md" @@ -0,0 +1,137 @@ +--- + title: '选择,冒泡,插入,快速排序' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2019-07-26 + category: '' + tag: 'javascript' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + +```javascript +/** + * 选择排序 + * [3, 1, 4, 1, 5, 9] + * [1, 3, 4, 1, 5, 9] + * 原理:一个min指针,一次循环找到最小值,和min互换位置,保证最左边是最小值 + */ +function selectSort(nums) { + for (let i = 0; i < nums.length - 1; i++) { + let min = i + for (let j = i + 1; j< nums.length; j++) { + if (nums[j] < nums[min]) { + min = j 1 + } + } + [nums[i], nums[min]] = [nums[min], nums[i]] + } + return nums +} +``` + +```javascript +/** + * 冒泡算法 + * 比较两两值,一次冒泡之后最右边的值肯定是为最大的 + * 第二次冒泡,就只要针对除去最后一个值的数值进行冒泡 + * [22, 34, 3, 32, 82, 55, 89, 50, 37, 5, 64, 35, 9, 70] + */ +function bubble_one_time(arr, j) { + for (let i = 0; i < arr.length-j; i++) { + const element = arr[i]; + if (arr[i] > arr[i+1]) { + let temp + temp = arr[i+1] + arr[i+1] = arr[i] + arr[i] = temp + } + } + return arr +} +function bubble_sort (arr) { + for (let i = 0; i < arr.length; i++) { + arr = bubble_one_time(arr, i) + } + return arr +} +``` + +```javascript +/** + * 插入排序 + * 从原数组一个一个遍历,将其放到新数组,但要 + * 维持新数组的序列 + */ +function insertSort (arr, l, r) { + for (let i = l + 1; i <= r; i++) { + if (arr[i] < arr[i-1]) { + let temp = arr[i] + let j = i + while (j > l && arr[j-1] > temp ) { + arr[j] = arr[j-1] + j-- + } + arr[j] = temp + } + } +} +``` + +```javascript +/** + * 快速排序 + * 是对冒泡排序的一种改进 + * 通过一趟排序将数据分为两部分,再对这两部分 + * 分别递归排序,以此类推 + * 双向的扫描比单项扫描快 + */ +function quick_sort(arr, l, r) { + if (l < r) { + let e = quick_one_time(arr, l, r) // e 是一次快排的分界线,以此对左右两边再进行快排 + quick_sort(arr, l, e-1) + quick_sort(arr, e+1, r) + } + return arr +} +function quick_one_time (arr,i,j) { + // i 和 j是两个指针,初始的时候分别指向首尾 + // 设定一个基准值 初始为 arr[0] + let pivot = arr[i] + // 现在指针移动, + while (i < j) { + while(arr[j] > pivot && i < j){ + j-- + } + arr[i] = arr[j] + while (arr[i]< pivot && i < j) { + i++ + } + arr[j] = arr[i] + } + // 指针重叠,说明遍历完一次了 + arr[i] = pivot + return i +} +``` + +```javascript +/** + * BFPRT算法,它和快速排序的区别仅仅是改变了pivot的取值 + * 快排中我们默认取首个为基准值 + * 在BFPRT算法中我们取五分中位数的中位数 + * 1. 将 n个元素划为 n/5 组,每组5个,至多只有一组由 n mod 5个元素组成。 + * 2. 寻找这 n/5 个组中每一个组的中位数,这个过程可以用插入排序。 + * 3. 对步骤2中的 n/5 个中位数,重复步骤1和步骤2,递归下去,直到剩下一个数字。 + * 4. 最终剩下的数字即为pivot,把大于它的数全放左边,小于等于它的数全放右边。 + * 5. 判断pivot的位置与k的大小,有选择的对左边或右边递归。 + */ +``` diff --git "a/src/Other/\345\207\275\346\225\260\351\207\215\350\275\275.md" "b/src/Other/\345\207\275\346\225\260\351\207\215\350\275\275.md" new file mode 100644 index 0000000..53c62c7 --- /dev/null +++ "b/src/Other/\345\207\275\346\225\260\351\207\215\350\275\275.md" @@ -0,0 +1,78 @@ +--- + title: '函数重载' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2019-05-09 + category: '' + tag: 'javascript js' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + javaScript并没有函数重载的概念,所谓重载就是一些函数具有相同的函数名,但是参数不同,也能进行正确的运算。 + +```javascript +function addNum(num1) { + return num1 + 100 +} +function addNum(num1,num2) { + return num1 + 200 + num2 +} +``` + +运行上述的函数,结果只会得到加200的值,对于同一函数名的函数,后者定义的会覆盖前者。 + +但是js可以实现重载吗?根据入参的不同执行不同的操作。 +完全可以 +第一种: +```javascript +function addNum() { + const a = arguments.length + switch (a) { + case 1: + break; + case 2: + break; + } +} +``` + +第二种: +使用了闭包的方法 + +```javascript +function addMethod (object, name, fn) { + let old = object[name] + // object 的 name属性就是存放这个判断函数的地方 + object[name] = function () { + // 这里匿名函数会有对应的词法作用域 + if (fn.length === arguments.length) { + //参数相等,表示匹配到了对应的处理函数,那么就执行他 + return fn.apply(this, arguments) + } else if (typeOf old == 'function') { + //参数不相等,执行原来的方法 + return old.apply(this, arguments) + } + } +} +let numbers = {value: 100, method: null } +// 我们执行 addMethod方法时,会在object的name属性上声明一个函数,这个函数的运行和fn有关 + +// 当没有参数的时候,返回对象的value值 +addMethod (numbers, method, function(){ + return this.value +}) +// 所以当我们运行numbers.method时,这时根据词法作用域,会去寻找fn的入参长度, +// 因为闭包的特性,我们能找到之前执行addMethod对应的fn,即function(){return this.value}这个匿名函数 +numbers.method() + +``` + diff --git "a/src/Other/\345\237\272\346\234\254\347\232\204shell\345\221\275\344\273\244.md" "b/src/Other/\345\237\272\346\234\254\347\232\204shell\345\221\275\344\273\244.md" new file mode 100644 index 0000000..71fc00d --- /dev/null +++ "b/src/Other/\345\237\272\346\234\254\347\232\204shell\345\221\275\344\273\244.md" @@ -0,0 +1,94 @@ +--- + title: '基本的shell命令' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2019-11-04 + category: '' + tag: 'sh' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ### 文件操作 + +```shell +ls #查看目录下文件 +ls -a #查看隐藏文件 +ls -F #在文件夹后面加了/,在可执行文件后面加了* +ls -R #递归遍历子文件夹 +ls -l #显示长列表形式的文件信息 + + +touch filename # 创建一个空文件 +cp source destination # 将源文件复制成一个新文件并且以 destination命名 + +# 制表键自动补全文件名 + +``` + +#### 链接文件 +如果需要在系统中维护同一文件的两份或者多分副本,除了保存多分单独的物理文件副本之外。还可以采用保存一份物理副本和多个虚拟副本的方法,这些虚拟副本被称为链接。链接是目录中指向文件真是位置的占位符。在linux中有两种不同类型的链接 +##### 符号链接 +符号链接就是一个实实在在的文件,它指向存放在虚拟目录结构中的某个地方的一个文件,这两个通过符号链接在一起的文件,彼此的内容并不相同 + +```shell +ln -s data_file rl_data_file +#以data_file文件创建一个rl_data_file符号链接,注意rl_data_file只是个占位符 + +# demo +wangcc:chenjian oukawa$ ln -s chenj rl_chenj +wangcc:chenjian oukawa$ ls -l chenj +-rw-r--r--@ 1 oukawa staff 11 10 30 22:07 chenj +wangcc:chenjian oukawa$ ls -l *chenj +-rw-r--r--@ 1 oukawa staff 11 10 30 22:07 chenj +lrwxr-xr-x 1 oukawa staff 5 10 30 22:08 rl_chenj -> chenj +wangcc:chenjian oukawa$ ls -i *chenj +8082497 chenj 8082525 rl_chenj +wangcc:chenjian oukawa$ + + +``` + +##### 硬链接 +硬链接会创建独立的虚拟文件,其中包含原始文件的信息及位置。引用硬链接文件等同于引用了原始文件。 + +> 只能对处于同一存储媒体的文件创建硬链接,要想在不同的存储媒体之间创建链接,只能是软链接。 + +### 重命名文件 + +```shell +mv fall fzll # 重命名文件称为移动 ,表示移动fall文件为fzll +``` + +### 删除文件 + +```shell +rm -i fall #删除fall文件,并且会提示 +rm -f fall #强制删除,不提示 +``` +### +### 处理目录 + +```shell +mkdir newDir #创建新目录 +rmdir newDir #删除空目录 +rm -rf newDir #全部删除,不提示 +``` + +### 查看文件 + +```shell +file my_file #查看文件是什么类型 +cat -n test1 #查看文件并加上行号 +#cat的缺陷是一旦运行就无法控制后面的操作 +more +``` + diff --git "a/src/Other/\346\210\221\344\273\254\347\234\237\347\232\204\344\270\215\351\234\200\350\246\201export default.md" "b/src/Other/\346\210\221\344\273\254\347\234\237\347\232\204\344\270\215\351\234\200\350\246\201export default.md" new file mode 100644 index 0000000..3e617c8 --- /dev/null +++ "b/src/Other/\346\210\221\344\273\254\347\234\237\347\232\204\344\270\215\351\234\200\350\246\201export default.md" @@ -0,0 +1,74 @@ +--- + title: '我们真的不需要export default' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2020-09-09 + category: '' + tag: 'emotibot' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ## 原文链接[https://zhuanlan.zhihu.com/p/40733281](https://zhuanlan.zhihu.com/p/40733281) +## 先说结论一: +![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1599660141441-fc985d64-f14d-43ec-be78-211492428e1b.png#align=left&display=inline&height=231&margin=%5Bobject%20Object%5D&name=image.png&originHeight=462&originWidth=1426&size=79607&status=done&style=none&width=713) +### 错误的写法 +```javascript +// lib.js +export default { + a: 1, + b: 2 +} +// main.js +import { a,b } from './lib'; +console.log('a:',a); +console.log('b:',b); +``` +-Q:为什么禁止对复合对象字面量的导出操作?(如上) +-A:babel5会将错误的写法输出成正确的值(babel5的bug),其实按照正确的esm语法,使输出为undefined的。 +-A:在esm和cjs互相转换的时候加大复杂性。如下图: +![image.png](https://cdn.nlark.com/yuque/0/2020/png/297368/1599660867222-47c57e86-4a29-45e5-8f12-5f1f7dc0486c.png#align=left&display=inline&height=314&margin=%5Bobject%20Object%5D&name=image.png&originHeight=628&originWidth=1420&size=114520&status=done&style=none&width=710) + + +## 正确使用ESM +### 导出 +```javascript +// lib.js +export function getName(){} +export const a = 1 +export default 'name' + +``` +### 导入 +```javascript +import * as all from './lib.js' +import lib from './lib.js' +import {a} from './lib.js' +``` + +## CJS的导入导出 +### 导出 +```javascript +module.export = {} +export.name = 'chenjian' +``` +### 导入 +```javascript +const name = require('./lib.js'); +``` + +## 原文链接[https://zhuanlan.zhihu.com/p/97335917](https://zhuanlan.zhihu.com/p/97335917) +## 再说结论二: +彻底禁用**export default** + + +## 我个人的观点: +esm一把梭,兼容问题是暂时的,就像微软不支持ie一样。该进步还是要进步。 diff --git "a/src/Other/\346\265\217\350\247\210\345\231\250\350\277\233\347\250\213\344\270\216\347\272\277\347\250\213.md" "b/src/Other/\346\265\217\350\247\210\345\231\250\350\277\233\347\250\213\344\270\216\347\272\277\347\250\213.md" new file mode 100644 index 0000000..db0e9d0 --- /dev/null +++ "b/src/Other/\346\265\217\350\247\210\345\231\250\350\277\233\347\250\213\344\270\216\347\272\277\347\250\213.md" @@ -0,0 +1,57 @@ +--- +title: '浏览器进程与线程' # 当前页面内容标题,默认为 Markdown 文件中的第一个 h1 标签内容 +shortTitle: '' # 当前页面的短标题 +description: '' # 当前页面内容描述 +icon: '' # 当前页面图标的 FontClass 或文件路径 (建议填写)。 +author: { + name: 'Song' +} +isOriginal: true # 当前文章是否为原创。 +date: 2024-04-03 # 写作时间。 +category: '' # 分类 +tag: '' # 标签 +sticky: 100 # 是否在列表中置顶。当填入数字时,数字越大,排名越靠前 +star: true # 是否收藏在博客主题的文章列表中。当填入数字时,数字越大,排名越靠前。 +article: true # 是否将该文章添加至文章列表中 +timeline: true # 是否将该文章添加至时间线中 +image: '' # 设置预览图 (分享图),请填入绝对路径 +editLink: false # 设置横幅图片 (宽屏分享图),请填入绝对路径。 +--- + +## 进程 + +浏览器是多进程架构,相比于单进程架构 + +- 有数据隔离的能力 +- 有多核优势 +- 各司其职,互不影响。单页面崩溃不会影响其他页面 + +### 包含的进程 + +1. Browser进程 + + 主进程 + +2. 插件进程 + + 插件管理 + +3. GPU进程 + +4. 渲染进程(浏览器内核) + + 每个tab一个进程 + +## 渲染进程 + +每个进程内部有多个线程,展开渲染进程: + +1. GUI渲染进程 +2. JS引擎线程 +3. 事件触发线程 +4. 定时触发线程 +5. 异步http请求线程 + +### GUI渲染线程与JS引擎线程互斥 + +道理就是js可以操作dom,dom没确定,是不会去渲染页面。 diff --git "a/src/Other/\351\207\215\346\216\222\351\207\215\347\273\230.md" "b/src/Other/\351\207\215\346\216\222\351\207\215\347\273\230.md" new file mode 100644 index 0000000..199c53f --- /dev/null +++ "b/src/Other/\351\207\215\346\216\222\351\207\215\347\273\230.md" @@ -0,0 +1,27 @@ +--- + title: '重排重绘' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2019-11-21 + category: '' + tag: 'javascript' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + 参考:[https://github.com/AlloyTeam/Mars/blob/master/performance/css-property-animation-performance.md](https://github.com/AlloyTeam/Mars/blob/master/performance/css-property-animation-performance.md) + +![relayout&&repaint&&composite.png](https://cdn.nlark.com/yuque/0/2019/png/297368/1574304166487-08c0ee90-8778-465e-861b-ca07e18f10a4.png#align=left&display=inline&height=684&name=relayout%26%26repaint%26%26composite.png&originHeight=684&originWidth=872&size=18498&status=done&width=872) + +repaint不一定触发relayout,但relayout一定会发生repaint。 + +- CSS动画属性会触发整个页面的重排relayout、重绘repaint、重组recomposite +- Paint通常是其中最花费性能的,尽可能避免使用触发paint的CSS动画属性,这也是为什么我们推荐在CSS动画中使用`webkit-transform: translateX(3em)`的方案代替使用`left: 3em`,因为`left`会额外触发layout与paint,而`webkit-transform`只触发整个页面composite diff --git "a/src/Poem/\344\270\200\347\250\213.md" "b/src/Poem/\344\270\200\347\250\213.md" new file mode 100644 index 0000000..ebf69d6 --- /dev/null +++ "b/src/Poem/\344\270\200\347\250\213.md" @@ -0,0 +1,24 @@ +--- + title: '旅程' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2023-09-25 + category: '' + tag: '随笔' + sticky: 8 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + +```html +山水同路,川流入海 +星汉灿灿, +``` diff --git "a/src/Poem/\345\275\222\345\256\266\346\234\211\346\204\237.md" "b/src/Poem/\345\275\222\345\256\266\346\234\211\346\204\237.md" new file mode 100644 index 0000000..ab2e145 --- /dev/null +++ "b/src/Poem/\345\275\222\345\256\266\346\234\211\346\204\237.md" @@ -0,0 +1,25 @@ +--- + title: '春节归家有感' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2023-09-25 + category: '' + tag: '随笔' + sticky: 8 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + +## 月 + +```html +南游万里客,群群向北归 +陌路无知己,又是异乡人 +``` diff --git "a/src/Poem/\346\227\240\351\242\230\357\274\210\344\270\200\357\274\211.md" "b/src/Poem/\346\227\240\351\242\230\357\274\210\344\270\200\357\274\211.md" new file mode 100644 index 0000000..000c442 --- /dev/null +++ "b/src/Poem/\346\227\240\351\242\230\357\274\210\344\270\200\357\274\211.md" @@ -0,0 +1,48 @@ +--- + title: '无题(一)' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2023-09-25 + category: '' + tag: '随笔' + sticky: 8 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + +```html +秋中桂香 + +异乡明月 + +常道共团圆 + + +离家万里 + +亲忧百遍 + +何物不思乡 + + +兢业无感 + +身无侣伴 + +落落锦衣还 + + +知命知命 + +无运无运 + +福祸皆健康 + +``` diff --git "a/src/Poem/\346\227\240\351\242\230\357\274\210\344\272\214\357\274\211.md" "b/src/Poem/\346\227\240\351\242\230\357\274\210\344\272\214\357\274\211.md" new file mode 100644 index 0000000..913599f --- /dev/null +++ "b/src/Poem/\346\227\240\351\242\230\357\274\210\344\272\214\357\274\211.md" @@ -0,0 +1,23 @@ +--- + title: '无题(二)' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2024-02-23 + category: '' + tag: '随笔' + sticky: 8 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + +```html +碎叶风飘零,新芽笑春风 +满城皆桃李,终有悲欢离 +``` diff --git "a/src/Poem/\346\234\210.md" "b/src/Poem/\346\234\210.md" new file mode 100644 index 0000000..41549bd --- /dev/null +++ "b/src/Poem/\346\234\210.md" @@ -0,0 +1,25 @@ +--- + title: '月' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2023-09-25 + category: '' + tag: '随笔' + sticky: 8 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + +## 月 + +```html +明月盈月,琼楼又宫阙 +阴晴圆缺,相逢复离别 +``` diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..6d63739 --- /dev/null +++ b/src/README.md @@ -0,0 +1,43 @@ +--- +home: true +layout: BlogHome +icon: home +title: Song +heroImage: /logo.svg +bgImage: /PastedGraphic.png +heroText: SongBlog +heroFullScreen: true +tagline: '小学篱笆旁的蒲公英......' +# projects: +# - icon: project +# name: 项目名称 +# desc: 项目详细描述 +# link: https://你的项目链接 + +# - icon: link +# name: 链接名称 +# desc: 链接详细描述 +# link: https://链接地址 + +# - icon: book +# name: 书籍名称 +# desc: 书籍详细描述 +# link: https://你的书籍链接 + +# - icon: article +# name: 文章名称 +# desc: 文章详细描述 +# link: https://你的文章链接 + +# - icon: friend +# name: 伙伴名称 +# desc: 伙伴详细介绍 +# link: https://你的伙伴链接 + +# - icon: /logo.svg +# name: 自定义项目 +# desc: 自定义详细介绍 +# link: https://你的自定义链接 + +footer: 无畏宿命,无畏的你 +--- diff --git a/src/Safety/CSRF.md b/src/Safety/CSRF.md new file mode 100644 index 0000000..7ac6303 --- /dev/null +++ b/src/Safety/CSRF.md @@ -0,0 +1,190 @@ +--- + title: 'CSRF(Cors Site Request Forgery 跨站请求伪造)' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2023-09-01 + category: '' + tag: 'csrf' + sticky: 9 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + +## 什么是CSRF + +跨站请求伪造(英语:Cross-site request forgery),也被称为one-click attack或者session riding,通常缩写为CSRF或者XSRF,是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。 跟跨网站脚本(XSS)相比,XSS利用的是用户对指定网站的信任,CSRF利用的是网站对用户网页浏览器的信任。 + +[](https://portswigger.net/web-security/images/cross-site%20request%20forgery.svg) + +## 攻击的原理 + +简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求是发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。也就是上面所说的,利用网站对用户网页浏览器的信任。 + +要实现CSRF攻击,需要满足3个关键条件: + +1. 攻击者有理由诱发应用程序内的某个操作。这可能是特权操作(例如修改其他用户的权限)或对用户特定数据的任何操作(例如更改用户自己的密码)。 + +2. 基于 Cookie 的会话处理。执行该操作涉及发出一个或多个 HTTP 请求,并且应用程序仅依赖会话 cookie 来识别发出请求的用户。没有其他机制可以跟踪会话或验证用户请求。 + +3. 没有不可预知的请求参数。执行操作的请求不包含攻击者无法确定或猜测其值的任何参数。例如,当导致用户更改密码时,如果攻击者需要知道现有密码的值,则该功能不易受到攻击。 + +### 举个栗子 + +- 受害者登录a.com,并保留了登录凭证(Cookie)。 +- 攻击者引诱受害者访问了b.com。 +- b.com 向 a.com 发送了一个请求:a.com/act=xx。浏览器会默认携带a.com的Cookie。 +- a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。 +- a.com以受害者的名义执行了act=xx。 +攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作。 + +有这么一个请求: + +```curl +POST /email/change HTTP/1.1 +Host: vulnerable-website.com +Content-Type: application/x-www-form-urlencoded +Content-Length: 30 +Cookie: session=yvthwsztyeQkAPzeQ5gHgTvlyxHfsAfE + +email=wiener@normal-user.com +``` + +那么就可以这么触发: + +```html + + +
+ +
+ + + +``` + +## 攻击的类型 + +### GET类型 + +假如一家银行用以执行转账操作的URL地址如下: `https://bank.example.com/withdraw?account=AccoutName&amount=1000&for=PayeeName` + +那么,一个恶意攻击者可以在另一个网站上放置如下代码: `` + +### POST类型 + +如上的form表单提交 + +### 链接类型 + +需要用户点击触发 + +` + 重磅消息!! +` + +> CSRF通常是跨域的,因为外域通常更容易被攻击者掌控。但是如果本域下有容易被利用的功能,比如可以发图和链接的论坛和评论区,攻击可以直接在本域下进行,而且这种攻击更加危险。 + +## 防御措施 + +从上面的介绍,我们可以看出csrf攻击有这些特点: + +1. 多发生在外域 +2. 获取不到cookie,只是使用 + +### 同源检测 + +- Origin Header + + 检查Http请求头中的Referer字段. 但是这种方式还有有局限性的,因为存在篡改Referer字段的可能。 + +- Referer Header + + 但是Origin在以下两种情况下并不存在: + + 1. IE11同源策略: IE 11 不会在跨站CORS请求上添加Origin标头,Referer头将仍然是唯一的标识。最根本原因是因为IE 11对同源的定义和其他浏览器有不同,有两个主要的区别,可以参考MDN Same-origin_policy#IE_Exceptions + 2. 302重定向: 在302重定向之后Origin不包含在重定向的请求中,因为Origin可能会被认为是其他来源的敏感信息。对于302重定向的情况来说都是定向到新的服务器上的URL,因此浏览器不想将Origin泄漏到新的服务器上。 + +### 添加校验token + +Token不能放在Cookie中,服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击。 + +Token是一个比较有效的CSRF防护方法,只要页面没有XSS漏洞泄露Token,那么接口的CSRF攻击就无法成功。 + +### 令牌同步模式 + +令牌同步模式(英语:Synchronizer token pattern,简称STP) + +Synchronizer Token Pattern 的工作原理如下: + +1. 令牌生成: 在用户登录Web应用时,服务器会为该用户生成一个唯一的令牌(也称为 CSRF 令牌或同步令牌)。这个令牌通常是随机生成的,与用户的会话相关,并且存储在会话中或者通过Cookie发送给客户端。 + +2. 令牌嵌入: 在Web应用中,每个可能受到CSRF攻击的请求都会在表单中嵌入这个令牌。这可以通过将令牌作为隐藏字段或自定义HTTP头部的一部分包含在请求中来实现。 + +3. 请求验证: 当用户提交包含令牌的请求时,服务器会验证请求中的令牌与用户会话中的令牌是否匹配。如果匹配成功,请求被视为有效。如果不匹配,服务器会拒绝请求,因为这可能是CSRF攻击的尝试。 + +4. 保护: 如果请求验证失败,服务器将拒绝请求,防止未经授权的操作。这种方式确保了只有知道有效令牌的用户才能执行敏感操作。 + +和校验token得区别 + +Synchronizer Token Pattern: + +主要目的: 防止跨站请求伪造(CSRF)攻击。它确保每个敏感操作请求都伴随一个与用户会话相关的令牌。 + +工作方式: 服务器在用户登录后生成一个随机的 CSRF 令牌,并将其与用户会话相关联。每个可能受到 CSRF 攻击的请求都需要包含这个令牌。服务器在接收请求时验证令牌是否匹配用户会话中的令牌。如果匹配成功,请求被视为有效;否则,请求被拒绝。 + +使用 Token 进行校验的模式: + +主要目的: 进行身份验证和授权。这种模式通常用于验证用户的身份,授权其访问资源或执行操作。 + +工作方式: 用户登录后会收到一个身份验证令牌(通常称为访问令牌或身份令牌)。客户端在每个请求中都要携带这个令牌,以证明其身份。服务器会验证令牌的有效性,如果令牌有效且具有适当的权限,则允许访问请求的资源或执行操作。 + +### Set-Cookie + +服务器使用 Set-Cookie 响应头部向用户代理(一般是浏览器)发送 Cookie 信息。一个简单的 Cookie 可能像这样: + +```http +Set-Cookie: = +``` + +现在,对该服务器发起的每一次新请求,浏览器都会将之前保存的 Cookie 信息通过 Cookie 请求头部再发送给服务器。 + +```http +Cookie: yummy_cookie=choco; tasty_cookie=strawberry +``` + +#### Secure 属性 + +标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端。它永远不会使用不安全的 HTTP 发送(本地主机除外),这意味着中间人攻击者无法轻松访问它。不安全的站点(在 URL 中带有 http:)无法使用 Secure 属性设置 cookie。但是,Secure 不会阻止对 cookie 中敏感信息的访问。例如,有权访问客户端硬盘(或,如果未设置 HttpOnly 属性,则为 JavaScript)的人可以读取和修改它。 + +#### HttpOnly 属性 + +![Alt text](image.png) + +带有 HttpOnly 属性的 cookie;此类 Cookie 仅作用于服务器。例如,持久化服务器端会话的 Cookie 不需要对 JavaScript 可用,而应具有 HttpOnly 属性。此预防措施有助于缓解跨站点脚本(XSS) (en-US)攻击。 + +```http +Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly +``` + +### SameSite 属性 + +SameSite 属性允许服务器指定是否/何时通过跨站点请求发送(其中站点由注册的域和方案定义:http 或 https)。这提供了一些针对跨站点请求伪造攻击(CSRF)的保护。它采用三个可能的值:Strict、Lax 和 None。 + +>SameSite 属性允许服务器指定是否/何时通过跨站点请求发送(其中站点由注册的域和方案定义:http 或 https)。这提供了一些针对跨站点请求伪造攻击(CSRF)的保护。它采用三个可能的值:Strict、Lax 和 None。 + +## 总结 + +防护策略: + +1. CSRF自动防御策略:同源检测(Origin 和 Referer 验证)。 +2. CSRF主动防御措施:Token验证 或者 双重Cookie验证 以及配合Samesite Cookie。 +3. 保证页面的幂等性,后端接口不要在GET页面中做用户操作。 diff --git a/src/Safety/XSS.md b/src/Safety/XSS.md new file mode 100644 index 0000000..06add5c --- /dev/null +++ b/src/Safety/XSS.md @@ -0,0 +1,218 @@ +--- + title: 'XSS' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2020-09-01 + category: '' + tag: 'xss' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + +# Cross-site scripting(跨站脚本攻击) + +### 背景和现状 +当网景(Netscape)最初推出JavaScript语言时,他们也察觉到准许网页服务器传送可执行的程式码给一个浏览器的安全风险(即使仅是在一个浏览器的沙盒里)。它所造成的一个关键的问题在于使用者同时开启多个浏览器视窗时,在某些例子里,网页里的片断程式码被允许从另一个网页或物件取出资料,而因为恶意的网站可以用这个方法来尝试窃取机密资讯,所以在某些情形,这应是完全被禁止的。为了解决这个问题,浏览器采用了同源决策——仅允许来自相同网域名称系统和使用相同协定的物件与网页之间的任何互动。 + +XSS漏洞可以追溯到1990年代。大量的网站曾遭受XSS漏洞攻击或被发现此类漏洞,如Twitter,Facebook,MySpace,Orkut ,新浪微博和百度贴吧 。研究表明,最近几年XSS已经超过缓冲区溢出成为最流行的攻击方式,有68%的网站可能遭受此类攻击。根据开放网页应用安全计划(Open Web Application Security Project)公布的2010年统计数据,在Web安全威胁前10位中,XSS排名第2,仅次于代码注入(Injection)。 + +### 什么是XSS +Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击。它允许攻击者规避同源策略,利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java,VBScript,ActiveX,Flash或者甚至是普通的HTML。攻击成功后,攻击者可能得到更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。 + +### 工作原理 + +![work](https://gyenno-test.oss-cn-beijing.aliyuncs.com/pub/miniprogram/demo-test/cross-site-scripting.svg) + +不仅仅是业务上的“用户的 UGC 内容”可以进行注入,包括 URL 上的参数等都可以是攻击的来源。在处理输入时,以下内容都不可信: + +- 来自用户的 UGC 信息 +- 来自第三方的链接 +- URL 参数 +- POST 参数 +- Referer (可能来自不可信的来源) +- Cookie (可能来自其他子域注入) + +### 演示环节 + +举一个🌰 + +vue中的v-html + +`Money` + +### 注入方法 + +- 在 HTML 中内嵌的文本中,恶意内容以 script 标签形成注入。 +- 在内联的 JavaScript 中,拼接的数据突破了原本的限制(字符串,变量,方法名等)。 +- 在标签属性中,恶意内容包含引号,从而突破属性值的限制,注入其他属性或者标签。 +- 在标签的 href、src 等属性中,包含 javascript: 等可执行代码。 +- 在 onload、onerror、onclick 等事件中,注入不受控制代码。 +- 在 style 属性和标签中,包含类似 background-image:url("javascript:..."); 的代码(新版本浏览器已经可以防范)。 +- 在 style 属性和标签中,包含类似 expression(...) 的 CSS 表达式代码(新版本浏览器已经可以防范)。 + +### 分类 + +#### 存储型XSS(Stored XSS) + +恶意脚本来自数据库 + +[演示地址](https://portswigger.net/web-security/cross-site-scripting/stored/lab-html-context-nothing-encoded) + +存储型 XSS 的攻击步骤: + +1. 攻击者将恶意代码提交到目标网站的数据库中。 +2. 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。 +3. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。 +4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。 + +这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。 + +#### 反射行XSS(Reflected XSS) + +恶意脚本来自当前的http请求 + +假设一个网站有一个搜索功能,它接收用户在 URL 参数中提供的搜索词: +``` +https://www.website.com/search?value=gift +``` +应用程序会在对此 URL 的响应中回显所提供的搜索词: + +``` +

搜索关键词: gift

+``` +如果将搜索词变成一段脚本 + +``` +https://www.website.com/search?value= +``` + +[演示地址](https://portswigger.net/web-security/cross-site-scripting/reflected/lab-html-context-nothing-encoded) + +反射型 XSS 的攻击步骤: + +1. 攻击者构造出特殊的 URL,其中包含恶意代码。 +2. 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。 +3. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。 +4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。 + +> 反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。 + +> 反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等。 + +> POST 的内容也可以触发反射型 XSS,只不过其触发条件比较苛刻(需要构造表单提交页面,并引导用户点击),所以非常少见。 + +#### DOM型XSS(DOM-based XSS) + +漏洞存在于客户端代码而不是服务器端代码中。 + +``` + + + + + + + Document + + +
我的银行卡密码是123456
+ + +
+ + + +``` + +DOM 型 XSS 的攻击步骤: + +1. 攻击者构造出特殊的 URL,其中包含恶意代码。 +2. 用户打开带有恶意代码的 URL。 +3. 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。 +4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。 + +DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。 + +## 检测方法 + +### 工具扫描 + +#### w3af + +#### nikto + +### 手动检测 + +``` +jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//\x3csVg/\x3e +``` + +## 预防措施 + +从上面三种类型的xss攻击方式中,我们可以看出: + +- 恶意代码被提交到后端 +- 浏览器执行了恶意代码 + +### (阻止恶意代码被提交到后端)--> 输入过滤转义 + +#### 前端进行过滤 + +可行,但是如果绕过前端直接请求接口,还是可以提交恶意代码。 + +#### 后端进行过滤 + +可行,但是要确保,消费数据的端能够正常解析转义后的内容。 + +例如,浏览器可以直接渲染转义后的`< `, +但是在vue模版中,`< `被认为是个字符串。 + +所以数据消费,有一定的心智负担。 + +### (阻止浏览器执行恶意代码)--> 防止HTML中出现注入 + +#### Reflected XSS/ Stored XSS + +- 纯前端渲染 + +浏览器直接明确渲染方式,例如innerText,避免使用innerHtml,v-html + +- 转义HTML + + 有时候确实要拼接HTML,使用转义库。 + +#### Dom XSS + +- 避免使用v-html,innerHTML,outerHTML, + +- 避免将不可信数据直接应用于内联事件监听器,如href,onload,onclick等 + +### 内容安全策略(CSP) + diff --git a/src/Safety/image.png b/src/Safety/image.png new file mode 100644 index 0000000..2a17566 Binary files /dev/null and b/src/Safety/image.png differ diff --git a/src/Vue/Vue3.md b/src/Vue/Vue3.md new file mode 100644 index 0000000..38b04f0 --- /dev/null +++ b/src/Vue/Vue3.md @@ -0,0 +1,150 @@ +--- + title: 'VUE3' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2020-07-29 + category: '' + tag: 'vue vue3' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + +## tree-shaking + +## fragments碎片 + +## vite + +基于网络请求的依赖加载 + +## composition API + +- 2.X 基于选项的api(options API) +- 3.X 基于函数的api +-- 类型支持 +-- 压缩友好 + +现有api不影响, +共存,组件的封装和架构会更友好 + +单文件组件太大,不容易切分 + +## 动机 + +### 逻辑复用模式 + +在vue2中(options api): + +- 当一个组件中使用了多个mixin的时候光看模版很难分清一个属性来自哪一个 +- 命名空间冲突,多个开发者无法保证属性或者方法名不重复 +- 性能方面,高阶组件和无状态组件都需要额外创建组件实例来封装逻辑 + +## now + +composition api 组合式api +composition function 组合函数 +function-based api 基于函数的api,使用基于函数的api将相关联的代码抽取到一个组合函数中,该函数封装了相关联的逻辑,并将需要暴露给组件的状态以响应式的数据源方式返回出来。 +e.g + +```js +function useMouse() { + const x = ref(0) + const y = ref(0) + const update = e => { + x.value = e.pageX + y.value = e.pageY + } + onMounted(() => { + window.addEventListener('mousemove', update) + }) + onUnmounted(() => { + window.removeEventListener('mousemove', update) + }) + return { x, y } +} + +// 在组件中使用该函数 +const Component = { + setup() { + const { x, y } = useMouse() + // 与其它函数配合使用 + const { z } = useOtherLogic() + return { x, y, z } + }, + template: `
{{ x }} {{ y }} {{ z }}
` +} +``` + +> 组合函数useMouse里面就有很多基于函数的api,然后将状态x,y返回出来,组件里面就可以用了 + + +## setup函数 + +这一个新引入的组件选项,它会在一个组件实例被创建时,初始化了props之后调用。会将接受到的props作为参数 + +## ref函数 + +如果我们想在setup内部创建一个被管理的值。可以使用ref函数,它返回的是一个包装对象。 +包装函数包装str和num这些基本类型是为了能更好追踪它们的变化。如果一个函数返回的是1这个数字,接收到这个数值的代码只会获得一个值。 +包装函数去包装对象或是数组也是有意义的。他可以让我们替换整个对象的值 +e.g + +```js +const numbers = ref([1, 2, 3]) +// 替代原数组,但引用不变 +numbers.value = numbers.value.filter(n => n > 1) +``` + +> 当包装对象被暴露给模版渲染上下文,或是被嵌套在另一个响应式对象中的时候,它会被自动展开 (unwrap) 为内部的值。 + + +## Watchers + +和 2.x 的 $watch 有所不同的是,watch() 的回调会在创建时就执行一次。这有点类似 2.x watcher 的 immediate: true 选项,但有一个重要的不同:默认情况下 watch() 的回调总是会在当前的 renderer flush 之后才被调用 —— 换句话说,watch()的回调在触发时,DOM 总是会在一个已经被更新过的状态下。 这个行为是可以通过选项来定制的。 + +> 在 2.x 的代码中,我们经常会遇到同一份逻辑需要在 mounted 和一个 watcher 的回调中执行(比如根据当前的 id 抓取数据),3.0 的 watch() 默认行为可以直接表达这样的需求。 + + +观察多个数据源的时候只要任意一个数据源改变都会出发回调 + +### 停止观察 + +`watch()`返回的是一个停止观察的函数 + +### 清理副作用 + +watch的回调函数支持传入第三个参数。这第三个参数是一个用来注册清理操作的函数。调用这个函数会注册一个清理函数。 + +> 什么情况下会用到清理函数?比如当一个异步操作还没没完成,观测的数据就变化了。我们可能要撤销还在等待结果的前一个操作。 + +#### 和react对比 + +react的`useEffect`方法是直接返回一个清理函数。但是vue没有,而是采用传入注册函数来注册清理函数,是因为vue的watch回调中往往是异步操作,而异步的async函数返回的是个promise,这无法保证需要一个立刻注册的清理函数。 + +#### Watcher 回调的调用时机 + +默认情况下,所有的 watcher 回调都会在当前的 renderer flush 之后被调用。这确保了在回调中 DOM 永远都已经被更新完毕。如果你想要让回调在 DOM 更新之前或是被同步触发,可以使用 flush 选项: + +```js +watch( + () => count.value + 1, + () => console.log(`count changed`), + { + flush: 'post', // default, fire after renderer flush + flush: 'pre', // fire right before renderer flush + flush: 'sync' // fire synchronously + } +) +``` + +## 强调点 + +Vue 的 setup() 每个组件实例只会在初始化时调用一次 ,状态通过引用储存在 setup() 的闭包内。 diff --git a/src/Vue/Vue3_Tips.md b/src/Vue/Vue3_Tips.md new file mode 100644 index 0000000..2c4a5de --- /dev/null +++ b/src/Vue/Vue3_Tips.md @@ -0,0 +1,30 @@ +--- + title: 'vue3 tips' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2020-06-18 + category: '' + tag: 'vue' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ### ref/toRef/toRefs +因为 props 是响应式的,你**不能使用 ES6 解构**,它会消除 prop 的响应性 +如果需要解构 prop,可以在 setup 函数中使用 [toRefs](https://v3.cn.vuejs.org/guide/reactivity-fundamentals.html#%E5%93%8D%E5%BA%94%E5%BC%8F%E7%8A%B6%E6%80%81%E8%A7%A3%E6%9E%84) 函数来完成。 +```javascript +const { name } = toRefs(props) +``` +但是当name属性是可选属性的时候,`toRefs`就失效了。此时需要使用toRef +```javascript +const name = toRef(props, 'name') +``` + diff --git "a/src/Vue/Vue\344\270\216React\347\232\204\345\214\272\345\210\253.md" "b/src/Vue/Vue\344\270\216React\347\232\204\345\214\272\345\210\253.md" new file mode 100644 index 0000000..fbf0929 --- /dev/null +++ "b/src/Vue/Vue\344\270\216React\347\232\204\345\214\272\345\210\253.md" @@ -0,0 +1,78 @@ +--- +title: 'Vue与React的区别' # 当前页面内容标题,默认为 Markdown 文件中的第一个 h1 标签内容 +shortTitle: '' # 当前页面的短标题 +description: '' # 当前页面内容描述 +icon: '' # 当前页面图标的 FontClass 或文件路径 (建议填写)。 +author: { + name: 'Song' +} +isOriginal: true # 当前文章是否为原创。 +date: 2024-04-01 # 写作时间。 +category: '' # 分类 +tag: '' # 标签 +sticky: 100 # 是否在列表中置顶。当填入数字时,数字越大,排名越靠前 +star: true # 是否收藏在博客主题的文章列表中。当填入数字时,数字越大,排名越靠前。 +article: true # 是否将该文章添加至文章列表中 +timeline: true # 是否将该文章添加至时间线中 +image: '' # 设置预览图 (分享图),请填入绝对路径 +editLink: false # 设置横幅图片 (宽屏分享图),请填入绝对路径。 +--- + +- [参考链接](https://mp.weixin.qq.com/s/6QTdeM2l1htofCnKLZS6yw) + +## 从核心差异出发 + +从核心差异出发,能更清晰的将两者的不同形成脉络。 + +**核心差异:Vue进行数据的拦截/代理,颗粒度更细。React偏重函数式,页面更新就是不断的执行函数。** + +从这个核心差异衍生出来的不同: + +1. 保证页面渲染性能的手段不一样 + vue因为颗粒度更细,所以能采用依赖追踪,变多少,动多少,自动更新。React则将这个更新的心智通过 + shouldComponentUpdate这个生命周期hook交给开发者控制)。(在这一块,他们也有共同点,例如合并更新操作。) + +2. Hooks的实现和表现不一致 + React在每次render时都会按照顺序执行,但是vue主会被注册调用一次,因为他是基于数据代理观察的。这也就是为什么 + vue会包装.value这么一层,因为基于对象代理观察,那么一些基本类型,number和string是没办法做到的。 + +## 共同点 + +### 数据驱动视图 + +数据的变动会通知所有订阅者,通过diff算法,实现最小更新 + +### 组件化 + +组件化思想,所以都需要考虑数据流的问题,数据状态管理的解决方案也就自然而然 + +### Virtual DOM + +### 单向数据流 + +## 不同点 + +### 实现数据驱动视图的方式 + +实现方式的不同,会导致优化能力的不同。参考上面的核心差异 + +> Vue 需要做数据双向绑定,需要进行数据拦截或代理,那它就需要在预编译阶段静态分析模版,分析出视图依赖了哪些数据,进行响应式处理。而 React 就是局部重新渲染,React 拿到的或者说掌管的,所负责的就是一堆递归 React.createElement 的执行调用,它无法从模版层面进行静态分析 + +### 设计风格 + +React早期是class组件和生命周期,这就引申出高阶组件 + +Vue也是有生命周期的概念 + +探究生命周期的原理,其实它就是收集和处理副作用的节点,是副作用在视图不同阶段的产物。 + +这里需要思考,什么是**副作用**,怎么理解它。 + +其实从Vue3到React,两者都很默契的弱化了生命周期这个概念,着重介绍Hooks。 + +## React的特色 + +专注函数组件 + Hooks的开发模式,淡化生命周期的意识。 +本质就是如何利用好函数组件一遍遍重复执行变更视图。 + +众所周知周知,React的Hooks调用有限制,是因为Hooks是基于链表调用的。 diff --git a/src/Vue/vite.md b/src/Vue/vite.md new file mode 100644 index 0000000..36822a4 --- /dev/null +++ b/src/Vue/vite.md @@ -0,0 +1,59 @@ +--- + title: 'vite' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2022-03-18 + category: '' + tag: 'vue vite' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ## bundless +依据浏览器支持的ESM模块,去分别拉取对应的文件 + +- 冷启动快(使用esbuild-golang语言) +- 热更新快 + +## 创建构建流程 + +1. 创建koa服务 +2. 使用chokidar监听文件 +3. 模块解析 +4. 增加各种中间件 +5. 启动服务 + +## 静态文件托管 + +1. Vite会利用serverStaticPlugin将src和public目录设置为静态目录 +2. serverStaticPlugin利用koa的koa-etag中间件打tag +3. devServer具备静态文件服务功能 + +## 模块路径重写 + +1. 处理裸导入: `import vue from vue` ,浏览器识别不了(只能识别相对路径或者绝对路径) +2. 对于非JS的请求路径,加上import参数 + +## 静态资源打包策略 + +1. 获取vue内容,分别识别是否有template/style部分,有的话就发起请求 +### CSS打包策略 + +1. 判断是否是.css文件的请求 +2. 判断是否为js中的import css +3. 进行预编译处理 +4. 基于css的源码包装成js模块 +5. 创建style标签将css代码插入dom +6. 判断是否开启css module,是则导出对象否则导出css代码 + +### 模板打包策略 + +1. 使用compiler-dom编译template ,然后返回浏览器 diff --git "a/src/Vue/vue\345\272\225\345\261\202\346\236\266\346\236\204\347\254\224\350\256\260.md" "b/src/Vue/vue\345\272\225\345\261\202\346\236\266\346\236\204\347\254\224\350\256\260.md" new file mode 100644 index 0000000..728d617 --- /dev/null +++ "b/src/Vue/vue\345\272\225\345\261\202\346\236\266\346\236\204\347\254\224\350\256\260.md" @@ -0,0 +1,113 @@ +--- + title: 'vue底层架构笔记' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2021-11-23 + category: '' + tag: 'vue' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + > 了解Vue的底层架构,是为非web领域提供Vue能力的大前提。Vue核心分为三大块:core,compiler,platform + +原文地址:[https://juejin.im/post/5cef749451882530810e0626#heading-8](https://juejin.im/post/5cef749451882530810e0626#heading-8) +## core +core的作用是依据模板生成对应的虚拟节点——vnode,而后通过diff算法更新视图。所以可以根据生成的vnode,变成你想要的任何东西,不仅仅是在web平台的dom节点。 +![](https://cdn.nlark.com/yuque/0/2019/png/297368/1560759707817-e6b12cd5-3e82-4495-9846-3f7b5c9e324b.png#height=259&id=BmMCi&originHeight=259&originWidth=789&originalType=binary&ratio=1&rotation=0&showTitle=false&size=0&status=done&style=none&title=&width=789) +### 挂载 +在web平台来说,指的是将生成的dom节点,append到指定的元素节点上。我们可以看到在web平台的项目中main.js中的一段代码: + +```javascript +new Vue({ + router, + store, + render: h => h(App) +}).$mount('#app') +``` +这就是一个挂载操作,使用render方法生成对应的vnode,然后根据vnode生成指定平台(这里是web平台)的元素,然后append到app节点上。 +### 指令 +vue中的指令区分为编译时处理和运行时处理这两类,像 `v-for` 和 `v-if` 这些是编译时使用,在render生成vnode时就会生效 +### VNode +vnode是依据模板生成的虚拟节点,每一个元素对应一个vnode + +```javascript +
------------------对应一个vnode +

哈哈

-------对应一个vnode + ----------自定义组件同样对应一个vnode +
-----------------------对应一个vnode +
+``` + +首先是经过vue的complie模块生成render函数,然后执行render函数方法,生成vnode结构。如下: + +```javascript +//这里我只列出关键的vnode信息 +{ + tag:'div', + data:{attr:{},staticClass:'box',on:{click:onClick}}, + children:[{ + tag:'p', + data:{attr:{},staticClass:'content',on:{}}, + children:[{ + tag:'', + data:{}, + text:'哈哈' + }] + },{ + tag:'div', + data:{attr:{},on:{}}, + },{ + tag:'TestComps', + data:{ + attr:{}, + hook:{ + init:fn, + prepatch:fn, + insert:fn, + destroy:fn + } + }, + }] +} + +``` + +根据vnode关系,可以很清晰区分子节点和父节点。 +### vm +在vue中vm是组件实例。是对应一个自定义组件的,即只有自定义组件才有vm。 +### nextTick +在下一个事件循环执行,老版本的vue实现的方式是通过mutationsObserve监听dom变化来响应的,现在2.5+版本则是在promise不支持的情况下使用messageChannel方式(它输入宏任务macrotask) +### watcher +一个组件对应一个watcher,被观察的对象包括data,props,watch方法中的值,当被观察的对象发生变化的时候,会触发render函数生成新的vnode,然后通过diff算法以最小的代价更新视图。 +![](https://cdn.nlark.com/yuque/0/2019/webp/297368/1560765258435-1ea9f026-aede-4a67-ae1e-c40de9771637.webp#height=250&id=C3l8y&originHeight=250&originWidth=780&originalType=binary&ratio=1&rotation=0&showTitle=false&size=0&status=done&style=none&title=&width=780) + +> **整个watcher体系的建立过程**: +> 1. 创建组件实例的时候会对data和props进行observer, +> 2. 对传入的props进行浅遍历,重新设定属性的属性描述符get和set,如果props的某个属性值为对象,那么这个对象在父组件是被深度observe过的,所以props是浅遍历 +> 3. observer会深度遍历data,对data所包含属性重新定义,即defineReactive,重新设定属性描述符的get和set +> 4. 在mountComponent的时候,会new Wacther,当前watcher实例会被pushTarget,设定为目标watcher,然后执行`vm._update(vm._render(), hydrating)`,执行render函数导致属性的get函数被调用,每个属性会对应一个dep实例,在这个时候,dep实例关联到组件对应的watcher,实现依赖收集,关联后popTarget。 +> 5. 如果有子组件,会导致子组件的实例化,重新执行上述步骤 +> + + +### vnode diff 算法 + +## complier +complier的作用是负责对模板编译,生成render和staticRender函数,这两个函数会生成vnode,给core使用 +> template ==> AST ==> 递归ATS生成render和staticRender ==> VNode +> template => parse(正则匹配,AST) => optimize(标注静态节点) => generate(生成render) => 运行render function => VNode + + +## platform +> platform模块与具体平台相关,我们可以在这里定义平台相关接口传入runtime和compile,以实现具体平台的定制化,因此为其他平台带来Vue能力,大部分工作在这里。 + + diff --git "a/src/Vue/\345\205\263\344\272\216props.md" "b/src/Vue/\345\205\263\344\272\216props.md" new file mode 100644 index 0000000..7c2f3b2 --- /dev/null +++ "b/src/Vue/\345\205\263\344\272\216props.md" @@ -0,0 +1,56 @@ +--- + title: '关于props' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2019-07-02 + category: '' + tag: 'vue' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + 在编码过程中遇到这样的问题:父组件动态传给子组件一个对象,在子组件里,将这个props赋值给data +代码如下: + +```javascript +// 子组件代码 +props: ['loanData'], +data () { + return { + info: this.loanData, + } +}, +// 父组件代码 + +// 其中这个loanData初始值为空,通过接口调用来赋值数据 +``` + +按照我的理解: +子组件在父组件的beforeMount生命周期之后开始运行它自己的生命周期函数,由于loanData初始为空,在开始执行子组件的生命周期函数的时候,并不能保证接口已经有了返回值。所以props的值第一次可能为空,也可能有值。 +不管props初始有值还是为空,只要父元素的loanData更新了,那么子元素也会得到更新。 + +以上这些都是没问题,在vue的调试工具中,子组件props总是最新的值,即父组件的loanData更新子组件的props也会更新。 + +回到上面的代码,在子组件的data时,我是将props初始化了info属性,如果loanData更新,你们觉得info会更新吗?我第一次写的时候就是理所当然的认为会更新,但事实是info并没有改变,在初始化子组件的时候,props.loanData为空,info也就初始化为空,后面就算更新了props.loanData的值,但是data并不会初始化2次。 + +会出现这样的错误还是对vue的watcher机制不理解。 + +> 一个组件对应一个watcher,在挂载组件的时候创建这个观察者,组件的state,包含data,props都是被观察者,被观察者的任何变化会被通知到观察者,被观察者的变动导致观察者执行的动作是`vm._update(vm._render(), hydrating)`,组件重新render生成vnode并patch。 +> +> 作者:腾讯IVWEB团队 +> 链接:https://juejin.im/post/5cef749451882530810e0626 + +> 来源:掘金 + +> 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 + + +如上描述,子组件的mounted执行完毕是在父组件的mounted生命周期之前,所以也就是先生成子组件的观察者,对于data中info属性和props都建立了观察。接着父元素的data也建立观察。data初始化时loanData为空这个应该没有异议。接着接口返回数据,在父组件中,观测到loanData变化了,那么父组件就回渲染生成新的vnode与旧的vnode进行patch。在这个过程中子组件也会被更新,因为子组件也是对props进行的观察监听**(对传入的props进行浅遍历,重新设定属性的属性描述符get和set,如果props的某个属性值为对象,那么这个对象在父组件是被深度observe过的,所以props是浅遍历)**。所以props.loanData从空到有值。但是为什么info没更新?因为没有触发info的set,也就是没有触发info的更新。wathcer会生效不是自动生效,而是调用了set函数,这个动作才会通知依赖更新。 diff --git a/src/Web3/WEB3.0.pdf b/src/Web3/WEB3.0.pdf new file mode 100644 index 0000000..97d6978 Binary files /dev/null and b/src/Web3/WEB3.0.pdf differ diff --git "a/src/Webpack/vue\345\222\214react\347\232\204\344\273\243\347\240\201\345\210\206\345\211\262.md" "b/src/Webpack/vue\345\222\214react\347\232\204\344\273\243\347\240\201\345\210\206\345\211\262.md" new file mode 100644 index 0000000..f1b7eda --- /dev/null +++ "b/src/Webpack/vue\345\222\214react\347\232\204\344\273\243\347\240\201\345\210\206\345\211\262.md" @@ -0,0 +1,68 @@ +--- + title: 'vue和react的代码分割' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2019-06-26 + category: '' + tag: 'webpack' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ### vue中的代码分割(路由懒加载) +结合异步组件和webpack的代码分割功能来实现路由组件的懒加载 +> 链接地址 [https://router.vuejs.org/zh/guide/advanced/lazy-loading.html](https://router.vuejs.org/zh/guide/advanced/lazy-loading.html) + +首先,将异步组件定义为返回一个Promise的工厂函数 +`const foo = () => Promise.resolve({ /*组件定义对象*/ })`  +然后根据webpack代码分割规则,定义分割点 +`import('./Foo.vue')`  // 返回Promise + +> 注意: +> 动态 `import()` 语法是ECMAScript(JavaScript)[提案](https://github.com/tc39/proposal-dynamic-import),目前不是语言标准的一部分。预计在不远的将来会被接受。 + + + +> 注意 +> 如果您使用的是 Babel,你将需要添加 [`syntax-dynamic-import`](https://babeljs.io/docs/plugins/syntax-dynamic-import/) 插件,才能使 Babel 可以正确地解析语法 + + +结合这两者,这就是如何定义一个能够被 Webpack 自动代码分割的异步组件。 +```javascript +const Foo = () => import('./Foo.vue') +``` + +### react中的代码分割 +#### 基于路由的代码拆分 +```javascript +import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; +import React, { Suspense, lazy } from 'react'; +const Home = lazy(() => import('./routes/Home')); +const About = lazy(() => import('./routes/About')); +const App = () => ( + + Loading...}> + + + + + + +); +``` +上面的代码是路由和lazy配合拆分的示例,和vue的方式大同小异 +lazy函数的作用就是允许渲染动态导入为常规组件。 +Suspense组件的作用就是类似于骨架屏或者加载指示符,在等待加载的时候出现。 + +> 注意: +> `React.lazy` 和 Suspense 尚不可用于服务器端渲染。如果要在服务器渲染的应用程序中进行代码拆分,我们建议使用 [Loadable Components](https://github.com/smooth-code/loadable-components) 。它有一个很好的[服务器端渲染打包拆分指南](https://github.com/smooth-code/loadable-components/blob/master/packages/server/README.md)。 +> 并且lazy只支持默认导出,并不支持命名导出,想要命名导出那就as defalut + diff --git a/src/Webpack/webpack.md b/src/Webpack/webpack.md new file mode 100644 index 0000000..b92ef3f --- /dev/null +++ b/src/Webpack/webpack.md @@ -0,0 +1,66 @@ +--- + title: 'webpack' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2022-03-17 + category: '' + tag: 'webpack' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ## 启动 +运行命令后,npm会让命令行工具进入,node_module下的bin目录去找是否存在webpack.sh/webpack.cmd,存在就执行,不存在就抛出错误 + +运行目录:node_modules/webpack/bin/webpack.js + + +## loader +loader是一个导出为函数的javaScrpt模块 + +多个loader串行执行,从右往左 + +```javascript +module: { + rules: [ + { + test: /\.less$/, + use: [ + 'style-loader', + 'css-loader', + 'less-loader' + ] + } + ] +} +``` + +开发调试:loader-runner + + +## plugin +基本结构 +```javascript +class Myplugin(){ + apply(compiler) { ------- 插件需要实现apply方法 + compiler.hooks.done.tap('', ( + stats ------- 插件的hooks实现(compiler的hooks和compileration的hooks) + ) => { + }) + } +} +module.export = Myplugin; +``` + +## loader和plugin的区别 +loader更多是处理各种静态资源, +插件是伴随整个webpack 过程的,loader没法做的,插件都能做。 +插件没有独立的开发环境,loader有loader-runner diff --git a/src/Webpack/webpack5.md b/src/Webpack/webpack5.md new file mode 100644 index 0000000..ebc7406 --- /dev/null +++ b/src/Webpack/webpack5.md @@ -0,0 +1,55 @@ +--- + title: 'webpack5' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2022-03-18 + category: '' + tag: 'webpack' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + + ## node版本 +node版本最低需要`v10.13.0` + +## 功能清除 + +- require.includes语法被废弃 ,这个语法是模块预加载 +- 不再为node加载全部的polyfill,如果某个模块依赖了node.js的核心模块,之前的版本会将node的polyfill整个引入,5则不会 + +## 长期缓存 + 确定的moduleIds/chunkIds和导出名称 +文件没有改变,就不会改变 + +## 持久化缓存 +4里面需要使用cache-loader去做些优化 +5里面默认支持,cache字段控制 + +## Tree-Shaking + +- 嵌套的tree-shaking ,嵌套的引入,会将无用的方法摇掉 +- 内部模块的tree-shaking,分析内部模块的依赖关系,无用的依赖会被摇掉 + +## 代码生成 +支持生成ES6模块代码 +```javascript +output: { + ecmaVersion: 6 +} +``` +## 开创性特性:模块联邦 +概念:一个javascript应用程序可以在运行过程中动态加载另一个应用的代码,并支持共享依赖。不再需要本地加载依赖包 + +- Remote 被依赖方,被host消费的应用 (提供服务) +- host 依赖方, 消费其他remote的应用(消费) + +webpack内部通过` ModuleFederationPlugin`插件实现的将多个模块联合起来 + diff --git "a/src/Webpack/\347\254\224\350\256\260.md" "b/src/Webpack/\347\254\224\350\256\260.md" new file mode 100644 index 0000000..f95c53c --- /dev/null +++ "b/src/Webpack/\347\254\224\350\256\260.md" @@ -0,0 +1,191 @@ +--- + title: 'webpack笔记' + shortTitle: '' + description: '' + icon: '' + author: + name: 'Song' + isOriginal: true + date: 2019-03-22 + category: '' + tag: 'webpack' + sticky: 1 + star: false + article: true + timeline: true + image: '' + editLink: false +--- + +**1、压缩输出** +我们将使用 -p(production) 这个 webpack 编译标记,来启用 uglifyjs 压缩插件。 +从 webpack 4 开始,也可以通过 "mode" 配置选项轻松切换到压缩输出,只需设置为 "production"。 + +**是因为使用了mode为生产模式,webpack会自动应用优化插件** + + +webpack.optimize.UglifyJsPlugin +上面的压缩混淆插件在webpack版本小于3.0时,使用的是v0.4.6版本 +在webpack4中计划使用1.0.0版本,最新的使用按照官网上的步骤来,下载安装uglifyjs-webpack-plugin + + +**2、生产环境的配置** +使用 webpack-merge 可以合并两个配置文件 + + +**3、指定环境** +其实,当使用 process.env.NODE_ENV === 'production' 时,一些 library 可能针对具体用户的环境进行代码优化,从而删除或添加一些重要代码。 +我们可以使用 webpack 内置的 [DefinePlugin](https://www.webpackjs.com/plugins/define-plugin) 为所有的依赖定义这个变量 + + + + + + +概念:webpack是个资源打包器,能处理文件之间的依赖关系,并且生成一个文件,主要有以下四个概念 +1.入口 +2.出口 +3.loader +4.插件 + + +优化第一点: +1。作用域提升,webpack打包的时候会把每一个模块放到一个单独的闭包里面,如果有很多模块,闭包相对应的就多 +所以,使用作用域提升可以减少闭包的数量。所以在webpack3.0中有ModuleConcatenationPlugin这个插件,来提升作用域, +但是需要注意的一点是只对es module语法生效,即import/export default + + +2.CommonsChunkPlugin 优化的思路就是通过将公共模块拆出来,最终合成的文件能在最开始的是加载一次,便于后续访问其余页面,直接使用浏览器缓存中的公共代码,这样无疑体验会更好。  + +``` +new webpack.optimize.CommonsChunkPlugin({ // 这里的意思是将node_module中的模块抽离出来,成为vendor name: 'vendor', minChunks: function (module, count) { // any required modules inside node_modules are extracted to vendor return ( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, '../node_modules') ) === 0 ) }, chunks:['app'] }), // // extract webpack runtime and module manifest to its own file in order to // // prevent vendor hash from being updated whenever app bundle is updated new webpack.optimize.CommonsChunkPlugin({ // 这里是从vendor里面把runtime 这部分代码抽离出来,名字是manifest name: 'manifest', chunks: ['vendor'] // 这个属性的意思是通过 chunk name 去选择 chunks 的来源。chunk 必须是 公共chunk 的子模块,指定source chunk,即指定从哪些chunk当中去找公共模块,省略该选项的时候,默认就是entry chunks // minChunks: Infinity // 这种写法和上面的写法效果一样,会马上生成公共chunk,但里面没有模块 }), + + +``` + +这样改变业务代码,就不用部署vendor代码。但是如果业务代码增加删除依赖还是会导致vendor变化,因为依赖的id变化了。 + + + +先来说一下各种教程以及文档中CommonsChunkPlugin提及到chunk有哪几种,主要有以下三种: + +1. webpack当中配置的入口文件(entry)是chunk,可以理解为entry chunk +2. 入口文件以及它的依赖文件通过code split(代码分割)出来的也是chunk,可以理解为children chunk +3. 通过CommonsChunkPlugin创建出来的文件也是chunk,可以理解为commons chunk + + +在webpack4 legato 中已经被移除,新的分包工具使用 [SplitChunksPlugin](https://segmentfault.com/a/1190000015938570) + + 从4.0版本开始CommonsChunkPlugin被移除且被optimization.splitChunks和optimization.runtimeChunk配置项代替.下面展示它们将如何工作. + +webpack4以上无法使用  extract-text-webpack-plugin,用 mini-css-extract-plugin 代替 + +3。对于前两种方式 ,第三种是使用DllPlugin 和 DllReferencePlugin ,配合pwa, +dll插件则是预先打好一些三方库, 生成的dll文件和json文件,然后在build构建的时候根据json的映射去dll文件里面找到依赖的库, +也减小体积 + + + + +总结下现在公司PC端使用的打包优化策略 + +1.预渲染,[prerender-spa-plugin](https://github.com/chrisvfritz/prerender-spa-plugin)插件 +2.公共部分打包 CommonsChunkPlugin + + +2019/02/26 日总结回顾 +webpack低于4.0版本 +使用commonsChunkPlugin来进行分包,将node_module中的三方库打包在一起,然后再将运行文件抽离,配合htmlwebpackplugin +将分好的包引入index.html +减少了app.js体积 + + + + +dll和commons两个插件一起使用可能会造成重复打三方库的包 + +webpack插件原理: +在webpack构建的生命周期中,初始化时会遍历plugin选项中的插件,通过注册事件来监听,然后在生命周期中需要的时间点触发插件功能。 + +插件功能的实现,是通过操作 Webpack 对外暴露的事件钩子 + +总结流程就是: + +- Webpack 的配置文件中所有依赖的插件通过 new XXXPlugin() 的方式填写在 plugin 配置项下,这些 Plugin 中注册了特定事件并提供了回调。 +- 在 Webpack 初始化配置阶段将遍历 plugin 配置项并将每个 Plugin 都注册 +- 接下来在Webpack 主流程运行时,每个关键生命周期点通过 call 方式触发特定事件,注册了特定事件的 Plugin 回调被调用,回调方法中被注入编译对象,可以获取到特定事件触发时编译对象的状态(即当前编译信息)并完成一些操作达到扩展目的。 + +实际上不仅仅是用户配置的 Plugin,在 Webpack 源码中很多的流程操作也是基于 Plugin 的方式实现的,所以可以说 Webpack 就是一个插件合集。 + + +Webpack 的运行过程可以简单概括为以下几个步骤: + +配置解析 -> 内置插件&配置插件注册 -> 确认入口获取依赖资源 -> 使用Loader翻译资源 -> 识别资源加载语句并递归的遍历所有资源 -> 封装依赖资源输出结果 + + + + + + + + + + + + + + +接下来: +1. +vue-cli3有了基本的配置,只需要补充下一些优化操作 + +当开启extract +为true时,是应用了css提取插件 + +不应用时,css会内嵌到js代码中 + +webpack4和3的插件,也就是 +mini-css-extract-plugin +和 +extract-text-webpack-plugin +两者的对比差异如图 + +主要是第一点和第二点:支持按需加载和没有重复的编译 +后者要配合OptimizeCSSPlugin 插件实现去除重复编译 + + + + +摇树(tree-shaking)理解 +1.何为摇树 +2.如何使用 + +摇树是指把项目工程看成是一颗树,其中没有用到的函数或者模块啥的,是这个工程中不需要打包的东西,这时候 +就可以通过摇树这个动作将不必要的东西舍弃 + +webpack4新增了一个 sideEffects 属性,,通过给 package.json 加入 sideEffects声明该 包/模块 是否包含 sideEffects(副作用),从而可以为 tree-shaking 提供更大的优化空间。 +如果你的代码确实有一些副作用,可以改为提供一个数组。 + +**注意**: + + + +1.有没有副作用是相对于本身的包或模块 有没有改变外部的值,可以理解为是否是个纯函数。 +这也就是因为babel转换的原因,导致代码肯定会有副作用,比如babel转换为了更符合ES6语义,转化个iife函数来封装类 + +以上可以知道,第二点的条件在babel之后肯定不能满足,有一个解决方案就是先摇树,然后再babel编译 +这对我们自己的项目可以这个做,但是处理第三方库时,别人肯定是打好包的编译的 +所以一些三方库也有自己的按需导入的处理 + +所以为什么打包库还是rollup好用 +1.它支持程序流分析,在解析的时候就确定了哪些有没有副作用 +2.它支持导出es模块的包 + + + + + + + + + diff --git "a/src/\345\276\256\345\211\215\347\253\257/image.png" "b/src/\345\276\256\345\211\215\347\253\257/image.png" new file mode 100644 index 0000000..9eed1a5 Binary files /dev/null and "b/src/\345\276\256\345\211\215\347\253\257/image.png" differ diff --git "a/src/\345\276\256\345\211\215\347\253\257/\344\271\276\345\235\244\344\270\216\346\227\240\347\225\214.md" "b/src/\345\276\256\345\211\215\347\253\257/\344\271\276\345\235\244\344\270\216\346\227\240\347\225\214.md" new file mode 100644 index 0000000..a331d29 --- /dev/null +++ "b/src/\345\276\256\345\211\215\347\253\257/\344\271\276\345\235\244\344\270\216\346\227\240\347\225\214.md" @@ -0,0 +1,95 @@ +--- +title: '乾坤与无界' # 当前页面内容标题,默认为 Markdown 文件中的第一个 h1 标签内容 +shortTitle: '微前端框架' # 当前页面的短标题 +description: '' # 当前页面内容描述 +icon: '' # 当前页面图标的 FontClass 或文件路径 (建议填写)。 +author: { + name: 'Song' +} +isOriginal: true # 当前文章是否为原创。 +date: 2023-12-29 # 写作时间。 +category: '' # 分类 +tag: '' # 标签 +sticky: 1 # 是否在列表中置顶。当填入数字时,数字越大,排名越靠前 +star: false # 是否收藏在博客主题的文章列表中。当填入数字时,数字越大,排名越靠前。 +article: true # 是否将该文章添加至文章列表中 +timeline: true # 是否将该文章添加至时间线中 +image: '' # 设置预览图 (分享图),请填入绝对路径 +editLink: false # 设置横幅图片 (宽屏分享图),请填入绝对路径。 +--- + +## 什么是微前端 + +> 微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。 + +## 面临的主要问题 + +1. 如何解决css污染 +2. 如何解决js污染 +3. 如何解决应用间通信 +4. 如何保持子应用的路由状态 + +## iframe方案 + +主要是利用浏览器原生标签`